2012-02-14 318 views
16

概括地說,這是我的問題:使用Base64編碼的公鑰來驗證RSA簽名

private string publicKeyString = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVGUzbydMZS+fnkGTsUkDKEyFOGwghR234d5GjPnMIC0RFtXtw2tdcNM8I9Qk+h6fnPHiA7r27iHBfdxTP3oegQJWpbY2RMwSmOs02eQqpKx4QtIjWqkKk2Gmck5cll9GCoI8AUAA5e0D02T0ZgINDmo5yGPhGAAmqYrm8YiupwQIDAQAB"; 

/* Some transformation required, using publicKeyString to initiate a new RSACryptoServiceProvider object 
*/ 

//for now: 
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 

byte[] selfComputedHash = new byte[]; //left out of the example 
byte[] signature = new byte[]; 

bool result = rsa.VerifyHash(selfComputedHash, CryptoConfig.MapNameToOID("SHA1"), signature); 

正如你所看到的,問題是啓動與給定的Base64編碼的公共密鑰字符串的新的RSACryptoServiceProvider。我已經能夠使用對象RSAParameters進行實例化,該對象使用OpenSSL shell命令從此公鑰字符串派生出來的Modulus和Exponent加載到byte []中。但是由於這個公鑰可能會在將來發生變化,我希望能夠將它以原始形式存儲在數據庫中。必須有一個更直接的方式來處理這個問題。

到目前爲止,我讀過的很多示例通過將生成的私鑰和公鑰導出並導入到密鑰容器對象並在同一段代碼中使用它,從而避免了「傳輸'一些字符串中的關鍵字形式不足。有些人在StackOverflow和其他網站上都表達了同樣的問題,但我還沒有找到令人滿意的答案。

任何想法都是值得歡迎的。

背景信息: 我的通信夥伴從一個可變長度的輸入字符串計算一個20字節的SHA1哈希,它由ASCII編碼消息的幾個字段中包含的信息組成。然後這個哈希值與我的合作伙伴的私鑰進行RSA簽名,並與ASCII消息一起發送給我。抵達後,我自己計算SHA1哈希,使用ASCII消息中的相同字段,然後嘗試驗證這些字段是否未通過調用VerifyHash進行更改。

關鍵是以兩種形式提供:普通和'noNL'。所述NONL版本包含在上述代碼中,普通版本是這樣的:

-----BEGIN PUBLIC KEY----- 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVGUzbydMZS+fnkGTsUkDKEyFO 
GwghR234d5GjPnMIC0RFtXtw2tdcNM8I9Qk+h6fnPHiA7r27iHBfdxTP3oegQJWp 
bY2RMwSmOs02eQqpKx4QtIjWqkKk2Gmck5cll9GCoI8AUAA5e0D02T0ZgINDmo5y 
GPhGAAmqYrm8YiupwQIDAQAB 
-----END PUBLIC KEY----- 

回答

13

你的字符串是SubjectPublicKeyInfo的base64編碼。您可以使用Bouncycastle.net給它這樣的解碼:

byte[] publicKeyBytes = Convert.FromBase64String(publicKeyString); 
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBytes); 
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters) asymmetricKeyParameter; 
RSAParameters rsaParameters = new RSAParameters(); 
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(); 
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned(); 
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
rsa.ImportParameters(rsaParameters); 
+0

謝謝,這很容易。我將研究BouncyCastle庫,但是您確定無法僅使用.NET本身的加密庫來完成此操作嗎? – jscheppers 2012-02-15 08:38:01

+0

@jscheppers:你可以像poupou建議的那樣手動完成它,但是沒有直接支持.NET中的格式。快速搜索可以揭示這個手動解碼的例子:http://www.jensign.com/JavaScience/dotnet/pempublic/pempublic.cs,但看起來有點麻煩。 – 2012-02-15 09:00:26

+0

我認爲我更喜歡你的BouncyCastle解決方案,然後;)我已經在我目前的實施中嘗試過它,它的工作原理。我會將你的帖子標記爲答案! – jscheppers 2012-02-15 09:22:41

4

首先BASE64只有一些二進制數據的編碼。用二進制編碼RSA公鑰有多種方法。但是,如果你從OpenSSL得到這個結果,它可能是一個DER編碼的RSAPublicKey結構。

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

一般來說,你需要一個ASN.1解碼器,Mono.Security.dll提供one,但對於這樣一個簡單的結構,你可能想通過手工做因爲ASN.1基本上是一個標籤長度

+0

提供公鑰沒有提及關鍵的格式,但因爲我能夠與OpenSSL和由於鍵包含「閱讀」他們的組織這兩個----- BEGIN PUBLIC KEY -----和----- END PUBLIC KEY -----標籤(我會將其添加到我原來的帖子中)我相當肯定它是一個DER編碼鍵。 你會如何將這個字符串轉換爲ASN.1結構?當字節發揮作用時,我並不確定我自己:) – jscheppers 2012-02-15 08:29:32

+1

請注意,它實際上是一個包含* RSAPublicKey'的SubjectPublicKeyInfo *。 – 2012-02-15 09:01:52

0

如果您的合作伙伴也使用.NET,他/她可以從Mono的makecert https://github.com/mono/mono/blob/bf55818da11240bd108dc51b374fae3d3b5482ce/mcs/tools/security/makecert.cs派生生成證書文件並直接發送給您。

在這種情況下,您可以輕鬆地將證書,而不是原始字節,

http://www.lextm.com/2012/02/simple-publicprivate-key-signing-sample-code/

+0

這將是一個很好的可能性,但我無法控制生成密鑰的方式。密鑰是按原樣提供的,在此密鑰出現的地方沒有證書可用。 – jscheppers 2012-02-15 08:26:18