zoukankan      html  css  js  c++  java
  • .NET Core加解密实战系列之——RSA非对称加密算法


    简介

    加解密现状,编写此项目的背景:

    • 需要考虑系统环境兼容性问题(Linux、Windows)
    • 语言互通问题(如C#、Java)
    • 网上资料版本不一、不全面
    • .NET官方库密码算法提供不全面,很难针对其他语言(Java)进行适配

    本系列文章主要介绍如何结合BouncyCastle在 .NET Core 中使用非对称加密算法、编码算法、哈希算法、对称加密算法、国密算法等一系列算法,内容篇幅代码居多(加解密算法相关的原理知识网上有很多,因此不过多介绍)。如有错误之处,还请大家批评指正。

    本系列代码项目地址:https://github.com/fuluteam/ICH.BouncyCastle.git

    功能依赖

    BouncyCastle(https://www.bouncycastle.org/csharp) 是一个开放源码的轻量级密码术包;它支持大量的密码术算法,它提供了很多.NET Core标准库没有的算法。

    支持.NET 4,.NET Standard 1.0-2.0,WP,Silverlight,MonoAndroid,Xamarin.iOS,.NET Core

    功能 依赖
    Portable.BouncyCastle Portable.BouncyCastle • 1.8.5

    生成RSA秘钥

    PKCS1格式

    /// <summary>
    /// PKCS1(非Java适用)
    /// </summary>
    /// <param name="keySize">密钥长度”一般只是指模值的位长度。目前主流可选值:1024、2048、3072、4096...</param>
    /// <param name="format">PEM格式</param>
    /// <returns></returns>
    public RSAKeyParameter Pkcs1(int keySize, bool format=false)
    {
        var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA");
        keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));
    
        var keyPair = keyGenerator.GenerateKeyPair();
    
        var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
        var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
        
        if (!format)
        {
            return new RSAKeyParameter
            {
                PrivateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()),
                PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded())
            };
        }
    
        var rsaKey = new RSAKeyParameter();
        using (var sw = new StringWriter())
        {
            var pWrt = new PemWriter(sw);
            pWrt.WriteObject(keyPair.Private);
            pWrt.Writer.Close();
            rsaKey.PrivateKey = sw.ToString();
        }
    
        using (var sw = new StringWriter())
        {
            var pWrt = new PemWriter(sw);
            pWrt.WriteObject(keyPair.Public);
            pWrt.Writer.Close();
            rsaKey.PublicKey = sw.ToString();
        }
    
        return rsaKey;
    }
    

    PKCS8格式

    /// <summary>
    /// PKCS8(JAVA适用)
    /// </summary>
    /// <param name="keySize">密钥长度”一般只是指模值的位长度。目前主流可选值:1024、2048、3072、4096...</param>
    /// <param name="format">PEM格式</param>
    /// <returns></returns>
    public RSAKeyParameter Pkcs8(int keySize, bool format=false)
    {
        var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA");
        keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));
        var keyPair = keyGenerator.GenerateKeyPair();
    
        var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
        var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
    
        if (!format)
        {
            return new RSAKeyParameter
            {
                PrivateKey = Base64.ToBase64String(privateKeyInfo.GetEncoded()),
                PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded())
            };
        }
    
        var rsaKey = new RSAKeyParameter();
        using (var sw = new StringWriter())
        {
            var pWrt = new PemWriter(sw);
            var pkcs8 = new Pkcs8Generator(keyPair.Private);
            pWrt.WriteObject(pkcs8);
            pWrt.Writer.Close();
            rsaKey.PrivateKey = sw.ToString();
        }
    
        using (var sw = new StringWriter())
        {
            var pWrt = new PemWriter(sw);
            pWrt.WriteObject(keyPair.Public);
            pWrt.Writer.Close();
            rsaKey.PublicKey = sw.ToString();
        }
    
        return rsaKey;
    }
    

    私钥操作

    PKCS1与PKCS8格式互转

    仅私钥有PKCS1和PKCS8之分,公钥无格式区别。

     /// <summary>
     /// Pkcs1>>Pkcs8
     /// </summary>
     /// <param name="privateKey">Pkcs1私钥</param>
     /// <param name="format">是否转PEM格式</param>
     /// <returns></returns>
     public static string PrivateKeyPkcs1ToPkcs8(string privateKey, bool format = false)
     {
         var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey);
         if (format)
         {
             var sw = new StringWriter();
             var pWrt = new PemWriter(sw);
             var pkcs8 = new Pkcs8Generator(akp);
             pWrt.WriteObject(pkcs8);
             pWrt.Writer.Close();
             return sw.ToString();
         }
         else
         {
             var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp);
             return Base64.ToBase64String(privateKeyInfo.GetEncoded());
         }
     }
    
    /// <summary>
    /// Pkcs8>>Pkcs1
    /// </summary>
    /// <param name="privateKey">Pkcs8私钥</param>
    /// <param name="format">是否转PEM格式</param>
    /// <returns></returns>
    public static string PrivateKeyPkcs8ToPkcs1(string privateKey, bool format = false)
    {
        var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey);
        if (format)
        {
            var sw = new StringWriter();
            var pWrt = new PemWriter(sw);
            pWrt.WriteObject(akp);
            pWrt.Writer.Close();
            return sw.ToString();
        }
        else
        {
            var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp);
            return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
        }
    }
    

    PKCS1与PKCS8私钥中提取公钥

    /// <summary>
    /// 从Pkcs1私钥中提取公钥
    /// </summary>
    /// <param name="privateKey">Pkcs1私钥</param>
    /// <returns></returns>
    public static string GetPublicKeyFromPrivateKeyPkcs1(string privateKey)
    {
        var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));
    
        var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent);
    
        var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient);
    
        var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter);
        var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
    
        return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());
    }
    
    /// <summary>
    /// 从Pkcs8私钥中提取公钥
    /// </summary>
    /// <param name="privateKey">Pkcs8私钥</param>
    /// <returns></returns>
    public static string GetPublicKeyFromPrivateKeyPkcs8(string privateKey)
    {
        var privateKeyInfo = PrivateKeyInfo.GetInstance(Asn1Object.FromByteArray(Base64.Decode(privateKey)));
        privateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
    
        var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));
    
        var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent);
    
        var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient);
    
        var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter);
        var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
    
        return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());
    }
    

    PEM操作

    PEM格式密钥读取

    public static string ReadPkcs1PrivateKey(string text)
    {
        if (!text.StartsWith("-----BEGIN RSA PRIVATE KEY-----"))
        {
            return text;
        }
    
        using (var reader = new StringReader(text))
        {
            var pr = new PemReader(reader);
            var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair;
            pr.Reader.Close();
    
            var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair?.Private);
            return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
        }
    }
    
    public static string ReadPkcs8PrivateKey(string text)
    {
        if (!text.StartsWith("-----BEGIN PRIVATE KEY-----"))
        {
            return text;
        }
    
        using (var reader = new StringReader(text))
        {
            var pr = new PemReader(reader);
            var akp = pr.ReadObject() as AsymmetricKeyParameter; ;
            pr.Reader.Close();
            return Base64.ToBase64String(PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp).GetEncoded());
        }
    }
    
     public static string ReadPublicKey(string text)
     {
        if (!text.StartsWith("-----BEGIN PUBLIC KEY-----"))
        {
            return text;
        }
        using (var reader = new StringReader(text))
        {
            var pr = new PemReader(reader);
            var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair;
            pr.Reader.Close();
    
            var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair?.Public);
            returnBase64.ToBase64String(subjectPublicKeyIno.GetEncoded());
        }
     }
    

    PEM格式密钥写入

    public static string WritePkcs1PrivateKey(string privateKey)
    {
        if (privateKey.StartsWith("-----BEGIN RSA PRIVATE KEY-----"))
        {
            return privateKey;
        }
    
        var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey);
        using (var sw = new StringWriter())
        {
            var pWrt = new PemWriter(sw);
            pWrt.WriteObject(akp);
            pWrt.Writer.Close();
            return sw.ToString();
        }
    }
    
    public static string WritePkcs8PrivateKey(string privateKey)
    {
        if (privateKey.StartsWith("-----BEGIN PRIVATE KEY-----"))
        {
            return privateKey;
        }
    
        var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey);
    
        using (var sw = new StringWriter())
        {
            var pWrt = new PemWriter(sw);
            var pkcs8 = new Pkcs8Generator(akp);
            pWrt.WriteObject(pkcs8);
            pWrt.Writer.Close();
            return sw.ToString();
        }
    }
    
    public static string WritePublicKey(string publicKey)
    {
        if (publicKey.StartsWith("-----BEGIN PUBLIC KEY-----"))
        {
            return publicKey;
        }
        var akp = RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(publicKey);
        using (var sw = new StringWriter())
        {
            var pWrt = new PemWriter(sw);
            pWrt.WriteObject(akp);
            pWrt.Writer.Close();
            return sw.ToString();
        }
    }
    

    RSA加解密

    获取非对称秘钥参数(AsymmetricKeyParameter)

    /// <summary>
    /// -----BEGIN RSA PRIVATE KEY-----
    /// ...
    /// -----END RSA PRIVATE KEY-----
    /// </summary>
    /// <param name="privateKey">Pkcs1格式私钥</param>
    /// <returns></returns>
    public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPrivateKey(string privateKey)
    {
        if (string.IsNullOrEmpty(privateKey))
        {
            throw new ArgumentNullException(nameof(privateKey));
        }
    
        var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));
        return new RsaPrivateCrtKeyParameters(instance.Modulus, instance.PublicExponent, instance.PrivateExponent,instance.Prime1, instance.Prime2, instance.Exponent1, instance.Exponent2, instance.Coefficient);
    }
    
    /// <summary>
    /// -----BEGIN PRIVATE KEY-----
    /// ...
    /// -----END PRIVATE KEY-----
    /// </summary>
    /// <param name="privateKey">Pkcs8格式私钥</param>
    /// <returns></returns>
    public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormAsn1PrivateKey(string privateKey)
    {
        return PrivateKeyFactory.CreateKey(Base64.Decode(privateKey));
    }
    
    /// <summary>
    /// -----BEGIN PUBLIC KEY-----
    /// ...
    /// -----END PUBLIC KEY-----
    /// </summary>
    /// <param name="publicKey">公钥</param>
    /// <returns></returns>
    public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPublicKey(string publicKey)
    {
        if (string.IsNullOrEmpty(publicKey))
        {
            throw new ArgumentNullException(nameof(publicKey));
        }
    
        return PublicKeyFactory.CreateKey(Base64.Decode(publicKey));
    }
    

    RSA加解与解密

     /// <summary>
     /// RSA加密
     /// </summary>
     /// <param name="data">未加密数据字节数组</param>
     /// <param name="parameters">非对称密钥参数</param>
     /// <param name="algorithm">密文算法</param>
     /// <returns>已加密数据字节数组</returns>
     public static byte[] Encrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm)
     {
         if (data == null)
         {
             throw new ArgumentNullException(nameof(data));
         }
         if (parameters == null)
         {
             throw new ArgumentNullException(nameof(parameters));
         }
         if (string.IsNullOrEmpty(algorithm))
         {
             throw new ArgumentNullException(nameof(algorithm));
         }
    
         var bufferedCipher = CipherUtilities.GetCipher(algorithm);
         bufferedCipher.Init(true, parameters);
         return bufferedCipher.DoFinal(data);
     }
    
     /// <summary>
     /// RSA解密
     /// </summary>
     /// <param name="data">已加密数据字节数组</param>
     /// <param name="parameters">非对称密钥参数</param>
     /// <param name="algorithm">密文算法</param>
     /// <returns>未加密数据字节数组</returns>
     public static byte[] Decrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm)
    {
        if (data == null)
        {
            throw new ArgumentNullException(nameof(data));
        }
        if (parameters == null)
        {
            throw new ArgumentNullException(nameof(parameters));
        }
        if (string.IsNullOrEmpty(algorithm))
        {
            throw new ArgumentNullException(nameof(algorithm));
        }
        var bufferedCipher = CipherUtilities.GetCipher(algorithm);
        bufferedCipher.Init(false, parameters);
        return bufferedCipher.DoFinal(data);
    }
    
     /// <summary>
     /// RSA加密——Base64
     /// </summary>
     /// <param name="data">未加密字符串</param>
     /// <param name="parameters">非对称密钥参数</param>
     /// <param name="algorithm">密文算法</param>
     /// <returns>已加密Base64字符串</returns>
     public static string EncryptToBase64(string data, AsymmetricKeyParameter parameters, string algorithm)
     {
         return Base64.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm));
     }
    
     /// <summary>
     /// RSA解密——Base64
     /// </summary>
     /// <param name="data">已加密Base64字符串</param>
     /// <param name="parameters">非对称密钥参数</param>
     /// <param name="algorithm">密文算法</param>
     /// <returns>未加密字符串</returns>
     public static string DecryptFromBase64(string data, AsymmetricKeyParameter parameters, string algorithm)
     {
         return Encoding.UTF8.GetString(Decrypt(Base64.Decode(data), parameters, algorithm));
     }
    
    /// <summary>
    /// RSA加密——十六进制
    /// </summary>
    /// <param name="data">未加密字符串</param>
    /// <param name="parameters">非对称密钥参数</param>
    /// <param name="algorithm">密文算法</param>
    /// <returns>已加密十六进制字符串</returns>
    public static string EncryptToHex(string data, AsymmetricKeyParameter parameters, string algorithm)
    {
        return Hex.ToHexString(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm));
    }
    
    ///  <summary>
    /// RSA解密——十六进制
    /// </summary>
    /// <param name="data">已加密十六进制字符串</param>
    /// <param name="parameters">非对称密钥参数</param>
    /// <param name="algorithm">密文算法</param>
    /// <returns>未加密字符串</returns>
    public static string DecryptFromHex(string data, AsymmetricKeyParameter parameters, string algorithm)
    {
        return Encoding.UTF8.GetString(Decrypt(Hex.Decode(data), parameters, algorithm));
    }
    

    RSA密文算法

    public const string RSA_NONE_NoPadding = "RSA/NONE/NoPadding";
    public const string RSA_NONE_PKCS1Padding = "RSA/NONE/PKCS1Padding";
    public const string RSA_NONE_OAEPPadding = "RSA/NONE/OAEPPadding";
    public const string RSA_NONE_OAEPWithSHA1AndMGF1Padding = "RSA/NONE/OAEPWithSHA1AndMGF1Padding";
    public const string RSA_NONE_OAEPWithSHA224AndMGF1Padding = "RSA/NONE/OAEPWithSHA224AndMGF1Padding";
    public const string RSA_NONE_OAEPWithSHA256AndMGF1Padding = "RSA/NONE/OAEPWithSHA256AndMGF1Padding";
    public const string RSA_NONE_OAEPWithSHA384AndMGF1Padding = "RSA/NONE/OAEPWithSHA384AndMGF1Padding";
    public const string RSA_NONE_OAEPWithMD5AndMGF1Padding = "RSA/NONE/OAEPWithMD5AndMGF1Padding";
    
    public const string RSA_ECB_NoPadding = "RSA/ECB/NoPadding";
    public const string RSA_ECB_PKCS1Padding = "RSA/ECB/PKCS1Padding";
    public const string RSA_ECB_OAEPPadding = "RSA/ECB/OAEPPadding";
    public const string RSA_ECB_OAEPWithSHA1AndMGF1Padding = "RSA/ECB/OAEPWithSHA1AndMGF1Padding";
    public const string RSA_ECB_OAEPWithSHA224AndMGF1Padding = "RSA/ECB/OAEPWithSHA224AndMGF1Padding";
    public const string RSA_ECB_OAEPWithSHA256AndMGF1Padding = "RSA/ECB/OAEPWithSHA256AndMGF1Padding";
    public const string RSA_ECB_OAEPWithSHA384AndMGF1Padding = "RSA/ECB/OAEPWithSHA384AndMGF1Padding";
    public const string RSA_ECB_OAEPWithMD5AndMGF1Padding = "RSA/ECB/OAEPWithMD5AndMGF1Padding";
    
    ......
    
    

    编码算法

    大家要明白,不管是对称算法还是非对称算法,其输入与输出均是字节数组,通常我们要结合编码算法对加密之后或解密之前的数据,进行编码操作。

    BouncyCastle提供的Base64编码算法

    namespace Org.BouncyCastle.Utilities.Encoders
    {
        public sealed class Base64
        {
            //
            public static byte[] Decode(byte[] data);
            //
            public static byte[] Decode(string data);
            //
            public static int Decode(string data, Stream outStream);
            //
            public static byte[] Encode(byte[] data);
            //
            public static byte[] Encode(byte[] data, int off, int length);
            //
            public static int Encode(byte[] data, Stream outStream);
            //
            public static int Encode(byte[] data, int off, int length, Stream outStream);
            public static string ToBase64String(byte[] data);
            public static string ToBase64String(byte[] data, int off, int length);
        }
    }
    

    BouncyCastle提供的Hex十六进制编码算法

    namespace Org.BouncyCastle.Utilities.Encoders
    {
        //
        // 摘要:
        //     Class to decode and encode Hex.
        public sealed class Hex
        {
            //
            public static byte[] Decode(byte[] data);
            //
            public static byte[] Decode(string data);
            //
            public static int Decode(string data, Stream outStream);
            //
            public static byte[] Encode(byte[] data);
            //
            public static byte[] Encode(byte[] data, int off, int length);
            //
            public static int Encode(byte[] data, Stream outStream);
            //
            public static int Encode(byte[] data, int off, int length, Stream outStream);
            public static string ToHexString(byte[] data);
            public static string ToHexString(byte[] data, int off, int length);
        }
    }
    

    RSA加解密示例

    private static void RSA_ECB_PKCS1Padding()
     {
         var data = "hello rsa";
    
         Console.WriteLine($"加密原文:{data}");
    
         // rsa pkcs8 private key encrypt
         //algorithm  rsa/ecb/pkcs1padding
         var pkcs8data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(pkcs8_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding);
    
         Console.WriteLine("密钥格式:pkcs8,密文算法:rsa/ecb/pkcs1padding,加密结果");
         Console.WriteLine(pkcs8data);
    
         //rsa pkcs1 private key encrypt
         //algorithm  rsa/ecb/pkcs1padding
         var pkcs1data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(pkcs1_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding);
    
         Console.WriteLine($"密钥格式:pkcs1,密文算法:rsa/ecb/pkcs1padding");
         Console.WriteLine(pkcs1data);
    
         Console.WriteLine($"加密结果比对是否一致:{pkcs8data.Equals(pkcs1data)}");
    
         var _1024_public_key = RSAKeyConverter.GetPublicKeyFromPrivateKeyPkcs1(pkcs1_1024_private_key);
    
         Console.WriteLine($"从pkcs1私钥中提取公钥:");
         Console.WriteLine(_1024_public_key);
    
         Console.WriteLine("使用公钥解密数据:");
         //rsa public key decrypt
         //algorithm  rsa/ecb/pkcs1padding
         Console.WriteLine(RSA.DecryptFromBase64(pkcs1data, RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(_1024_public_key),CipherAlgorithms.RSA_ECB_PKCS1Padding));
    
         Console.WriteLine();
     }
    

    image

    下期预告

    下一篇将介绍哈希算法(HMACSHA1、HMACSHA256、SHA1、SHA1WithRSA、SHA256、SHA256WithRSA),敬请期待...

  • 相关阅读:
    vue中router与route的区别
    网络编程-20200427
    JSP-20200428
    Servelet-1
    多线程-20200422-高级应用
    20200415_多线程——重点是应用场景
    2020-04-13注解和反射
    HIVE
    流的关联关系
    排序算法整理——坑+后期的数据结构部分
  • 原文地址:https://www.cnblogs.com/fulu/p/13100471.html
Copyright © 2011-2022 走看看