这个功能网上搜了好多资料。贡献一下,转载须注明并对卓二妹的无私奉献表示感谢。
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.pem Enter Import Password:(输入导出时的密码) MAC verified OK Enter 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 -nocrypt Enter 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了~~