java/php/c#版rsa签名以及java验签实现

来源:互联网 发布:hack软件 编辑:程序博客网 时间:2024/06/05 09:56

在开放平台领域,需要给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,文件内容如下:

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

  c)生成公钥

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

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

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

-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDtd1lKsX6ylsAEWFi7E/ut8krJy9PQ7sGYKhIm9TvIdZiq5xzyaw8NOLzKZ1k486MePYG4tSuoaxSbwuPLwVUzYFvnUZo7aWCIGKn16UWTM4nxc/+dwce+bhcKrlLbTWi8l580LTE7GxclTh8z7gHq59ivhaoGbK7FNxlUfB4TSQIDAQAB-----END PUBLIC KEY-----

 2. 客户端签名

  2.1 java版签名实现

复制代码
/**     * rsa签名     *      * @param content     *            待签名的字符串     * @param privateKey     *            rsa私钥字符串     * @param charset     *            字符编码     * @return 签名结果     * @throws Exception     *             签名失败则抛出异常     */    public String rsaSign(String content, String privateKey, String charset) throws SignatureException {        try {            PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", new ByteArrayInputStream(privateKey.getBytes()));            Signature signature = Signature.getInstance("SHA1WithRSA");            signature.initSign(priKey);            if (StringUtils.isEmpty(charset)) {                signature.update(content.getBytes());            } else {                signature.update(content.getBytes(charset));            }            byte[] signed = signature.sign();            return new String(Base64.encodeBase64(signed));        } catch (Exception e) {            throw new SignatureException("RSAcontent = " + content + "; charset = " + charset, e);        }    }    public PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {        if (ins == null || StringUtils.isEmpty(algorithm)) {            return null;        }        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);        byte[] encodedKey = StreamUtil.readText(ins).getBytes();        encodedKey = Base64.decodeBase64(encodedKey);        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));    }
复制代码

 注意:参数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格式,需要使用以下命令转换一下:


在用java作为服务器端语言,对订单信息进行RSA签名的时候,私钥要求是PKCS8格式的。可以通过以下命令对原始key进行格式转换:
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem


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

2.2 php签名实现

复制代码
function sign($content, $rsaPrivateKeyPem) {        $priKey = file_get_contents($rsaPrivateKeyPem);        $res = openssl_get_privatekey($priKey);        openssl_sign($content, $sign, $res);        openssl_free_key($res);        $sign = base64_encode($sign);        return $sign;    }
复制代码

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

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

复制代码
using System;using System.Text;using System.Security.Cryptography;using System.Web;using System.IO;namespace Aop.Api.Util{    /// <summary>    /// RSA签名工具类。    /// </summary>    public class RSAUtil    {        public static string RSASign(string data, string privateKeyPem)        {            RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPem);            byte[] dataBytes = Encoding.UTF8.GetBytes(data);            byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA1");            return Convert.ToBase64String(signatureBytes);        }        private static byte[] GetPem(string type, byte[] data)        {            string pem = Encoding.UTF8.GetString(data);            string header = String.Format("-----BEGIN {0}-----\\n", 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 static 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 static 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 static 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;        }    }}
复制代码

注:privateKeyPem为私钥文件路径

  3. 服务端java验签

复制代码
/**     * rsa验签     *      * @param content 被签名的内容     * @param sign 签名后的结果     * @param publicKey rsa公钥     * @param charset 字符集     * @return 验签结果     * @throws SignatureException 验签失败,则抛异常     */    boolean doCheck(String content, String sign, String publicKey, String charset) throws SignatureException {        try {            PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));            Signature signature = Signature.getInstance("SHA1WithRSA");            signature.initVerify(pubKey);            signature.update(getContentBytes(content, charset));            return signature.verify(Base64.decodeBase64(sign.getBytes()));        } catch (Exception e) {            throw new SignatureException("RSA验证签名[content = " + content + "; charset = " + charset                                         + "; signature = " + sign + "]发生异常!", e);        }    }    private PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws NoSuchAlgorithmException {        try {            KeyFactory keyFactory = KeyFactory.getInstance(algorithm);            StringWriter writer = new StringWriter();            StreamUtil.io(new InputStreamReader(ins), writer);            byte[] encodedKey = writer.toString().getBytes();            // 先base64解码            encodedKey = Base64.decodeBase64(encodedKey);            return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));        } catch (IOException ex) {            // 不可能发生        } catch (InvalidKeySpecException ex) {            // 不可能发生        }        return null;    }    private byte[] getContentBytes(String content, String charset) throws UnsupportedEncodingException {        if (StringUtil.isEmpty(charset)) {            return content.getBytes();        }        return content.getBytes(charset);    }
复制代码

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

 

原文地址:http://xw-z1985.iteye.com/blog/1837376

0 0
原创粉丝点击