2013-10-08 54 views
5

自從Android v4.2.2以來,Google通過ADB強制執行RSA認證。我有一個帶USB OTG的Cortex-M0板,它通過ADB與Android進行通信。現在我必須在固件中部署RSA身份驗證。如何使用Python加載adbkey.pub 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) 

Android的RSA密鑰

Android的密鑰對使用不同fortmat。 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= [email protected]

此外,我發現在Python RSA和ADB 2048位RSA密鑰publice具有差的長度。我手動重新格式化PEM格式的adbkey.public像以下:密鑰生成期間

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密鑰對可以使用不同的參數(如填充):

----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----- 

然後,它會引發錯誤。但是我不知道它是什麼以及如何檢查它。

任何想法可以幫助我嗎?


UPDATE

現在我找到了一種方法來加載和解碼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(" [email protected]","") # 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 

UPDATE 02

通過Android系統平臺的源代碼介紹後,我發現了一個註釋,與OpenSSL的RSA公鑰衝突,但在microcrypt解決方案。

但是我仍然在使用C/C++實現之前在Python中加載它。

回答

3

您已經擁有所有已解碼的公鑰;你只需要提取正確的作品。

在AOSP項目中,ADB公鑰的相關頭文件是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編碼,然後接着與您的用戶和主機名([email protected]格式),以產生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) 
+0

我可以在這裏補充一個標準的RSA密鑰對Android的RSA密鑰之間的轉換在功能'實現在HTTPS RSA_to_RSAPublicKey'發現://github.com/android/platform_system_core/blob/master/adb/adb_auth_host.cpp –

0

下面是一個完整測試了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; 
}