Since Android v4.2.2, Google enforced RSA authentication over ADB. I have a Cortex-M0 board with USB OTG, which communicates with Android over ADB. Now I have to deploy RSA authentication in firmware.
由于Android v4.2.2,谷歌对ADB实施了RSA认证。我有一个带USB OTG的Cortex-M0板,它通过ADB与Android通信。现在我必须在固件中部署RSA认证。
I have collected sources code from Android ADB, AVRCryptolib/ARMCryptolib. Since I am a newbie of RSA authentication, I use Python RSA module as my leanring start.
我从Android ADB, AVRCryptolib/ARMCryptolib中收集了源代码。由于我是RSA认证的新手,所以我开始使用Python RSA模块。
Python RSA
Python RSA
Python RSA can generate 2048bit RSA key pair, and encrypt/decrypt, sign/verify correctly with generated key pair. Here is my source code:
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)
Android RSA Key
Android RSA密钥
The Android key pair uses different fortmat. The private key in Adbkey file uses standard PEM format, while the public key in adbkey.pub uses base64 encoded format without header and footer lines.
Android密钥对使用不同的fortmat。Adbkey文件中的私钥使用标准的PEM格式,而公共密钥则使用Adbkey。pub使用base64编码格式,没有页眉和页脚。
original adbkey.public:
原始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
QAAAAOlEXtSnLMF3igx8NMi7u8 fzlsuw57g9vuqpjdgj7ieg4 + LeD7BoVC + v2bvBJnvVsaJ31QtwEzbicob8mlLxEhbGSKdaoXIwwAsWR + 7 + 5 uys7karhirf3k + hUX6PDIF7gEo / 0 tgglxvnxmtkfv9zzb3vxmgv66z68vebxk46km7mffkihyu7p3jdtdptm2p7ju7xwvfgwh6a0rrrgzuwonefteh6ruhixp1z3cdxyvz0yf9uhwsweqzgf7n2rg0g4cxnjdls0cxqaao7xs16tzacyfn7czqpikcb057oo + jMWvgsh + 8 gy8qgqti5ehbizd7cpp3s / rVbzN8gUCo4aSuIn9TfT6uJ0S3D3TPWbHXs9Y8nskhIOM1Hkgv3CAODfzwH + ZM3nWmFD77FMtiWo / hJrMRcH63yvX5pVPYnqQyHcdembEM1Fbxg qWAVtLxNFJqoFKYXYHl9ktGcM + 3 izwvea5faebmbwiuezkyf6f49y3d异丙醇+ fWxunGkbehih7o0S9RoWIQIYByteF + b / EN2ntSpwfuhD8G9n6Bfaz4mEVTG82Lj8YeK6 + CYyEirSCl4Al7SGsb66E74Fnt + v + NouQFpZxCrrefm7sYug11NHSNiDeYa8cnatQsla + Cfd91GmgKsu + ZDO8uF8UR7 / r3MKEDohfMCrWdIiZ0w6Ad6hata1OgEAAQA = unknown@unknown
Furthermore, I found 2048bit RSA publice key from Python RSA and Adb have difference length. I manually reformat the adbkey.public in PEM format like following:
此外,我还发现了来自Python RSA和Adb的2048位RSA publice密钥有不同的长度。我手动重新格式化adbkey。公众的PEM格式如下:
----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-----
Then it throws an error:
然后它抛出一个错误:
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()
I guess Android RSA key pair may use different parameters (like padding) during key generation. However I have no idea what it is and how to check it.
我想Android RSA密钥对可能会在密钥生成过程中使用不同的参数(比如填充)。但是我不知道它是什么,怎么检查它。
Any ideas can help me out?
有什么想法可以帮我吗?
UPDATE
更新
Now I found a way to load and decode the adbkey.pub. Check the following code
现在我找到了一种方法来加载和解码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
Now I have a hexdecimal presentation of pubkey.
现在我有一个十六进制的pubkey。
40000000e9445ed4a72cc1778a0c7c34c8bbbbcf8b783ec1a150bebf66ef0499ef56c689df542dc0
4cdb89ca1bf2694bc4485b19229d6a85c8c3002c591fbb173952516e7b1bdbeeaa9243189ee2106e
3ee6e62ceca6ab862445dcafa1517e8f0c817b804a3fd13820971bcd5e64e47d5f7365bdd5c66815
ebacfaf157815cae3a90cecc7df2a21f2bbb3f725db5da6d9b6a7b8d4ed7c2f7e0587e9ad2baeb1b
351638d11fb5e87aaee1c8c4fd59dc2771615674605f6e1d6b30790ce07fb376446d20e1cc4d2432
ecd025ea69aa3bc52d7ab7368261f9fb71940f20a09bd39ee8a3e8cc5af82c87ef2063caa042d239
107062cddedc3e9dd2feb55bccdf20502a38692b889fd4df4fab89d12dc3dd33d66c75ecf58f27b2
484838cd47920bf70803837f3c07f993379d69850fbec532d896a3f849acc45c1fadf2bd7e6954f6
27a90c8771d7a66c433515bc60fea58056d2f134526aa052985d81e5f64b4670cfb7233c2f79ae5f
01e6e66d622e7b329817a178f58ddd20f03e7d6c6e9c691b7a18a1ee8d12f51a16210218072b5e17
e6ff10dda7b52a707ee843f06f67e817dacf89845531bcd8b8fc61e2baf82632122ad20a5e0097b4
86b1beba13be059edfaff8da2e405a59c42aeb79f9bbb18ba0d7534748d8837986bc7276ad42c95a
f827ddf751a680ab2ef990cef2e17c511effaf730a103a217cc0ab59d222674c3a01dea16ad6b53a
01000100
UPDATE 02
更新02
After briefing through source code of Android system platform, I found a comment that RSA public key conflicts with OpenSSL, but in microcrypt solution.
在介绍了Android系统平台的源代码之后,我发现了一个关于RSA公钥与OpenSSL冲突的评论,但是在microcrypt解决方案中。
However I still working on to load it in Python before I goes to C/C++ implementation.
然而,在我进入C/ c++实现之前,我还在用Python来加载它。
2 个解决方案
#1
3
You already have all of the public key decoded; you just need to extract the right pieces.
你已经有了所有的公钥解码;你只需要提取正确的部分。
The relevant header file for the ADB public key is mincrypt/rsa.h, in the AOSP project.
ADB公钥的相关头文件是mincrypt/rsa。h,在AOSP项目中。
It defines a structure with five fields, like this:
它定义了一个有五个字段的结构,如下所示:
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)
The entire structure, all 524 bytes, is base64-encoded, and then followed with your user and hostname (user@host
format) to produce the adbkey.pub
file.
整个结构,全部524字节,是base64编码的,然后使用您的用户和主机名(user@host格式)来生成adbkey。酒吧文件。
With your first update, you can read the contents of adbkey.pub
. Now that you have b
, you can extract the two components that you need for the public key, like this:
在您的第一次更新中,您可以阅读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)
Then make a public key from those parts:
然后从这些部分做一个公钥:
pubkey = rsa.PublicKey(n, e)
#2
0
Here is a complete tested code snippet in Java. I know it is not Python, but it might be simple to convert it:
这里是一个完整的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;
}
#1
3
You already have all of the public key decoded; you just need to extract the right pieces.
你已经有了所有的公钥解码;你只需要提取正确的部分。
The relevant header file for the ADB public key is mincrypt/rsa.h, in the AOSP project.
ADB公钥的相关头文件是mincrypt/rsa。h,在AOSP项目中。
It defines a structure with five fields, like this:
它定义了一个有五个字段的结构,如下所示:
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)
The entire structure, all 524 bytes, is base64-encoded, and then followed with your user and hostname (user@host
format) to produce the adbkey.pub
file.
整个结构,全部524字节,是base64编码的,然后使用您的用户和主机名(user@host格式)来生成adbkey。酒吧文件。
With your first update, you can read the contents of adbkey.pub
. Now that you have b
, you can extract the two components that you need for the public key, like this:
在您的第一次更新中,您可以阅读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)
Then make a public key from those parts:
然后从这些部分做一个公钥:
pubkey = rsa.PublicKey(n, e)
#2
0
Here is a complete tested code snippet in Java. I know it is not Python, but it might be simple to convert it:
这里是一个完整的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;
}