X.590 sha1-DSA認證加密在Java平台與.net平台間的互連

來源:互聯網
上載者:User

X.509認證的讀取在.net中對應的是System.Security.Cryptography.X509Certificates.X509Certificate2類。X509Certificate2支援RSA和DSA加密。對於RSA加密方式在MSDN上已經說的很清楚,但對於DSA加密方式的加密應用在MSDN上基本上沒有提到。

本文闡述兩平台間DSA加密的差異

一、認證與密鑰的產生

在.net FrameWork下可以用內建的Makecert.exe工具產生密鑰與認證。Makecert.exe只能產生RSA加密方式的認證,用Makecert.exe產生認證.pvk和.crt認證後,再用Cert2spc.exe工具產生.spc格式的檔案,最後用pvk2pfx.ext工具產生我們想要的.pfx格式的檔案。關於Makecert.exe、Cert2spc.exe、pvk2pfx.ext三個工具的使用可以到MSDN上查到。

.net中要產生DSA格式的密鑰和公開金鑰需要用到OpenSSL,找到OpenSSL的Win32版本後按以下步驟操作

① 產生DSA參數檔案openssl dsaparam -rand -genkey -out ca.pem 1024② 產生DSA私密金鑰openssl gendsa -des3 -out ca.key ca.pem③ 產生CA的自我簽署憑證openssl req -new -x509 -key ca.key -out ca.crt -config openssl.cnf④ 產生用戶端CSRopenssl req -new -key ca.key -out jfsc_Public.csr -days 1460 -config openssl.cnf⑤ 用CA私密金鑰進行簽名,也可以到權威機構申請CA簽名openssl ca -in jfsc_Public.csr -out jfsc_Public.crt -cert ca.crt -days 1460 -keyfile ca.key -notext -config openssl.cnf⑥ 產生PKCS12格式認證openssl pkcs12 -export -inkey ca.key -in jfsc_Public.crt -out  jfsc_Private.p12⑦ 將PKCS12格式認證匯入本機憑證存放區單元,最後匯出jfsc_Public.cerC=CH,O=iflytek,OU=jfsc,L=bj,S=bj,CN=uam,E=xuf22@hotmail.com
產生私密金鑰時會提示輸入密碼,這裡用的是12345678,jfsc_Private.p12是私密金鑰,jfsc_Public.cer是公開金鑰

在JDK平台中用KeyTool.exe產生DSA加密的私密金鑰keystore和公開金鑰.crt檔案。

二、.net中如何用JDK的公開金鑰.crt檔案驗證簽名

JAVA的私密金鑰keyStore檔案在.net中是無法讀取的,而公開金鑰.crt檔案是可以載入到X509Certificate2類中的。Java平台中用私密金鑰簽名得到16進位的字串如下:

30 2C
       02 14
          1C FA 3A BB 5C 0F 37 FB D7 74 CB 51 E5 64 B5 76 B9 26 4F 7A
       02 14
          62 7E FF 2D 11 4B 2D D3 27 F3 8B 30 D1 6D B4 D2 78 5A 72 5B

可以發現Java的簽名是43位元組的,而.net的簽名只能是40位。 必須對java的簽名做轉換。

/* DER編碼規則DSA簽名產生得到的是r和s兩個整數。對於DSAWithSHA1來說,這兩個整數都不會超過20位元組。由於DSA簽名需要使用一個隨機數,所以每次簽名的結果是不同的。JAVA產生的DSAWithSHA1的簽名結果是個下面ASN.1結構的DER編碼:Dss-Sig-Value  ::=  SEQUENCE  {r       INTEGER,s       INTEGER  }.NET要求的簽名結果40位元組,前面20位元組是r,後面20位元組是s,採用大端位元組序。DER編碼採用TLV的形式,每種類型有個Tag的編碼,是為T部分的編碼,接著是長度的編碼,是為L部分的編碼,最後是內容的編碼,是為V部分的編碼。對於SEQUENCE來說,Tag的編碼為0x30,其內容編碼就是其容納的ASN.1對象的逐個TLV編碼拼起來。對於INTEGER來說,Tag的編碼為0x02,其內容編碼就是使用最小的位元組進行的補碼形式。前9位不能全為1或者全為0。最高位為0表示正數,最高位為1表示負數。由於這裡都是正數,所以如果最高位為1的話,前面要補0,也就是說最長的整數內容部分的編碼長度為20+1=21位元組。長度的編碼對於不超過127的來說,只佔有一個位元組,編碼就是值本身。這裡就是這種情況。例子說明:Java的簽名30 2C02 141C FA 3A BB 5C 0F 37 FB D7 74 CB 51 E5 64 B5 76 B9 26 4F 7A02 1462 7E FF 2D 11 4B 2D D3 27 F3 8B 30 D1 6D B4 D2 78 5A 72 5B30表示是SEQUENCE2C表示後面編碼有0x2c位元組02表示是INTEGER14表示後面編碼有0x14位元組1C FA 3A BB 5C 0F 37 FB D7 74 CB 51 E5 64 B5 76 B9 26 4F 7A這部分就是r內容的編碼這裡r內容的編碼剛好是20自己,直接拷貝即可。如果小於20位元組,則前面補0到20位元組。如果是21位元組,則去掉最前面的1個位元組,這個位元組應該是0,表示r是正數;如果這個位元組不是0則簽名是錯的。02 表示是INTEGER14表示後面編碼有0x14位元組62 7E FF 2D 11 4B 2D D3 27 F3 8B 30 D1 6D B4 D2 78 5A 72 5B這部分是s內容的編碼。處理方式和r內容的編碼一樣。最後轉換得到.NET的簽名為1C FA 3A BB 5C 0F 37 FB D7 74 CB 51 E5 64 B5 76 B9 26 4F 7A 62 7E FF 2D 11 4B 2D D3 27 F3 8B 30 D1 6D B4 D2 78 5A 72 5B* */
代碼:
public byte[] fromHex(byte[] sc){byte[] res = new byte[sc.Length / 2];for (int i = 0; i < sc.Length; i++){byte c1 = (byte)(sc[i] - 48 < 17 ? sc[i] - 48 : sc[i] - 55);i++;byte c2 = (byte)(sc[i] - 48 < 17 ? sc[i] - 48 : sc[i] - 55);res[i / 2] = (byte)(c1 * 16 + c2);}return res;}public byte[] fromHexString(String hex){return fromHex(System.Text.Encoding.Default.GetBytes(hex));}private bool VerifySignature(string originalString){try{byte[] hashByteToVerify;byte[] signByte;string signatureConvert = DERDecode(signature);signByte = fromHexString(signatureConvert);X509Certificate2 x509_Cer2 = new X509Certificate2("path/jfsc_Public.cer");dsa = (DSACryptoServiceProvider)x509_Cer2.PublicKey.Key;hashByteToVerify = System.Text.Encoding.Default.GetBytes(originalString);bool b =  dsa.VerifyData(hashByteToVerify, signByte);return b;}}public string DERDecode(string DERStr){string Result = "";string R = "";string S = "";byte[] RByte = new byte[20];byte[] SByte = new byte[20];byte[] Content = fromHexString(DERStr);string IntRStr = Content[3].ToString();int IntRLength = int.Parse(IntRStr);int j = 0;if (IntRLength == 20){for (int i = 0; i < 20; i++){RByte[i] = Content[4 + i];}j = 24;}else if (IntRLength > 20){for (int i = 0; i < 20; i++){RByte[i] = Content[5 + i];}j = 25;}else{int NotEnough = 20 - IntRLength;for (int i = 0; i < NotEnough; i++){byte[] tmp = new byte[] { 00 };RByte[i] = tmp[0];}for (int i = 0; i < NotEnough; i++){RByte[NotEnough + i] = Content[4 + i];}j = 24 - IntRLength;}int IntSLength = int.Parse(Content[j + 1].ToString());if (IntSLength == 20){for (int i = 0; i < 20; i++){SByte[i] = Content[j + 2 + i];}}else if (IntSLength > 20){for (int i = 0; i < 20; i++){SByte[i] = Content[j + 3 + i];}}else{int NotEnough = 20 - IntSLength;for (int i = 0; i < NotEnough; i++){byte[] tmp = new byte[] { 00 };SByte[i] = tmp[0];}for (int i = 0; i < IntSLength; i++){SByte[NotEnough + i] = Content[j + 2 + i];}}R = toHexString(RByte);S = toHexString(SByte);Result = R + S;return Result;}
三、將.net的簽名轉後java平台才可能對簽名驗證
    public string DEREncode(byte[] b){string str = toHexString(b);string newstr1 = str.Substring(0, 40);string newstr2 = str.Substring(40, 40);string newstr = "302C0214" + newstr1 + "0214" + newstr2;return newstr;}private string SigeXml(string originalString){X509Certificate2 x509_Cer2 = new X509Certificate2("path/jfsc_Private.p12");dsa = (DSACryptoServiceProvider)x509_Cer2.PublicKey.Key;byte[] xmlByte = Encoding.Default.GetBytes(originalString);byte[] hashByte = rsa.SignData(xmlByte);return DEREncode(hashByte);}
 
 
感謝素未謀面的仝工給予的協助

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.