常见加密算法简析

1、对称加密算法(AES、DES、3DES)

对称加密算法是指加密和解密采用相同的密钥,是可逆的(即可解密)。

AES加密算法是密码学中的高级加密标准,采用的是对称分组密码体制,密钥长度的最少支持为128。AES加密算法是美国联邦政府采用的区块加密标准,这个标准用来替代原先的DES,已经被多方分析且广为全世界使用。

AES数学原理详解:https://www.cnblogs.com/block2016/p/5596676.html

优点:加密速度快

缺点:密钥的传递和保存是一个问题,参与加密和解密的双方使用的密钥是一样的,这样密钥就很容易泄露。

2、非对称加密算法(RSA、DSA、ECC)

非对称加密算法是指加密和解密采用不同的密钥(公钥和私钥),因此非对称加密也叫公钥加密,是可逆的(即可解密)。公钥密码体制根据其所依据的难题一般分为三类:大素数分解问题类、离散对数问题类、椭圆曲线类。

RSA加密算法是基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解极其困难,因此可以将乘积公开作为加密密钥。虽然RSA的安全性一直未能得到理论上的证明,但它经历了各种攻击至今未被完全攻破。 

优点:加密和解密的密钥不一致,公钥是可以公开的,只需保证私钥不被泄露即可,这样就密钥的传递变的简单很多,从而降低了被破解的几率。

缺点:加密速度慢

RSA加密算法既可以用来做数据加密,也可以用来数字签名。

--数据加密过程:发送者用公钥加密,接收者用私钥解密(只有拥有私钥的接收者才能解读加密的内容)

--数字签名过程:甲方用私钥加密,乙方用公钥解密(乙方解密成功说明就是甲方加的密,甲方就不可以抵赖)

详细数学原理见 【来龙去脉系列】RSA算法原理

ECC加密算法是基于椭圆曲线上离散对数计算问题(ECDLP)的ECC算法。ECC算法的数学理论非常深奥和复杂,在工程应用中比较难于实现,但它的单位安全强度相对较高。

用国际上公认的对于ECC算法最有效的攻击方法--Pollard rho方法去破译和攻击ECC算法,它的破译或求解难度基本上是指数级的。正是由于RSA算法ECC算法这一明显不同,使得ECC算法的单位安全强度高于RSA算法,也就是说,要达到同样的安全强度,ECC算法所需的密钥长度远比RSA算法低。有研究表示160位的椭圆密钥与1024位的RSA密钥安全性相同。在私钥的加密解密速度上,ECC算法比RSA、DSA速度更快。存储空间占用更小。

扩展阅读:

ECDH and ECDSA   

How to encrypt data using Elliptic Curve Algorithm in C#

ECC Examples for C#

3、线性散列算法算法(MD5、SHA1、HMAC)

MD5全称是Message-Digest Algorithm 5(信息摘要算法5),单向的算法不可逆(被MD5加密的数据不能被解密)。MD5加密后的数据长度要比加密数据小的多,且长度固定,且加密后的串是唯一的。

适用场景:常用在不可还原的密码存储、信息完整性校验等。

信息完整性校验:典型的应用是对一段信息产生信息摘要,以防止被篡改。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,这就是所谓的数字签名应用。

SHA-1 与 MD5 的比较

SHA-1摘要比MD5摘要长32 位,所以SHA-1对强行攻击有更大的强度,比MD5更安全。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2^128数量级的操作,而对SHA-1则是2^160数量级的操作。

在相同的硬件上,SHA-1 的运行速度比 MD5 慢。

4、混合加密

由于以上加密算法都有各自的缺点(RSA加密速度慢、AES密钥存储问题、MD5加密不可逆),因此实际应用时常将几种加密算法混合使用。

例如:RSA+AES:

采用RSA加密AES的密钥,采用AES对数据进行加密,这样集成了两种加密算法的优点,既保证了数据加密的速度,又实现了安全方便的密钥管理。

那么,采用多少位的密钥合适呢?一般来讲密钥长度越长,安全性越高,但是加密速度越慢。所以密钥长度也要合理的选择,一般RSA建议采用1024位的数字,AES建议采用128位即可。

5、Base64

严格意义讲,Base64并不能算是一种加密算法,而是一种编码格式,是网络上最常见的用于传输8bid字节代码的编码方式之一。

Base64编码可用于在HTTP环境下传递较长的标识信息,Base编码不仅不仅比较简单,同时也据有不可读性(编码的数据不会被肉眼直接看到)。

 

C#实现:

using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace EnDeCode1
{
    /// <summary>
    /// 加密解密工具类
    /// 作者博客:https://www.cnblogs.com/tuyile006/
    /// </summary>
    public class EncodeHelper
    {
        #region MD5
        /// <summary>
        /// MD5哈希加密
        /// </summary>
        /// <param name="scr">原始string数据</param>
        /// <returns>加密后的数据</returns>
        public static string MD5(string scr)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] palindata = Encoding.Default.GetBytes(scr);//将要加密的字符串转换为字节数组
            byte[] encryptdata = md5.ComputeHash(palindata);//将字符串加密后也转换为字符数组
            return Convert.ToBase64String(encryptdata);//将加密后的字节数组转换为加密字符串
        }
        #endregion

        #region SHA1
        /// <summary>
        /// SHA1哈希加密
        /// </summary>
        /// <param name="scr">原始string数据</param>
        /// <returns>加密后的数据</returns>
        public static string SHA1(string scr)
        {
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] palindata = Encoding.Default.GetBytes(scr);//将要加密的字符串转换为字节数组
            byte[] encryptdata = sha1.ComputeHash(palindata);//将字符串加密后也转换为字符数组
            return Convert.ToBase64String(encryptdata);//将加密后的字节数组转换为加密字符串
        }
        #endregion

        #region RSA
        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="scr">原始string数据</param>
        /// <returns></returns>
        public static string RSA(string scr)
        {
            CspParameters csp = new CspParameters(); //密钥容器知识参见https://docs.microsoft.com/zh-cn/dotnet/standard/security/how-to-store-asymmetric-keys-in-a-key-container
                                                     //在Web中配置参见https://docs.microsoft.com/zh-cn/previous-versions/aspnet/yxw286t2%28v%3dvs.100%29
            csp.KeyContainerName = "tuyile006.cnblogs.com";//密匙容器的名称,保持加密解密一致才能解密成功
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp))
            {
                byte[] plaindata = Encoding.Default.GetBytes(scr);//将要加密的字符串转换为字节数组
                byte[] encryptdata = rsa.Encrypt(plaindata, false);//将加密后的字节数据转换为新的加密字节数组
                return Convert.ToBase64String(encryptdata);//将加密后的字节数组转换为字符串
            }
        }
        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="scr">密文</param>
        /// <returns></returns>
        public static string RSADecrypt(string scr)
        {
            try
            {
                CspParameters csp = new CspParameters();
                csp.KeyContainerName = "tuyile006.cnblogs.com";//密匙容器的名称,保持加密解密一致才能解密成功
                using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp))
                {
                    byte[] bytes = Convert.FromBase64String(scr); //加密时用了Base64,则解密时对应的也要用Base64解码
                    byte[] DecryptBytes =  rsa.Decrypt(bytes, false);
                    return Encoding.Default.GetString(DecryptBytes);
                }
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// 返回RSA公匙
        /// </summary>
        /// <returns></returns>
        public static string GetRSAPublicKey()
        {
            CspParameters csp = new CspParameters();
            csp.KeyContainerName = "tuyile006.cnblogs.com";//密匙容器的名称,保持加密解密一致才能解密成功
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp))
            {
                return rsa.ToXmlString(false);
            }
        }

        #endregion

        #region DES
        const string DesIV_64 = "xiaoy><@";//定义默认加密密钥 8个字节 
        /// <summary>
        /// 按指定键值进行DES加密
        /// </summary>
        /// <param name="strContent">要加密字符</param>
        /// <param name="strKey">自定义键值 ASCII编码  必须大于或等于8个字符</param>
        /// <returns></returns>
        public static string DES(string strContent, string strKey)
        {
            if (string.IsNullOrEmpty(strContent)) return string.Empty;
            if (strKey.Length > 8) strKey = strKey.Substring(0, 8);
            
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
            byte[] byKey = Encoding.ASCII.GetBytes(strKey);
            byte[] byIV = Encoding.ASCII.GetBytes(DesIV_64);
            
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cst = new CryptoStream(ms, cryptoProvider.CreateEncryptor(byKey, byIV), CryptoStreamMode.Write))
                {
                    using (StreamWriter sw = new StreamWriter(cst))
                    {
                        sw.Write(strContent);
                        sw.Flush();
                        cst.FlushFinalBlock();
                        sw.Flush();
                        return Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length);
                    }
                }
            }
        }

        /// <summary>
        /// 按指定键值进行DES解密
        /// </summary>
        /// <param name="strContent">要解密字符</param>
        /// <param name="strKey">加密时使用的键值 ASCII编码 必须大于或等于8个字符</param>
        /// <returns></returns>
        public static string DESDecrypt(string strContent, string strKey)
        {
            if (string.IsNullOrEmpty(strContent)) return string.Empty;
            if (strKey.Length > 8) strKey = strKey.Substring(0, 8); 
            byte[] byKey = Encoding.ASCII.GetBytes(strKey);
            byte[] byIV = Encoding.ASCII.GetBytes(DesIV_64);

            byte[] byEnc;
            try
            {
                byEnc = Convert.FromBase64String(strContent);
                using (DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider())
                {
                    using (MemoryStream ms = new MemoryStream(byEnc))
                    {
                        using (CryptoStream cst = new CryptoStream(ms, cryptoProvider.CreateDecryptor(byKey, byIV), CryptoStreamMode.Read))
                        {
                            StreamReader sr = new StreamReader(cst);
                            return sr.ReadToEnd();
                        }
                    }
                }
            }
            catch
            {
                return string.Empty;
            }
            
        }
        #endregion

        #region AES
        const string AesIV_128 = "xiaoy设计.";//定义默认加密密钥 16个字节 Unicode编码为8个英文或汉字
        /// <summary>
        /// 按指定键值进行AES加密
        /// </summary>
        /// <param name="plainText">要解密字符</param>
        /// <param name="strKey">加密时使用的键值 Unicode编码 必须大于或等于8个英文或汉字</param>
        /// <returns></returns>
        public static string AES(string strContent, string strKey)
        {
            if (string.IsNullOrEmpty(strContent)) return string.Empty;
            if (strKey.Length > 8) strKey = strKey.Substring(0, 8);

            using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
            {
                aesAlg.Key = Encoding.Unicode.GetBytes(strKey);
                aesAlg.IV = Encoding.Unicode.GetBytes(AesIV_128);
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(strContent);
                        }
                        return Convert.ToBase64String(msEncrypt.ToArray()); //返回Base64密文方便传输
                    }
                }
            }
        }
        /// <summary>
        /// 按指定键值进行AES解密
        /// </summary>
        /// <param name="strContent">要解密字符</param>
        /// <param name="strKey">加密时使用的键值 Unicode编码 必须大于或等于8个英文或汉字</param>
        /// <returns></returns>
        public static string AESDecrypt(string strContent, string strKey)
        {
            if (string.IsNullOrEmpty(strContent)) return string.Empty;
            if (strKey.Length > 8) strKey = strKey.Substring(0, 8);
            //与加密时Base64对应
            byte[] byEnc;
            try
            {
                byEnc = Convert.FromBase64String(strContent);
                //解密
                using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
                {
                    aesAlg.Key = Encoding.Unicode.GetBytes(strKey);
                    aesAlg.IV = Encoding.Unicode.GetBytes(AesIV_128);

                    // Create a decryptor to perform the stream transform.
                    ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                    // Create the streams used for decryption.
                    using (MemoryStream msDecrypt = new MemoryStream(byEnc))
                    {
                        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                            {
                                return srDecrypt.ReadToEnd();
                            }
                        }
                    }
                }
            }
            catch
            {
                return string.Empty;
            }
        }
        #endregion

        #region ECC

        /// <summary>
        /// 利用ecc生成key
        /// 假设从A-->B进行信息发送
        /// </summary>
        /// <param name="AKeyName">A的公钥名称 自身</param>
        /// <param name="BKey">B的公钥</param>
        /// <returns> 生成A端用于交互信息的密钥,可以用于AES加密的密钥</returns>
        public static string ECC_EncodeKey(string AKeyName,string BKey)
        {
            byte[] BKeybyte = Convert.FromBase64String(BKey);
            using (ECDiffieHellmanCng AClient = new ECDiffieHellmanCng(CngKey.Open(AKeyName)))
            //using (ECDiffieHellmanCng AClient = new ECDiffieHellmanCng())
            {
                AClient.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
                AClient.HashAlgorithm = CngAlgorithm.Sha256;
                
                byte[] MsgKey = AClient.DeriveKeyMaterial(CngKey.Import(BKeybyte, CngKeyBlobFormat.EccPublicBlob));
                return Convert.ToBase64String(MsgKey);
            }
        }

        /// <summary>
        /// 获取自身的公钥
        /// </summary>
        /// <returns>Base64编码的字符串,接收端需要Base64解码再使用</returns>
        public static string ECC_GetMyPublicKey(string keyName)
        {
            if (!CngKey.Exists(keyName))
            {
                using (ECDiffieHellmanCng MyECC = new ECDiffieHellmanCng(CngKey.Create(CngAlgorithm.ECDiffieHellmanP256, keyName)))
                {
                    MyECC.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
                    MyECC.HashAlgorithm = CngAlgorithm.Sha256;
                    byte[] Keybyte = MyECC.PublicKey.ToByteArray();
                    return Convert.ToBase64String(Keybyte);

                }
            }
            else
            {
                using (ECDiffieHellmanCng MyECC = new ECDiffieHellmanCng(CngKey.Open(keyName)))
                {
                    byte[] Keybyte = MyECC.PublicKey.ToByteArray();
                    return Convert.ToBase64String(Keybyte);
                }
            }
        }
        #endregion
    }
}

这些算法已经在.net框架里面封装好了,只需要引用System.Security.Cryptography库,使用起来还是非常方便的。

使用示例:(下载Demo)

算法调用很简单:

 txtEncode.Text = EncodeHelper.MD5(txtMsg.Text);
 txtEncode.Text = EncodeHelper.RSA(txtMsg.Text);
txtMsg.Text = "解密后的文本:" + EncodeHelper.RSADecrypt(txtEncode.Text);
txtEncode.Text = EncodeHelper.AES(txtMsg.Text, "密钥可以是汉字哦");
txtMsg.Text = "解密后的文本:" + EncodeHelper.AESDecrypt(txtEncode.Text, "密钥可以是汉字哦");

稍微复杂一点的是ECC+AES混合加密,用ECC加密AES的密钥,而用AES加密要传送的信息,接收端用ECC公钥解密得到AES密钥,然后解密信息。

该过程可以参考MSDN上的例子https://docs.microsoft.com/ru-ru/dotnet/api/system.security.cryptography.ecdiffiehellmancng

string bkey, akey,akeyname="akey",bkeyname="bkey";

        private void btn_Ecc_Click(object sender, EventArgs e)
        {
            akey = EncodeHelper.ECC_GetMyPublicKey(akeyname); //A先获取自身的publickey
            bkey = EncodeHelper.ECC_GetMyPublicKey(bkeyname); //B先获取自身的publickey
            string  AClientAESKey = EncodeHelper.ECC_EncodeKey(akeyname, bkey);  //用bkey生成用于AES算法的key
            txtEncode.Text = EncodeHelper.AES(txtMsg.Text, AClientAESKey);
        }

        private void btn_eccrec_Click(object sender, EventArgs e)
        {
            string BClientAESKey = EncodeHelper.ECC_EncodeKey(bkeyname, akey);  //用akey生成用于AES算法的key
            txtMsg.Text ="解密后的文本:"+ EncodeHelper.AESDecrypt(txtEncode.Text, BClientAESKey);
        }

 

posted @ 2019-05-16 10:17 小y 阅读(...) 评论(...) 编辑 收藏