PHP,C# 和JAVARSA簽名及驗簽
這個功能網上搜了好多資料。貢獻一下,轉載須註明並對卓二妹的無私奉獻表示感謝。
1)簽名演算法使用SHA1withRSA。
2)簽名後的資料位元base64編碼的密文字串。
3)三個環境進行簽名的私密金鑰的格式不同,需要openssl工具進行轉換。
——————————————————————————————————————————
JAVA簽名:
1)從包含公私密金鑰的pfx認證中取得.key私密金鑰:
F:\openssl-0.9.8k_WIN32\bin>openssl pkcs12 -in f:\certs\zhuo.pfx -out f:\certs\zhuo.pemEnter Import Password:(輸入匯出時的密碼)MAC verified OKEnter PEM pass phrase:(長度至少為4位的pem認證密碼)Verifying - Enter PEM pass phrase:(確認一次pem認證密碼) F:\openssl-0.9.8k_WIN32\bin>openssl pkcs8 -topk8 -inform PEM -outform DER -in f:\certs\zhuo.pem -out f:\certs\zhuo_der.key -nocryptEnter pass phrase for f:\certs\zhuo.pem:(輸入pem認證密碼)
??
該步驟產生的.key檔案即為JAVA簽名所需私密金鑰檔案。
2)產生公開金鑰:直接從IE中匯出X.509格式二進位編碼的cer為尾碼的密鑰憑證即可。
?
?
3)簽名驗簽:
?
//簽名:/** * * 函數功能說明: 簽名資料 * created by zhuoyueping 2013-8-17 * modified by zhuoyueping 2013-8-17 * 修改內容說明: * @param @param content:簽名原文 * @param @param keyfile:私密金鑰檔案.key路徑 * @param @return * @param @throws Exception * @return String :base64簽名 * @throws */public String sign(String content, String keyfile) throws Exception {File file = new File(keyfile); //keyfile key檔案的地址FileInputStream in;in = new FileInputStream(file);ByteArrayOutputStream bout = new ByteArrayOutputStream();byte[] tmpbuf = new byte[1024];int count = 0;while ((count = in.read(tmpbuf)) != -1) {bout.write(tmpbuf, 0, count);tmpbuf = new byte[1024];}in.close();KeyFactory keyFactory = KeyFactory.getInstance("RSA");EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(bout.toByteArray());RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);Signature dsa = Signature.getInstance("SHA1withRSA"); //採用SHA1withRSA加密dsa.initSign(privateKey);dsa.update(content.getBytes("UTF-8")); //voucher需要加密的String必須變成byte類型的byte[] sig = dsa.sign();String rtnValue = new String(Base64.encode(sig));return rtnValue;}/** * * 驗證簽名 *
* * @param data 原文位元組 * @param sign 資料簽名[BASE64] * @param certificatePath 憑證存放區路徑 * @return * @throws Exception */public static boolean verifySign(byte[] data, String sign,String certificatePath) throws Exception {// 獲得認證X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);return verifySign(data,sign,x509Certificate);}private static boolean verifySign(byte[] data, String sign, X509Certificate x509Certificate)throws Exception {PublicKey publicKey = x509Certificate.getPublicKey();Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());signature.initVerify(publicKey);signature.update(data);return signature.verify(Base64.decode(sign.getBytes()));}
?
C#簽名:
1)從包含公私密金鑰的pfx認證中取得.key私密金鑰:
F:\openssl-0.9.8k_WIN32\bin> openssl rsa -in d:\\certs\\zhuo.pfx -nocerts -nodes -out d:\\certs\\zhuo.key該步驟產生的.key檔案即為C#簽名所需私密金鑰檔案。
?
2)公開金鑰產生:於java方式相同,都是二進位格式的x509認證3)簽名及驗簽:
using System;using System.Text;using System.Security.Cryptography;using System.Web;using System.IO;using System.Security.Cryptography.X509Certificates; namespace Safe{ public class SafeUtil { /// /// 驗證簽名 /// /// 原文:UTF8編碼 /// 簽名:base64編碼的位元組 /// 公開金鑰路徑 /// 驗簽結果 public bool Verify(String OriginalString, String SignatureString,String publicKeyPath) { //將base64簽名資料轉碼為位元組 byte[] signedBase64 = Convert.FromBase64String(SignatureString); byte[] orgin = Encoding.UTF8.GetBytes(OriginalString); X509Certificate2 x509_Cer1 = new X509Certificate2(publicKeyPath); RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(); oRSA.FromXmlString(x509_Cer1.PublicKey.Key.ToXmlString(false)); bool bVerify = oRSA.VerifyData(orgin, "SHA1", signedBase64); return bVerify; } /// /// 驗證簽名 /// /// 原文:UTF8編碼 /// 憑證路徑:D:/certs/mycert.key /// 驗簽 public string Sign(string data, string privateKeyPath) { RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPath); byte[] dataBytes = Encoding.UTF8.GetBytes(data); byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA1"); return Convert.ToBase64String(signatureBytes); } private byte[] GetPem(string type, byte[] data) { string pem = Encoding.UTF8.GetString(data); string header = String.Format("-----BEGIN {0}-----", type); string footer = String.Format("-----END {0}-----", type); int start = pem.IndexOf(header) + header.Length; int end = pem.IndexOf(footer, start); string base64 = pem.Substring(start, (end - start)); return Convert.FromBase64String(base64); } private RSACryptoServiceProvider LoadCertificateFile(string filename) { using (System.IO.FileStream fs = System.IO.File.OpenRead(filename)) { byte[] data = new byte[fs.Length]; byte[] res = null; fs.Read(data, 0, data.Length); if (data[0] != 0x30) { res = GetPem("RSA PRIVATE KEY", data); } try { RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res); return rsa; } catch (Exception ex) { } return null; } } private RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) { byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; // --------- Set up stream to decode the asn.1 encoded RSA private key ------ MemoryStream mem = new MemoryStream(privkey); BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; int elems = 0; try { twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; twobytes = binr.ReadUInt16(); if (twobytes != 0x0102) //version number return null; bt = binr.ReadByte(); if (bt != 0x00) return null; //------ all private key components are Integer sequences ---- elems = GetIntegerSize(binr); MODULUS = binr.ReadBytes(elems); elems = GetIntegerSize(binr); E = binr.ReadBytes(elems); elems = GetIntegerSize(binr); D = binr.ReadBytes(elems); elems = GetIntegerSize(binr); P = binr.ReadBytes(elems); elems = GetIntegerSize(binr); Q = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DP = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DQ = binr.ReadBytes(elems); elems = GetIntegerSize(binr); IQ = binr.ReadBytes(elems); // ------- create RSACryptoServiceProvider instance and initialize with public key ----- CspParameters CspParameters = new CspParameters(); CspParameters.Flags = CspProviderFlags.UseMachineKeyStore; RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024, CspParameters); RSAParameters RSAparams = new RSAParameters(); RSAparams.Modulus = MODULUS; RSAparams.Exponent = E; RSAparams.D = D; RSAparams.P = P; RSAparams.Q = Q; RSAparams.DP = DP; RSAparams.DQ = DQ; RSAparams.InverseQ = IQ; RSA.ImportParameters(RSAparams); return RSA; } catch (Exception ex) { return null; } finally { binr.Close(); } } private int GetIntegerSize(BinaryReader binr) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; int count = 0; bt = binr.ReadByte(); if (bt != 0x02) //expect integer return 0; bt = binr.ReadByte(); if (bt == 0x81) count = binr.ReadByte(); // data size in next byte else if (bt == 0x82) { highbyte = binr.ReadByte(); // data size in next 2 bytes lowbyte = binr.ReadByte(); byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } else { count = bt; // we already have the data size } while (binr.ReadByte() == 0x00) { //remove high order zeros in data count -= 1; } binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte return count; } }}
?
PHP簽名:
1)從包含公私密金鑰的pfx認證中取得.key私密金鑰:於C#的認證一致
2)公開金鑰產生:
F:\openssl-0.9.8k_WIN32\bin>openssl pkcs12 -in f:\certs\zhuo.pfx -out f:\certs\zhuo.pem
?
3)簽名及驗簽:
/*簽名資料:data:utf-8編碼的訂單原文,privatekeyFile:私密金鑰路徑passphrase:私密金鑰密碼返回:base64轉碼的簽名資料*/function sign($data, $privatekeyFile,$passphrase){ $signature = '';$privatekey = openssl_pkey_get_private(file_get_contents($privatekeyFile), $passphrase); $res=openssl_get_privatekey($privatekey); openssl_sign($data, $signature, $res); openssl_free_key($res); return base64_encode($signature);}/*驗證簽名:data:原文signature:簽名publicKeyPath:公開金鑰路徑返回:簽名結果,true為驗簽成功,false為驗簽失敗*/function verity($data, $signature, $publicKeyPath){$pubKey = file_get_contents('D:/certs/test.pem');$res = openssl_get_publickey($pubKey);$result = (bool)openssl_verify($data, base64_decode($signature), $res);openssl_free_key($res);return $result;}
?* PHP需要注意版本和一些包的匯入,如果有報錯再google了~~
?
?
?
?
?
?