2017-01-23 69 views
3

我目前正試圖創建一個RSACryptoServiceProvider對象完全從一個解碼的PEM文件。經過幾天的搜索,我確實設法解決了一個可行的解決方案,但它不是一個可以生產的解決方案。正確創建RSACryptoServiceProvider從公鑰

概括地說,以創建從構成在PEM文件中的公共密鑰的字節的RSACryptoServiceProvider對象,我必須(目前2048使用SHA256,具體地),然後導入RSAParameters創建指定密鑰大小的對象對象與ExponentModulus集。我這樣做;

byte[] publicKeyBytes = Convert.FromBase64String(deserializedPublicKey.Replace("-----BEGIN PUBLIC KEY-----", "") 
                     .Replace("-----END PUBLIC KEY-----", "")); 

// extract the modulus and exponent based on the key data 
byte[] exponentData = new byte[3]; 
byte[] modulusData = new byte[256]; 
Array.Copy(publicKeyBytes, publicKeyBytes.Length - exponentData.Length, exponentData, 0, exponentData.Length); 
Array.Copy(publicKeyBytes, 9, modulusData, 0, modulusData.Length); 


// import the public key data (base RSA - works) 
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(dwKeySize: 2048); 
RSAParameters rsaParam = rsa.ExportParameters(false); 
rsaParam.Modulus = modulusData; 
rsaParam.Exponent = exponentData; 
rsa.ImportParameters(rsaParam); 

雖然這個作品,它不是可行的假設deserializedPublicKey將完全270字節,我需要的模量在9位發現總是在長度爲256個字節。

我該如何改變這個來正確地挑出一組公鑰字節的模數?我試圖理解ANS標準,但運氣很好,找到了我需要的東西。我不需要(或者有時間)解碼整個公鑰結構,只需一直得到Modulus字節。指數比較容易,因爲它總是最後幾個字節,儘管其長度也可能不同。

任何幫助表示讚賞。

回答

8

您不需要導出現有參數,然後重新導入它們之上。這會迫使你的機器生成一個RSA密鑰,然後將其丟棄。因此,爲構造函數指定一個密鑰大小並不重要(如果您不使用密鑰,它通常不會生成一個密鑰大小)。

公鑰文件是DER編碼的blob。

-----BEGIN PUBLIC KEY----- 
MIGgMA0GCSqGSIb3DQEBAQUAA4GOADCBigKBggC8rLGlNJ17NaWArDs5mOsV6/kA 
7LMpvx91cXoAshmcihjXkbWSt+xSvVry2w07Y18FlXU9/3unyYctv34yJt70SgfK 
Vo0QF5ksK0G/5ew1cIJM8fSxWRn+1RP9pWIEryA0otCP8EwsyknRaPoD+i+jL8zT 
SEwV8KLlRnx2/HYLVQkCAwEAAQ== 
-----END PUBLIC KEY----- 

如果你拿PEM護甲內的內容,它是一個Base64編碼的字節數組。

30 81 A0 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 
05 00 03 81 8E 00 30 81 8A 02 81 82 00 BC AC B1 
A5 34 9D 7B 35 A5 80 AC 3B 39 98 EB 15 EB F9 00 
EC B3 29 BF 1F 75 71 7A 00 B2 19 9C 8A 18 D7 91 
B5 92 B7 EC 52 BD 5A F2 DB 0D 3B 63 5F 05 95 75 
3D FF 7B A7 C9 87 2D BF 7E 32 26 DE F4 4A 07 CA 
56 8D 10 17 99 2C 2B 41 BF E5 EC 35 70 82 4C F1 
F4 B1 59 19 FE D5 13 FD A5 62 04 AF 20 34 A2 D0 
8F F0 4C 2C CA 49 D1 68 FA 03 FA 2F A3 2F CC D3 
48 4C 15 F0 A2 E5 46 7C 76 FC 76 0B 55 09 02 03 
01 00 01 

ITU-T X.690定義如何讀的東西在基本編碼規則(BER),典型編碼規則(CER,這是我從來沒有見過明確使用)和區分編碼規則(DER)編碼。大多數情況下,CER限制BER,DER限制CER,使得DER最容易閱讀。 (ITU-T X.680描述抽象語法標記(ASN.1),這是語法是DER是二進制編碼)

我們可以做一點,現在解析的:

30 

該標識序列( 0x10)與CONSTRUCTED位集(0x20),這意味着它包含其他DER /標記的值。 (序列始終構造在DER中)

81 A0 

接下來的部分是長度。由於它具有高位設置(> 0x7F),所以第一個字節是「長度」值。它表示真實長度在接下來的1個字節(lengthLength & 0x7F)中被編碼。因此這個SEQUENCE的內容總共是160個字節。 (在這種情況下,「其餘數據」,但SEQUENCE可能已包含在其他內容中)。因此,讓我們讀的內容:

30 0D 

我們再次(0x30)看到我們的流構建序列,其中的0x0D的長度值,所以我們有一個13字節的有效載荷。

06 09 2A 86 48 86 F7 0D 01 01 01 05 00 

06是OBJECT IDENTIFIER,具有0x09字節有效載荷。 OID具有稍微不直觀的編碼,但是這個編碼相當於文本表示1.2.840.113549.1.1.1,它是id-rsaEncryptionhttp://www.oid-info.com/get/1.2.840.113549.1.1.1)。

這仍然留給我們兩個字節(05 00),我們看到它是一個NULL(0字節的有效載荷,因爲,它是NULL)。

所以到目前爲止,我們已經

SEQUENCE 
    SEQUENCE 
    OID 1.2.840.113549.1.1.1 
    NULL 
    143 more bytes. 

在繼續:

03 81 8E 00 

03意味着位串。 BIT STRING編碼爲[標籤] [長度] [未使用位數]。未使用的比特基本上總是零。所以這是一個比特序列,長度爲0x8E字節,並且所有這些都被使用。

從技術上講,我們應該在那裏停下來,因爲CONSTRUCTED沒有設置。但是由於我們碰巧知道該結構的格式,我們把值,好像構建的位被設置反正:

30 81 8A 

這裏又是我們的朋友流構建SEQUENCE,0x8A有效載荷字節,這方便對應於「一切的剩下」。

02 81 82 

02標識的整數,並且這其中有0x82有效載荷字節:

00 BC AC B1 A5 34 9D 7B 35 A5 80 AC 3B 39 98 EB 
15 EB F9 00 EC B3 29 BF 1F 75 71 7A 00 B2 19 9C 
8A 18 D7 91 B5 92 B7 EC 52 BD 5A F2 DB 0D 3B 63 
5F 05 95 75 3D FF 7B A7 C9 87 2D BF 7E 32 26 DE 
F4 4A 07 CA 56 8D 10 17 99 2C 2B 41 BF E5 EC 35 
70 82 4C F1 F4 B1 59 19 FE D5 13 FD A5 62 04 AF 
20 34 A2 D0 8F F0 4C 2C CA 49 D1 68 FA 03 FA 2F 
A3 2F CC D3 48 4C 15 F0 A2 E5 46 7C 76 FC 76 0B 
55 09 

前導爲0x00將違反DER,除了下一個字節具有高比特集。這意味着0x00用於保持符號位不被設置,這使得這是一個正數。

02 03 01 00 01 

另一個INTEGER,3個字節,值01 00 01。我們完成了。

SEQUENCE 
    SEQUENCE 
    OID 1.2.840.113549.1.1.1 
    NULL 
    BIT STRING 
    SEQUENCE 
     INTEGER 00 BC AC ... 0B 55 09 
     INTEGER 01 00 01 

收穫https://tools.ietf.org/html/rfc5280我們看到,這看起來很像一個SubjectPublicKeyInfo結構:

SubjectPublicKeyInfo ::= SEQUENCE { 
    algorithm   AlgorithmIdentifier, 
    subjectPublicKey  BIT STRING } 

AlgorithmIdentifier ::= SEQUENCE { 
    algorithm    OBJECT IDENTIFIER, 
    parameters    ANY DEFINED BY algorithm OPTIONAL } 
          -- contains a value of the type 
          -- registered for use with the 
          -- algorithm object identifier value 

當然,它不知道的RSA公鑰格式是什麼。但是,OID-信息的網站叫我們看看RFC 2313,我們看到

An RSA public key shall have ASN.1 type RSAPublicKey: 

RSAPublicKey ::= SEQUENCE { 
    modulus INTEGER, -- n 
    publicExponent INTEGER -- e } 

這樣說,我們讀到的第一個整數爲模值,第二個是(公共)指數。

DER編碼是big-endian,它也是RSAParameters編碼,但對於RSAParameters,您需要刪除Modulus中的前導0x00值。

雖然這並不像給你代碼那樣容易,但在給出這些信息的情況下編寫RSA密鑰的解析器應該相當簡單。我建議你把它寫成internal static RSAParameters ReadRsaPublicKey(...),然後你只需要做

RSAParameters rsaParameters = ReadRsaPublicKey(...); 

using (RSA rsa = RSA.Create()) 
{ 
    rsa.ImportParameters(rsaParameters); 
    // things you want to do with the key go here 
} 
+0

這是一個非常令人印象深刻的迴應!我會盡力根據提供的信息實施,謝謝! – DiskJunky

-1

PEM文件只是一系列base64編碼的DER文件,而.net允許直接導入DER文件,所以你可以這樣做(我假設你只使用公鑰,因爲你聲明你只使用它):

byte[] certBytes = Convert.FromBase64String(deserializedPublicKey 
    .Replace("-----BEGIN PUBLIC KEY-----", "") 
    .Replace("-----END PUBLIC KEY-----", "")); 

X509Certificate2 cert = new X509Certificate2(certBytes); 
RSACryptoServiceProvider publicKeyProvider = 
(RSACryptoServiceProvider)cert.PublicKey.Key; 
+0

,只要它是那麼容易:-)如果我通過公鑰到構造,它導致了'CryptographicException' '找不到請求的對象' – DiskJunky

+0

它應該是這麼簡單,但似乎X509Certificate2要求DER文件包含私鑰... – Gusman

+0

作爲替代方案,使用Bouncy Castle,它具有本地支持導入PEM文件 – Gusman

相關問題