5

自 Android v4.2.2 起,Google 对 ADB 强制执行 RSA 身份验证。我有一个带有 USB OTG 的 Cortex-M0 板,它通过 ADB 与 Android 通信。现在我必须在固件中部署 RSA 身份验证。

我从 Android ADB、AVRCryptolib/ARMCryptolib 收集了源代码。由于我是 RSA 身份验证的新手,我使用 Python RSA 模块作为我的学习开始。

Python RSA

Python RSA 可以生成 2048 位 RSA 密钥对,并使用生成的密钥对正确加密/解密、签名/验证。这是我的源代码:

-*- coding: utf-8 -*-
# with_statement is used in Python 2.6+, 
from __future__ import with_statement
import rsa
from datetime import datetime

# load pub/private keys
with open('adbkey.pub.pem') as publickfile:
    p = publickfile.read()
    pubkey = rsa.PublicKey.load_pkcs1(p)

with open('adbkey.pem') as privatefile:
    p = privatefile.read()
    privkey = rsa.PrivateKey.load_pkcs1(p)

message = 'http://ennovation.sinaapp.com/'

begin = datetime.now()
# Encrypt message with pubkey, decrypt it with private key
crypto = rsa.encrypt(message, pubkey)
stop1 = datetime.now()
message = rsa.decrypt(crypto, privkey)
stop2 = datetime.now()
print message
print "EncryptTime = %s, DecryptTime = %s"%(stop1-begin,stop2-stop1)

begin = datetime.now()
# Sign message with private key, verify it with public key
signature = rsa.sign(message, privkey, 'SHA-1')
stop1 = datetime.now()
#result = rsa.verify('hello', signature, pubkey)
result = rsa.verify(message, signature, pubkey)
stop2 = datetime.now()

print result
print "SignTime = %s, VerifyTime = %s"%(stop1-begin,stop2-stop1)

安卓 RSA 密钥

Android 密钥对使用不同的格式。Adbkey 文件中的私钥使用标准 PEM 格式,而 adbkey.pub 中的公钥使用 base64 编码格式,没有页眉和页脚行。

原始 adbkey.public:

QAAAAOlEXtSnLMF3igx8NMi7u8+LeD7BoVC+v2bvBJnvVsaJ31QtwEzbicob8mlLxEhbGSKdaoXIwwAsWR+7FzlSUW57G9vuqpJDGJ7iEG4+5uYs7KarhiRF3K+hUX6PDIF7gEo/0TgglxvNXmTkfV9zZb3VxmgV66z68VeBXK46kM7MffKiHyu7P3Jdtdptm2p7jU7XwvfgWH6a0rrrGzUWONEfteh6ruHIxP1Z3CdxYVZ0YF9uHWsweQzgf7N2RG0g4cxNJDLs0CXqaao7xS16tzaCYfn7cZQPIKCb057oo+jMWvgsh+8gY8qgQtI5EHBizd7cPp3S/rVbzN8gUCo4aSuIn9TfT6uJ0S3D3TPWbHXs9Y8nskhIOM1Hkgv3CAODfzwH+ZM3nWmFD77FMtiWo/hJrMRcH63yvX5pVPYnqQyHcdembEM1Fbxg/qWAVtLxNFJqoFKYXYHl9ktGcM+3Izwvea5fAebmbWIuezKYF6F49Y3dIPA+fWxunGkbehih7o0S9RoWIQIYByteF+b/EN2ntSpwfuhD8G9n6Bfaz4mEVTG82Lj8YeK6+CYyEirSCl4Al7SGsb66E74Fnt+v+NouQFpZxCrrefm7sYug11NHSNiDeYa8cnatQsla+Cfd91GmgKsu+ZDO8uF8UR7/r3MKEDohfMCrWdIiZ0w6Ad6hata1OgEAAQA= unknown@unknown

此外,我发现 Python RSA 和 Adb 的 2048 位 RSA 公钥长度不同。我以 PEM 格式手动重新格式化 adbkey.public,如下所示:

----BEGIN RSA PUBLIC KEY-----
QAAAAOlEXtSnLMF3igx8NMi7u8+LeD7BoVC+v2bvBJnvVsaJ31QtwEzbicob8mlL
xEhbGSKdaoXIwwAsWR+7FzlSUW57G9vuqpJDGJ7iEG4+5uYs7KarhiRF3K+hUX6P
DIF7gEo/0TgglxvNXmTkfV9zZb3VxmgV66z68VeBXK46kM7MffKiHyu7P3Jdtdpt
m2p7jU7XwvfgWH6a0rrrGzUWONEfteh6ruHIxP1Z3CdxYVZ0YF9uHWsweQzgf7N2
RG0g4cxNJDLs0CXqaao7xS16tzaCYfn7cZQPIKCb057oo+jMWvgsh+8gY8qgQtI5
EHBizd7cPp3S/rVbzN8gUCo4aSuIn9TfT6uJ0S3D3TPWbHXs9Y8nskhIOM1Hkgv3
CAODfzwH+ZM3nWmFD77FMtiWo/hJrMRcH63yvX5pVPYnqQyHcdembEM1Fbxg/qWA
VtLxNFJqoFKYXYHl9ktGcM+3Izwvea5fAebmbWIuezKYF6F49Y3dIPA+fWxunGkb
ehih7o0S9RoWIQIYByteF+b/EN2ntSpwfuhD8G9n6Bfaz4mEVTG82Lj8YeK6+CYy
EirSCl4Al7SGsb66E74Fnt+v+NouQFpZxCrrefm7sYug11NHSNiDeYa8cnatQsla
+Cfd91GmgKsu+ZDO8uF8UR7/r3MKEDohfMCrWdIiZ0w6Ad6hata1OgEAAQA=
-----END RSA PUBLIC KEY-----

然后它抛出一个错误:

Traceback (most recent call last):
File "D:\Freescale FRDM KL25Z\RSA\rsa_speed_adb.py", line 10, in <module>
pubkey = rsa.PublicKey.load_pkcs1(p)
File "build\bdist.win32\egg\rsa\key.py", line 65, in load_pkcs1
File "build\bdist.win32\egg\rsa\key.py", line 192, in _load_pkcs1_pem
File "build\bdist.win32\egg\rsa\key.py", line 160, in _load_pkcs1_der
File "C:\Python25\lib\site-packages\pyasn1-0.1.7-py2.5.egg\pyasn1\codec\ber\decoder.py", line 798, in __call__ pyasn1.error.PyAsn1Error: TagSet(Tag(tagClass=64, tagFormat=0, tagId=0)) not in asn1Spec: AsnPubKey()

我猜 Android RSA 密钥对可能在密钥生成期间使用不同的参数(如填充)。但是我不知道它是什么以及如何检查它。

有什么想法可以帮助我吗?


更新

现在我找到了一种加载和解码 adbkey.pub 的方法。检查以下代码

import base64
import binascii

f = open('adbkey.pub','r') # load file from adbkey.pub
line = f.readline() # actually oneline is enough
line = line.replace(" unknown@unknown","") # remove text information
print line
print len(line)
b = base64.b64decode(line) # decode base64 into binary
s = binascii.hexlify(b) # get hexdecimal of the binary
print s

现在我有一个 pubkey 的十六进制表示。

40000000e9445ed4a72cc1778a0c7c34c8bbbbcf8b783ec1a150bebf66ef0499ef56c689df542dc0
4cdb89ca1bf2694bc4485b19229d6a85c8c3002c591fbb173952516e7b1bdbeeaa9243189ee2106e
3ee6e62ceca6ab862445dcafa1517e8f0c817b804a3fd13820971bcd5e64e47d5f7365bdd5c66815
ebacfaf157815cae3a90cecc7df2a21f2bbb3f725db5da6d9b6a7b8d4ed7c2f7e0587e9ad2baeb1b
351638d11fb5e87aaee1c8c4fd59dc2771615674605f6e1d6b30790ce07fb376446d20e1cc4d2432
ecd025ea69aa3bc52d7ab7368261f9fb71940f20a09bd39ee8a3e8cc5af82c87ef2063caa042d239
107062cddedc3e9dd2feb55bccdf20502a38692b889fd4df4fab89d12dc3dd33d66c75ecf58f27b2
484838cd47920bf70803837f3c07f993379d69850fbec532d896a3f849acc45c1fadf2bd7e6954f6
27a90c8771d7a66c433515bc60fea58056d2f134526aa052985d81e5f64b4670cfb7233c2f79ae5f
01e6e66d622e7b329817a178f58ddd20f03e7d6c6e9c691b7a18a1ee8d12f51a16210218072b5e17
e6ff10dda7b52a707ee843f06f67e817dacf89845531bcd8b8fc61e2baf82632122ad20a5e0097b4
86b1beba13be059edfaff8da2e405a59c42aeb79f9bbb18ba0d7534748d8837986bc7276ad42c95a
f827ddf751a680ab2ef990cef2e17c511effaf730a103a217cc0ab59d222674c3a01dea16ad6b53a
01000100

更新 02

通过Android系统平台的源码介绍后,发现有一条评论说RSA公钥和OpenSSL冲突,但是在microcrypt解决方案中。

但是,在进行 C/C++ 实现之前,我仍在努力将其加载到 Python 中。

4

2 回答 2

3

你已经解码了所有的公钥;你只需要提取正确的部分。

ADB 公钥的相关头文件是AOSP 项目中的mincrypt/rsa.h

它定义了一个包含五个字段的结构,如下所示:

Field      Size
================================
len        4 bytes (1 word)
n0inv      4 btyes (1 word)
n        256 bytes (64 words)
rr       256 bytes (64 words)
exponent   4 bytes (1 word)

整个结构,全部 524 字节,是 base64 编码的,然后是你的用户和主机名(user@host格式)来生成adbkey.pub文件。

首次更新时,您可以阅读adbkey.pub. 现在您有了b,您可以提取公钥所需的两个组件,如下所示:

n_bytes = bytearray(reversed(b[8:256+8])) # reversed because LSB is first
n_str = binascii.hexlify(n_bytes)         # convert to hex string
n = int(n_str, 16)                        # make an integer

e_bytes = bytearray(reversed(b[-4:]))     # last four bytes are the exponent
e_str = binascii.hexlify(n_bytes)
e = int(e_str, 16)

然后从这些部分制作一个公钥:

pubkey = rsa.PublicKey(n, e)
于 2014-02-27T02:48:08.590 回答
-1

这是一个完整的 Java 测试代码片段。我知道它不是 Python,但转换它可能很简单:

/**
 * Parses an Android public RSA key like stored under .android/adbkey.pub and returns a Java public RSA key.
 * @param inputKey The Android public key.
 * @return the public RSA key.
 * @throws Exception
 */
public static PublicKey parseAndroidPubKey(String inputKey) {
    BufferedReader bufferedReader = new BufferedReader(new StringReader(inputKey));
    String line = null;
    try {
        line = bufferedReader.readLine();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    line = line.replaceAll(" .*@.*", "");
    byte[] raw = Base64.getDecoder().decode(line);
    ByteBuffer bb = ByteBuffer.wrap(raw);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    IntBuffer intBuffer = bb.asIntBuffer();
    int len = intBuffer.get();
    BigInteger n0Inv = BigInteger.valueOf(intBuffer.get());
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(len*4);
    int[] dst = new int[len];
    intBuffer.get(dst);
    ArrayUtils.reverse(dst);
    for (int i = 0; i < len; i++) {
        int value = dst[i];
        byte[] convertedBytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(value).array();
        byteArrayOutputStream.write(convertedBytes, 0, convertedBytes.length);
    }
    byte[] n = byteArrayOutputStream.toByteArray();
    byteArrayOutputStream.reset();
    dst = new int[len];
    intBuffer.get(dst);
    ArrayUtils.reverse(dst);
    for (int i = 0; i < len; i++) {
        int value = dst[i];
        byte[] convertedBytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(value).array();
        byteArrayOutputStream.write(convertedBytes, 0, convertedBytes.length);
    }
    int e = intBuffer.get();

    RSAPublicKey publicKey;
    try {
        publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(new BigInteger(1, n), BigInteger.valueOf(e)));
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
    return publicKey;
}
于 2016-02-17T00:22:01.623 回答