zoukankan      html  css  js  c++  java
  • c#与js的rsa加密互通

    ASN.1

     抽象语法表示(标记)ASN.1(Abstract Syntax Notation One )一种数据定义语言,描述了对数据进行表示、编码、传输和解码的数据格式。网络管理系统中的管理信息库(MIB)、应用程序的数据结构、协议数据单元(PDU)都是用ASN.1定义的。

    可以理解为ASN.1是对密钥结构定义的一种规范

    密钥结构类型

    PKCS#1

    RSAPublicKey ::= SEQUENCE {
        modulus           INTEGER,  -- n
        publicExponent    INTEGER   -- e
    }
    
    RSAPrivateKey ::= SEQUENCE {
      version           Version,
      modulus           INTEGER,  -- n
      publicExponent    INTEGER,  -- e
      privateExponent   INTEGER,  -- d
      prime1            INTEGER,  -- p
      prime2            INTEGER,  -- q
      exponent1         INTEGER,  -- d mod (p-1)
      exponent2         INTEGER,  -- d mod (q-1)
      coefficient       INTEGER,  -- (inverse of q) mod p
      otherPrimeInfos   OtherPrimeInfos OPTIONAL
    }

    PKCS#8

    PublicKeyInfo ::= SEQUENCE {
      algorithm       AlgorithmIdentifier,
      PublicKey       BIT STRING ; 其中的BIT STRING是某个算法自己指定的二进制格式
                                 ; RSA算法的话,就是上面的RSAPublicKey
    }
    
    AlgorithmIdentifier ::= SEQUENCE {
      algorithm       OBJECT IDENTIFIER,
      parameters      ANY DEFINED BY algorithm OPTIONAL
    }
    
    PrivateKeyInfo ::= SEQUENCE {
      version         Version,
      algorithm       AlgorithmIdentifier,
      PrivateKey      BIT STRING
    }
    
    AlgorithmIdentifier ::= SEQUENCE {
      algorithm       OBJECT IDENTIFIER,
      parameters      ANY DEFINED BY algorithm OPTIONAL
    }

    密钥编码类型

    der格式

    二进制格式

    pem格式

    把der格式的数据用base64编码后,然后再在头尾加上一段“-----”开始的标记

    证书类型

    X.509证书

    X.509只包含公钥,没有私钥,这种证书一般公开发布,可用于放在客服端使用,用于加密、验签

    PKCS#12证书

    因为X.509证书只包含公钥,但有些时候我们需要把私钥和公钥合并成一个证书,放在服务端使用,用于解密、签名。

    PKCS#12就定义了这样一种证书,它既包含了公钥有包含了私钥。典型的入pfx、p12证书就是PKCS#12证书。

    PKCS#7证书

    当你收到一个网站的证书后,你需要验证其真实性。因为一个X.509证书包含了公钥、持有人信息、签名。为了验证其真实性,你需要签证其签名,而验证签名则需要签发的CA机构的公钥证书。同样原理,当你拿到CA机构的公钥证书后,你也需要验证该CA机构的真实性,而验证该CA机构的证书,你需要该CA上级机构的CA公钥证书...以此类推,你需要一直验证到根证书为止。所以为了验证一个网站证书的真实性,你需要的不仅一张证书,而是一个证书链。而PKCS#7就定义了这样一个证书链的类型结构。典型如p7b后缀名的证书就是这样的格式。

    证书后缀

    .cer/.crt:存放公钥,没有私钥,就是一个X.509证书,二进制形式存放

    .pfx/.p12:存放公钥和私钥,通常包含保护密码,二进制方式

    证书与密钥关系

    数字证书和私钥是匹配的关系。就好比钥匙牌和钥匙的关系。在数字证书签发的时候,数字证书签发系统(CA系统),在生成数字证书的同时,还会随机生成一对密钥,一个私钥,一个公钥。数字证书标示用户身份, 相匹配的私钥和公钥,则是用来保障用户身份的可认证性。就好比咱们拿着一串钥匙,每个钥匙上都标明有时某某房间的钥匙,但是否是真的,还需要看能不能打开相应的房门。

    密钥生成

       /// <summary>
            /// 取得私钥和公钥 XML 格式,返回数组第一个是私钥,第二个是公钥.
            /// </summary>
            /// <param name="size">密钥长度,默认1024,可以为2048</param>
            /// <returns></returns>
            public static string[] CreateXmlKey(int size = 1024)
            {
                //密钥格式要生成pkcs#1格式的  而不是pkcs#8格式的
                RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size);
                string privateKey = sp.ToXmlString(true);//private key
                string publicKey = sp.ToXmlString(false);//public  key
                return new string[] { privateKey, publicKey };
            }
    
            /// <summary>
            /// 取得私钥和公钥 CspBlob 格式,返回数组第一个是私钥,第二个是公钥.
            /// </summary>
            /// <param name="size"></param>
            /// <returns></returns>
            public static string[] CreateCspBlobKey(int size = 1024)
            {
                //密钥格式要生成pkcs#1格式的  而不是pkcs#8格式的
                RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size);
                string privateKey = System.Convert.ToBase64String(sp.ExportCspBlob(true));//private key
                string publicKey = System.Convert.ToBase64String(sp.ExportCspBlob(false));//public  key 
    
                return new string[] { privateKey, publicKey };
            }
            /// <summary>
            /// 导出PEM PKCS#1格式密钥对,返回数组第一个是私钥,第二个是公钥.
            /// </summary>
            public static string[] CreateKey_PEM_PKCS1(int size = 1024)
            {
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size);
                string privateKey = RSA_PEM.ToPEM(rsa, false, false);
                string publicKey = RSA_PEM.ToPEM(rsa, true, false);
                return new string[] { privateKey, publicKey };
            }
    
            /// <summary>
            /// 导出PEM PKCS#8格式密钥对,返回数组第一个是私钥,第二个是公钥.
            /// </summary>
            public static string[] CreateKey_PEM_PKCS8(int size = 1024, bool convertToPublic = false)
            {
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size);
                string privateKey = RSA_PEM.ToPEM(rsa, false, true);
                string publicKey = RSA_PEM.ToPEM(rsa, true, true);
                return new string[] { privateKey, publicKey };
    
            }

    后端加/解密方法使用

       /// <summary>
            /// RSA加密
            /// </summary>
            /// <param name="Data">原文</param>
            /// <param name="PublicKeyString">公钥</param>
            /// <param name="KeyType">密钥类型XML/PEM</param>
            /// <returns></returns>
            public static string RSAEncrypt(string Data,string PublicKeyString,string KeyType)
            {
                byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(Data);
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                switch (KeyType)
                {
                    case "XML":
                        rsa.FromXmlString(PublicKeyString);
                        break;
                    case "PEM":
                        rsa = RSA_PEM.FromPEM(PublicKeyString);
                        break;
                    default:
                        throw new Exception("不支持的密钥类型");
                }
                //加密块最大长度限制,如果加密数据的长度超过 秘钥长度/8-11,会引发长度不正确的异常,所以进行数据的分块加密
                int MaxBlockSize = rsa.KeySize / 8 - 11;
                //正常长度
                if (data.Length <= MaxBlockSize)
                {
                    byte[] hashvalueEcy = rsa.Encrypt(data, false); //加密
                    return System.Convert.ToBase64String(hashvalueEcy);
                }
                //长度超过正常值
                else
                {
                    using (MemoryStream PlaiStream = new MemoryStream(data))
                    using (MemoryStream CrypStream = new MemoryStream())
                    {
                        Byte[] Buffer = new Byte[MaxBlockSize];
                        int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                        while (BlockSize > 0)
                        {
                            Byte[] ToEncrypt = new Byte[BlockSize];
                            Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);
    
                            Byte[] Cryptograph = rsa.Encrypt(ToEncrypt, false);
                            CrypStream.Write(Cryptograph, 0, Cryptograph.Length);
                            BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                        }
                        return System.Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
                    }
                }
            }
    
            /// <summary>
            /// RSA解密
            /// </summary>
            /// <param name="Data">密文</param>
            /// <param name="PrivateKeyString">私钥</param>
            /// <param name="KeyType">密钥类型XML/PEM</param>
            /// <returns></returns>
            public static string RSADecrypt(string Data,string PrivateKeyString, string KeyType)
            {
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                switch (KeyType)
                {
                    case "XML":
                        rsa.FromXmlString(PrivateKeyString);
                        break;
                    case "PEM":
                        rsa = RSA_PEM.FromPEM(PrivateKeyString);
                        break;
                    default:
                        throw new Exception("不支持的密钥类型");
                }
                int MaxBlockSize = rsa.KeySize / 8;    //解密块最大长度限制
                //正常解密
                if (Data.Length <= MaxBlockSize)
                {
                    byte[] hashvalueDcy = rsa.Decrypt(System.Convert.FromBase64String(Data), false);//解密
                    return Encoding.GetEncoding("UTF-8").GetString(hashvalueDcy);
                }
                //分段解密
                else
                {
                    using (MemoryStream CrypStream = new MemoryStream(System.Convert.FromBase64String(Data)))
                    using (MemoryStream PlaiStream = new MemoryStream())
                    {
                        Byte[] Buffer = new Byte[MaxBlockSize];
                        int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
    
                        while (BlockSize > 0)
                        {
                            Byte[] ToDecrypt = new Byte[BlockSize];
                            Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);
    
                            Byte[] Plaintext = rsa.Decrypt(ToDecrypt, false);
                            PlaiStream.Write(Plaintext, 0, Plaintext.Length);
                            BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
                        }
                        string output = Encoding.GetEncoding("UTF-8").GetString(PlaiStream.ToArray());
                        return output;
                    }
                }
            }

    前端加密方法

    注:jsencrypt默认PKCS#1结构,生成密钥时需要注意

     <script src="http://passport.cnblogs.com/scripts/jsencrypt.min.js"></script>
      var encryptor = new JSEncrypt()  // 创建加密对象实例
      //之前ssl生成的公钥,复制的时候要小心不要有空格
      var pubKey = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1QQRl0HlrVv6kGqhgonD6A9SU6ZJpnEN+Q0blT/ue6Ndt97WRfxtS'+
      'As0QoquTreaDtfC4RRX4o+CU6BTuHLUm+eSvxZS9TzbwoYZq7ObbQAZAY+SYDgAA5PHf1wNN20dGMFFgVS/y0ZWvv1UNa2laEz0I8Vmr5ZlzIn88GkmSiQIDAQAB-----END PUBLIC KEY-----'
      encryptor.setPublicKey(pubKey)//设置公钥
      var rsaPassWord = encryptor.encrypt('要加密的内容')  // 对内容进行加密

    c#pem格式转换

    注:c#的RSACryptoServiceProvider默认只支持xml格式的密钥解析

     public class RSA_Unit
        {
            static public string Base64EncodeBytes(byte[] byts)
            {
                return System.Convert.ToBase64String(byts);
            }
            static public byte[] Base64DecodeBytes(string str)
            {
                try
                {
                    return System.Convert.FromBase64String(str);
                }
                catch
                {
                    return null;
                }
            }
            /// <summary>
            /// 把字符串按每行多少个字断行
            /// </summary>
            static public string TextBreak(string text, int line)
            {
                var idx = 0;
                var len = text.Length;
                var str = new StringBuilder();
                while (idx < len)
                {
                    if (idx > 0)
                    {
                        str.Append('
    ');
                    }
                    if (idx + line >= len)
                    {
                        str.Append(text.Substring(idx));
                    }
                    else
                    {
                        str.Append(text.Substring(idx, line));
                    }
                    idx += line;
                }
                return str.ToString();
            }
    
        }
        static public class Extensions
        {
            /// <summary>
            /// 从数组start开始到指定长度复制一份
            /// </summary>
            static public T[] sub<T>(this T[] arr, int start, int count)
            {
                T[] val = new T[count];
                for (var i = 0; i < count; i++)
                {
                    val[i] = arr[start + i];
                }
                return val;
            }
            static public void writeAll(this Stream stream, byte[] byts)
            {
                stream.Write(byts, 0, byts.Length);
            }
        }
    点击并拖拽以移动
     public class RSA_PEM
        {
            public static RSACryptoServiceProvider FromPEM(string pem)
            {
                var rsaParams = new CspParameters();
                rsaParams.Flags = CspProviderFlags.UseMachineKeyStore;
                var rsa = new RSACryptoServiceProvider(rsaParams);
    
                var param = new RSAParameters();
    
                var base64 = _PEMCode.Replace(pem, "");
                var data = RSA_Unit.Base64DecodeBytes(base64);
                if (data == null)
                {
                    throw new Exception("PEM内容无效");
                }
                var idx = 0;
    
                //读取长度
                Func<byte, int> readLen = (first) =>
                {
                    if (data[idx] == first)
                    {
                        idx++;
                        if (data[idx] == 0x81)
                        {
                            idx++;
                            return data[idx++];
                        }
                        else if (data[idx] == 0x82)
                        {
                            idx++;
                            return (((int)data[idx++]) << 8) + data[idx++];
                        }
                        else if (data[idx] < 0x80)
                        {
                            return data[idx++];
                        }
                    }
                    throw new Exception("PEM未能提取到数据");
                };
                //读取块数据
                Func<byte[]> readBlock = () =>
                {
                    var len = readLen(0x02);
                    if (data[idx] == 0x00)
                    {
                        idx++;
                        len--;
                    }
                    var val = data.sub(idx, len);
                    idx += len;
                    return val;
                };
                //比较data从idx位置开始是否是byts内容
                Func<byte[], bool> eq = (byts) =>
                {
                    for (var i = 0; i < byts.Length; i++, idx++)
                    {
                        if (idx >= data.Length)
                        {
                            return false;
                        }
                        if (byts[i] != data[idx])
                        {
                            return false;
                        }
                    }
                    return true;
                };
    
    
    
    
                if (pem.Contains("PUBLIC KEY"))
                {
                    /****使用公钥****/
                    //读取数据总长度
                    readLen(0x30);
                    if (!eq(_SeqOID))
                    {
                        throw new Exception("PEM未知格式");
                    }
                    //读取1长度
                    readLen(0x03);
                    idx++;//跳过0x00
                          //读取2长度
                    readLen(0x30);
    
                    //Modulus
                    param.Modulus = readBlock();
    
                    //Exponent
                    param.Exponent = readBlock();
                }
                else if (pem.Contains("PRIVATE KEY"))
                {
                    /****使用私钥****/
                    //读取数据总长度
                    readLen(0x30);
    
                    //读取版本号
                    if (!eq(_Ver))
                    {
                        throw new Exception("PEM未知版本");
                    }
    
                    //检测PKCS8
                    var idx2 = idx;
                    if (eq(_SeqOID))
                    {
                        //读取1长度
                        readLen(0x04);
                        //读取2长度
                        readLen(0x30);
    
                        //读取版本号
                        if (!eq(_Ver))
                        {
                            throw new Exception("PEM版本无效");
                        }
                    }
                    else
                    {
                        idx = idx2;
                    }
    
                    //读取数据
                    param.Modulus = readBlock();
                    param.Exponent = readBlock();
                    param.D = readBlock();
                    param.P = readBlock();
                    param.Q = readBlock();
                    param.DP = readBlock();
                    param.DQ = readBlock();
                    param.InverseQ = readBlock();
                }
                else
                {
                    throw new Exception("pem需要BEGIN END标头");
                }
    
                rsa.ImportParameters(param);
                return rsa;
            }
            static private Regex _PEMCode = new Regex(@"--+.+?--+|s+");
            static private byte[] _SeqOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
            static private byte[] _Ver = new byte[] { 0x02, 0x01, 0x00 };
    
    
            /// <summary>
            /// 将RSA中的密钥对转换成PEM格式,usePKCS8=false时返回PKCS#1格式,否则返回PKCS#8格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
            /// </summary>
            public static string ToPEM(RSACryptoServiceProvider rsa, bool convertToPublic, bool usePKCS8)
            {
                //https://www.jianshu.com/p/25803dd9527d
                //https://www.cnblogs.com/ylz8401/p/8443819.html
                //https://blog.csdn.net/jiayanhui2877/article/details/47187077
                //https://blog.csdn.net/xuanshao_/article/details/51679824
                //https://blog.csdn.net/xuanshao_/article/details/51672547
    
                var ms = new MemoryStream();
                //写入一个长度字节码
                Action<int> writeLenByte = (len) =>
                {
                    if (len < 0x80)
                    {
                        ms.WriteByte((byte)len);
                    }
                    else if (len <= 0xff)
                    {
                        ms.WriteByte(0x81);
                        ms.WriteByte((byte)len);
                    }
                    else
                    {
                        ms.WriteByte(0x82);
                        ms.WriteByte((byte)(len >> 8 & 0xff));
                        ms.WriteByte((byte)(len & 0xff));
                    }
                };
                //写入一块数据
                Action<byte[]> writeBlock = (byts) =>
                {
                    var addZero = (byts[0] >> 4) >= 0x8;
                    ms.WriteByte(0x02);
                    var len = byts.Length + (addZero ? 1 : 0);
                    writeLenByte(len);
    
                    if (addZero)
                    {
                        ms.WriteByte(0x00);
                    }
                    ms.Write(byts, 0, byts.Length);
                };
                //根据后续内容长度写入长度数据
                Func<int, byte[], byte[]> writeLen = (index, byts) =>
                {
                    var len = byts.Length - index;
    
                    ms.SetLength(0);
                    ms.Write(byts, 0, index);
                    writeLenByte(len);
                    ms.Write(byts, index, len);
    
                    return ms.ToArray();
                };
    
    
                if (rsa.PublicOnly || convertToPublic)
                {
                    /****生成公钥****/
                    var param = rsa.ExportParameters(false);
    
    
                    //写入总字节数,不含本段长度,额外需要24字节的头,后续计算好填入
                    ms.WriteByte(0x30);
                    var index1 = (int)ms.Length;
    
                    //固定内容
                    // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
                    ms.writeAll(_SeqOID);
    
                    //从0x00开始的后续长度
                    ms.WriteByte(0x03);
                    var index2 = (int)ms.Length;
                    ms.WriteByte(0x00);
    
                    //后续内容长度
                    ms.WriteByte(0x30);
                    var index3 = (int)ms.Length;
    
                    //写入Modulus
                    writeBlock(param.Modulus);
    
                    //写入Exponent
                    writeBlock(param.Exponent);
    
    
                    //计算空缺的长度
                    var byts = ms.ToArray();
    
                    byts = writeLen(index3, byts);
                    byts = writeLen(index2, byts);
                    byts = writeLen(index1, byts);
    
    
                    return "-----BEGIN PUBLIC KEY-----
    " + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "
    -----END PUBLIC KEY-----";
                }
                else
                {
                    /****生成私钥****/
                    var param = rsa.ExportParameters(true);
    
                    //写入总字节数,后续写入
                    ms.WriteByte(0x30);
                    int index1 = (int)ms.Length;
    
                    //写入版本号
                    ms.writeAll(_Ver);
    
                    //PKCS8 多一段数据
                    int index2 = -1, index3 = -1;
                    if (usePKCS8)
                    {
                        //固定内容
                        ms.writeAll(_SeqOID);
    
                        //后续内容长度
                        ms.WriteByte(0x04);
                        index2 = (int)ms.Length;
    
                        //后续内容长度
                        ms.WriteByte(0x30);
                        index3 = (int)ms.Length;
    
                        //写入版本号
                        ms.writeAll(_Ver);
                    }
    
                    //写入数据
                    writeBlock(param.Modulus);
                    writeBlock(param.Exponent);
                    writeBlock(param.D);
                    writeBlock(param.P);
                    writeBlock(param.Q);
                    writeBlock(param.DP);
                    writeBlock(param.DQ);
                    writeBlock(param.InverseQ);
    
    
                    //计算空缺的长度
                    var byts = ms.ToArray();
    
                    if (index2 != -1)
                    {
                        byts = writeLen(index3, byts);
                        byts = writeLen(index2, byts);
                    }
                    byts = writeLen(index1, byts);
    
    
                    var flag = " PRIVATE KEY";
                    if (!usePKCS8)
                    {
                        flag = " RSA" + flag;
                    }
                    return "-----BEGIN" + flag + "-----
    " + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "
    -----END" + flag + "-----";
                }
            }
        }
  • 相关阅读:
    Redis5.x五种数据类型常见命令
    Redis5.x安装以及常见数据类型
    《Redis5.x入门教程》正式推出
    PPT制作套路指南
    如何更优雅地对接第三方API
    软件开发要质量还是要效率?
    前后端分离对于开发人员的挑战
    Spring中老生常谈的FactoryBean
    消费端如何保证消息队列MQ的有序消费
    《ElasticSearch6.x实战教程》之实战ELK日志分析系统、多数据源同步
  • 原文地址:https://www.cnblogs.com/yanpeng19940119/p/14589214.html
Copyright © 2011-2022 走看看