本文为转载,请转载请注明地址: 原文地址为        http://xw-z1985.iteye.com/blog/1837376

在开放平台领域,需要给isv提供sdk,签名是Sdk中需要提供的功能之一。由于isv使用的开发语言不是单一的,因此sdk需要提供多种语言的版 本。譬如java、php、c#。另外,在电子商务尤其是支付领域,对安全性的要求比较高,所以会采用非对称密钥RSA

本文主要介绍如何基于java、php、c#在客户端使用rsa签名,然后在服务端使用Java验签。

  1. 基于openssl生成RSA公私钥对

a)从网上下载openssl工具:http://www.slproweb.com/products/Win32OpenSSL.html

b)生成私钥

进入到openssl的bin目录下,执行以下命令:

openssl genrsa -out rsa_private_key.pem 1024

会在bin目录下看到新生成的私钥文件rsa_private_key.pem,文件内容如下:

  1. -----BEGIN RSA PRIVATE KEY-----
  2. MIICXgIBAAKBgQDtd1lKsX6ylsAEWFi7E/ut8krJy9PQ7sGYKhIm9TvIdZiq5xzy
  3. aw8NOLzKZ1k486MePYG4tSuoaxSbwuPLwVUzYFvnUZo7aWCIGKn16UWTM4nxc/+d
  4. wce+bhcKrlLbTWi8l580LTE7GxclTh8z7gHq59ivhaoGbK7FNxlUfB4TSQIDAQAB
  5. AoGBAIgTk0x1J+hI8KHMypPxoJCOPoMi1S9uEewTd7FxaB+4G5Mbuv/Dj62A7NaD
  6. oKI9IyUqE9L3ppvtOLMFXCofkKU0p4j7MEJdZ+CjVvgextkWa80nj/UZiM1oOL6Y
  7. HwH4ZtPtY+pFCTK1rdn3+070qBB9tnVntbN/jq0Ld7f0t7UNAkEA9ryI0kxJL9Pu
  8. pO9NEeWuCUo4xcl9x/M9+mtkfY3VoDDDV1E/eUjmoTfANYwrjcddiQrO0MLyEdoo
  9. tiLpN77qOwJBAPZhtv/+pqMVTrLxWnVKLZ4ZVTPPgJQQkFdhWwYlz7oKzB3VbQRt
  10. /jLFXUyCN2eCP7rglrXnaz7AYBftF0ajHEsCQQDDNfkeQULqN0gpcDdOwKRIL1Pp
  11. kHgWmWlg1lTETVJGEi6Kx/prL/VgeiZ1dzgCTUjAoy9r1cEFxM/PAqH3+/F/AkEA
  12. zsTCp6Q2hLblDRewKq7OCdiIwKpr5dbgy/RQR6CD7EYTdxYeH5GPu1wXKJY/mQae
  13. JV9GG/LS9h7MhkfbONS6cQJAdBEb5vloBDLcSQFDQO/VZ9SKFHCmHLXluhhIizYK
  14. Gzgf3OXEGNDSAC3qy+ZTnLd3N5iYrVbK52UoiLOLhhNMqA==
  15. -----END RSA PRIVATE KEY-----

c)生成公钥

在bin目录下,执行以下命令:

openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

会在bin目录下看到新生成的公钥文件rsa_public_key.pem,文件内容如下:

  1. -----BEGIN PUBLIC KEY-----
  2. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDtd1lKsX6ylsAEWFi7E/ut8krJ
  3. y9PQ7sGYKhIm9TvIdZiq5xzyaw8NOLzKZ1k486MePYG4tSuoaxSbwuPLwVUzYFvn
  4. UZo7aWCIGKn16UWTM4nxc/+dwce+bhcKrlLbTWi8l580LTE7GxclTh8z7gHq59iv
  5. haoGbK7FNxlUfB4TSQIDAQAB
  6. -----END PUBLIC KEY-----

2. 客户端签名

2.1 java版签名实现

  1. /**
  2. * rsa签名 * @param content
  3. * 待签名的字符串
  4. * @param privateKey
  5. * rsa私钥字符串
  6. * @param charset
  7. * 字符编码
  8. * @return 签名结果
  9. * @throws Exception
  10. * 签名失败则抛出异常
  11. */
  12. public String rsaSign(String content, String privateKey, String charset) throws SignatureException {
  13. try {
  14. PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", new ByteArrayInputStream(privateKey.getBytes()));
  15.  
  16. Signature signature = Signature.getInstance("SHA1WithRSA");
  17. signature.initSign(priKey);
  18. if (StringUtils.isEmpty(charset)) {
  19. signature.update(content.getBytes());
  20. } else {
  21. signature.update(content.getBytes(charset));
  22. }
  23.  
  24. byte[] signed = signature.sign();
  25. return new String(Base64.encodeBase64(signed));
  26. } catch (Exception e) {
  27. throw new SignatureException("RSAcontent = " + content + "; charset = " + charset, e);
  28. }
  29. }
  30.  
  31. public PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
  32. if (ins == null || StringUtils.isEmpty(algorithm)) {
  33. return null;
  34. }
  35.  
  36. KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
  37. byte[] encodedKey = StreamUtil.readText(ins).getBytes();
  38. encodedKey = Base64.decodeBase64(encodedKey);
  39. return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
  40. }

注意:参数privateKey是Pem私钥文件中去除头(-----BEGIN RSA PRIVATE KEY-----)和尾(-----END RSA PRIVATE KEY-----)以及换行符后的字符串。

如果签名报以下错误:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence

则说明rsa私钥的格式不是pksc8格式,需要使用以下命令转换一下:

openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt

然后再提取去除头和尾以及换行符后字符串作为java版用的rsa私钥

2.2 php签名实现

  1. function sign($content, $rsaPrivateKeyPem) {
  2. $priKey = file_get_contents($rsaPrivateKeyPem);
  3. $res = openssl_get_privatekey($priKey);
  4. openssl_sign($content, $sign, $res);
  5. openssl_free_key($res);
  6. $sign = base64_encode($sign);
  7. return $sign;
  8. }

注意:$rsaPrivateKeyPem为pem私钥文件路径

2.3 c#签名实现(引用了国外某位仁兄的方案)

  1. using System;
  2. using System.Text;
  3. using System.Security.Cryptography;
  4. using System.Web;
  5. using System.IO;
  6.  
  7. namespace Aop.Api.Util
  8. {
  9. /// <summary>
  10. /// RSA签名工具类。
  11. /// </summary>
  12. public class RSAUtil
  13. {
  14.  
  15. public static string RSASign(string data, string privateKeyPem)
  16. {
  17. RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPem);
  18. byte[] dataBytes = Encoding.UTF8.GetBytes(data);
  19. byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA1");
  20. return Convert.ToBase64String(signatureBytes);
  21. }
  22.  
  23. private static byte[] GetPem(string type, byte[] data)
  24. {
  25. string pem = Encoding.UTF8.GetString(data);
  26. string header = String.Format("-----BEGIN {0}-----\\n", type);
  27. string footer = String.Format("-----END {0}-----", type);
  28. int start = pem.IndexOf(header) + header.Length;
  29. int end = pem.IndexOf(footer, start);
  30. string base64 = pem.Substring(start, (end - start));
  31. return Convert.FromBase64String(base64);
  32. }
  33.  
  34. private static RSACryptoServiceProvider LoadCertificateFile(string filename)
  35. {
  36. using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
  37. {
  38. byte[] data = new byte[fs.Length];
  39. byte[] res = null;
  40. fs.Read(data, , data.Length);
  41. if (data[] != 0x30)
  42. {
  43. res = GetPem("RSA PRIVATE KEY", data);
  44. }
  45. try
  46. {
  47. RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res);
  48. return rsa;
  49. }
  50. catch (Exception ex)
  51. {
  52. }
  53. return null;
  54. }
  55. }
  56.  
  57. private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
  58. {
  59. byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
  60.  
  61. // --------- Set up stream to decode the asn.1 encoded RSA private key ------
  62. MemoryStream mem = new MemoryStream(privkey);
  63. BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
  64. byte bt = ;
  65. ushort twobytes = ;
  66. int elems = ;
  67. try
  68. {
  69. twobytes = binr.ReadUInt16();
  70. if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
  71. binr.ReadByte(); //advance 1 byte
  72. else if (twobytes == 0x8230)
  73. binr.ReadInt16(); //advance 2 bytes
  74. else
  75. return null;
  76.  
  77. twobytes = binr.ReadUInt16();
  78. if (twobytes != 0x0102) //version number
  79. return null;
  80. bt = binr.ReadByte();
  81. if (bt != 0x00)
  82. return null;
  83.  
  84. //------ all private key components are Integer sequences ----
  85. elems = GetIntegerSize(binr);
  86. MODULUS = binr.ReadBytes(elems);
  87.  
  88. elems = GetIntegerSize(binr);
  89. E = binr.ReadBytes(elems);
  90.  
  91. elems = GetIntegerSize(binr);
  92. D = binr.ReadBytes(elems);
  93.  
  94. elems = GetIntegerSize(binr);
  95. P = binr.ReadBytes(elems);
  96.  
  97. elems = GetIntegerSize(binr);
  98. Q = binr.ReadBytes(elems);
  99.  
  100. elems = GetIntegerSize(binr);
  101. DP = binr.ReadBytes(elems);
  102.  
  103. elems = GetIntegerSize(binr);
  104. DQ = binr.ReadBytes(elems);
  105.  
  106. elems = GetIntegerSize(binr);
  107. IQ = binr.ReadBytes(elems);
  108.  
  109. // ------- create RSACryptoServiceProvider instance and initialize with public key -----
  110. CspParameters CspParameters = new CspParameters();
  111. CspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
  112. RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(, CspParameters);
  113. RSAParameters RSAparams = new RSAParameters();
  114. RSAparams.Modulus = MODULUS;
  115. RSAparams.Exponent = E;
  116. RSAparams.D = D;
  117. RSAparams.P = P;
  118. RSAparams.Q = Q;
  119. RSAparams.DP = DP;
  120. RSAparams.DQ = DQ;
  121. RSAparams.InverseQ = IQ;
  122. RSA.ImportParameters(RSAparams);
  123. return RSA;
  124. }
  125. catch (Exception ex)
  126. {
  127. return null;
  128. }
  129. finally
  130. {
  131. binr.Close();
  132. }
  133. }
  134.  
  135. private static int GetIntegerSize(BinaryReader binr)
  136. {
  137. byte bt = ;
  138. byte lowbyte = 0x00;
  139. byte highbyte = 0x00;
  140. int count = ;
  141. bt = binr.ReadByte();
  142. if (bt != 0x02) //expect integer
  143. return ;
  144. bt = binr.ReadByte();
  145.  
  146. if (bt == 0x81)
  147. count = binr.ReadByte(); // data size in next byte
  148. else
  149. if (bt == 0x82)
  150. {
  151. highbyte = binr.ReadByte(); // data size in next 2 bytes
  152. lowbyte = binr.ReadByte();
  153. byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
  154. count = BitConverter.ToInt32(modint, );
  155. }
  156. else
  157. {
  158. count = bt; // we already have the data size
  159. }
  160.  
  161. while (binr.ReadByte() == 0x00)
  162. { //remove high order zeros in data
  163. count -= ;
  164. }
  165. binr.BaseStream.Seek(-, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
  166. return count;
  167. }
  168. }
  169. }

注:privateKeyPem为私钥文件路径

3. 服务端java验签

  1. /**
  2. * rsa验签
  3. *
  4. * @param content 被签名的内容
  5. * @param sign 签名后的结果
  6. * @param publicKey rsa公钥
  7. * @param charset 字符集
  8. * @return 验签结果
  9. * @throws SignatureException 验签失败,则抛异常
  10. */
  11. boolean doCheck(String content, String sign, String publicKey, String charset) throws SignatureException {
  12. try {
  13. PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
  14.  
  15. Signature signature = Signature.getInstance("SHA1WithRSA");
  16. signature.initVerify(pubKey);
  17. signature.update(getContentBytes(content, charset));
  18. return signature.verify(Base64.decodeBase64(sign.getBytes()));
  19. } catch (Exception e) {
  20. throw new SignatureException("RSA验证签名[content = " + content + "; charset = " + charset
  21. + "; signature = " + sign + "]发生异常!", e);
  22. }
  23. }
  24.  
  25. private PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws NoSuchAlgorithmException {
  26. try {
  27. KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
  28.  
  29. StringWriter writer = new StringWriter();
  30. StreamUtil.io(new InputStreamReader(ins), writer);
  31. byte[] encodedKey = writer.toString().getBytes();
  32.  
  33. // 先base64解码
  34. encodedKey = Base64.decodeBase64(encodedKey);
  35. return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
  36. } catch (IOException ex) {
  37. // 不可能发生
  38. } catch (InvalidKeySpecException ex) {
  39. // 不可能发生
  40. }
  41. return null;
  42. }
  43.  
  44. private byte[] getContentBytes(String content, String charset) throws UnsupportedEncodingException {
  45. if (StringUtil.isEmpty(charset)) {
  46. return content.getBytes();
  47. }
  48.  
  49. return content.getBytes(charset);
  50. }

注意:参数publicKey是Pem公钥文件中去除头(-----BEGIN RSA PRIVATE KEY-----)和尾(-----END RSA PRIVATE KEY-----)以及换行符后的字符串。

java/php/c#版rsa签名以及验签实现的更多相关文章

  1. Delphi RSA签名与验签【支持SHA1WithRSA(RSA1)、SHA256WithRSA(RSA2)和MD5WithRSA签名与验签】

    作者QQ:(648437169) 点击下载➨ RSA签名与验签 [delphi RSA签名与验签]支持3种方式签名与验签(SHA1WithRSA(RSA1).SHA256WithRSA(RSA2)和M ...

  2. erlang的RSA签名与验签

    1.RSA介绍 RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对 其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而 ...

  3. .Net C# RSA签名和验签

    帮助类 using System; using System.Text; using System.IO; using System.Security.Cryptography; namespace ...

  4. java/php/c#版rsa签名以及java验签实现--转

    在开放平台领域,需要给isv提供sdk,签名是Sdk中需要提供的功能之一.由于isv使用的开发语言不是单一的,因此sdk需要提供多种语言的版本.譬如java.php.c#.另外,在电子商务尤其是支付领 ...

  5. .NET Core RSA 签名和验签(密钥为 16 进制编码)

    使用 OpenSSL 生成公私钥对,命令: $ openssl genrsa -out rsa_1024_priv.pem $ openssl pkcs8 -topk8 -inform PEM -in ...

  6. RSA签名和验签Util

    目录 1.DigitalSign类 2.CryptException异常类 3.加签示例 1.DigitalSign类 import org.apache.commons.codec.binary.B ...

  7. .Net C# RSA签名和验签重写

    namespace com._80community.unittest.CUP { /// <summary> /// CUP Client /// </summary> pu ...

  8. RSA签名、验签、加密、解密

    最近在做一个项目,与一个支付渠道进行连接进行充值,为了安全,每个接口访问时,都要先登陆(调用登陆接口),拿到一个sessionKey,后续业务接口内容用它来进行3DES加密处理.而登陆需要用到RSA进 ...

  9. .NET RSA解密、签名、验签

    using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Sec ...

随机推荐

  1. SerialPort

    using System;   using System.Collections.Generic;   using System.ComponentModel;   using System.Data ...

  2. 在CSDN上看到的一个过滤方法,感觉还不错

    /// <summary> /// 把字符串中包含的敏感词替换成别的关键字 /// </summary> /// <param name="s"> ...

  3. Mvc5+Entity Framework6 之二----在MVC中用Entity Framework实现基本的CRUD

    目标:创建控制器和视图的代码,实现CRUD(创建,读取,更新,删除)功能 创建一个详细信息页 控制器为Students的Index页生成的代码排除Enrollments属性在外,因为该属性中关联着一个 ...

  4. windows server 2008 应用程序池自动关闭 C:\Windows\system32\RpcProxy\RpcProxy.dll failed to load

    Sign In Join Search IIS Home Downloads Learn Reference Solutions Blogs Forums HomeIIS.NET Forums64-b ...

  5. Android Material Design之Toolbar与Palette

    转:http://blog.csdn.net/jdsjlzx/article/details/41441083 前言 我们都知道Marterial Design是Google推出的全新UI设计规范,如 ...

  6. yum 安装软件时报Public key for * is not installed

    这个是由于没有导入rpm签名信息引起的 解决方案: rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

  7. [转] Mysql 数据库引擎

    你能用的数据库引擎取决于mysql在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外两种类型INNO ...

  8. opennebula auth module ldap

    1,安装net-ldap  addon ruby library for openldap

  9. position:relative可以默认提升元素的z-index;

    position:relative可以默认提升元素的z-index; 相对没有添加position的元素来说:

  10. 在magento中使用正则式

    $sqlCondition = "IFNULL(_table_name.value, _table_name_default.value) REGEXP '^[^a-zA-Z]'" ...