加密_加密狗 - CSDN
精华内容
参与话题
  • 数据加密技术是网络中最基本的安全技术,主要是通过对网络中传输的信息进行数据加密来保障其安全性,这是一种主动安全防御策略,用很小的代价即可为信息提供相当大的安全保护。 一、加密的基本概念  "加密",是...

        数据加密技术是网络中最基本的安全技术,主要是通过对网络中传输的信息进行数据加密来保障其安全性,这是一种主动安全防御策略,用很小的代价即可为信息提供相当大的安全保护。

    一、加密的基本概念 

    "加密",是一种限制对网络上传输数据的访问权的技术。原始数据(也称为明文,plaintext)被加密设备(硬件或软件)和密钥加密而产生的经过编码的数据称为密文(ciphertext)。将密文还原为原始明文的过程称为解密,它是加密的反向处理,但解密者必须利用相同类型的加密设备和密钥对密文进行解密。

    加密的基本功能包括:

    1. 防止不速之客查看机密的数据文件;

    2. 防止机密数据被泄露或篡改;

    3. 防止特权用户(如系统管理员)查看私人数据文件;

    4. 使入侵者不能轻易地查找一个系统的文件。

    数据加密是确保计算机网络安全的一种重要机制,虽然由于成本、技术和管理上的复杂性等原因,目前尚未在网络中普及,但数据加密的确是实现分布式系统和网络环境下数据安全的重要手段之一。

    数据加密可在网络OSI七层协议(OSI是Open System Interconnect的缩写,意为开放式系统互联。国际标准组织(国际标准化组织)制定了OSI模型。这个模型把网络通信的工作分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。)的多层上实现、所以从加密技术应用的逻辑位置看,有三种方式:

    ①链路加密:通常把网络层以下的加密叫链路加密,主要用于保护通信节点间传输的数据,加解密由置于线路上的密码设备实现。根据传递的数据的同步方式又可分为同步通信加密和异步通信加密两种,同步通信加密又包含字节同步通信加密和位同步通信加密。

    ②节点加密:是对链路加密的改进。在协议传输层上进行加密,主要是对源节点和目标节点之间传输数据进行加密保护,与链路加密类似.只是加密算法要结合在依附于节点的加密模件中,克服了链路加密在节点处易遭非法存取的缺点。

    ③端对端加密:网络层以上的加密称为端对端加密。是面向网络层主体。对应用层的数据信息进行加密,易于用软件实现,且成本低,但密钥管理问题困难,主要适合大型网络系统中信息在多个发方和收方之间传输的情况。

    二、数据加密的应用 

    1、  媒体加密:DRM

    2、  文件加密:文本加密、pdf、word

    3、  数据加密:ASP.NET(C#)中的数据加密

    4、  硬件加密:加密狗

     

    三.加密技术发展趋势 

    ①私用密钥加密技术与公开密钥加密技术相结合:鉴于两种密码体制加密的特点,在实际应用中可以采用折衷方案,即结合使用DES/IDEA和RSA,以DES为"内核",RSA为"外壳",对于网络中传输的数据可用DES或IDEA加密,而加密用的密钥则用RSA加密传送,此种方法既保证了数据安全又提高了加密和解密的速度,这也是目前加密技术发展的新方向之一。

    ②寻求新算法:跳出以常见的迭代为基础的构造思路,脱离基于某些数学问题复杂性的构造方法。如刘尊全先生提出的刘氏算法,是一种基于密钥的公开密钥体制,它采用了随机性原理构造加解密变换,并将其全部运算控制隐匿于密钥中,密钥长度可变。它是采用选取一定长度的分割来构造大的搜索空间,从而实现一次非线性变换。此种加密算法加密强度高、速度快、计算开销低。

    ③加密最终将被集成到系统和网络中,例如IPV6协议就已有了内置加密的支持,在硬件方面,Intel公司正研制一种加密协处理器。它可以集成到微机的主极上。

    四、加密技术的分类 

    加密类型可以简单地分为四种:

    1. 根本不考虑解密问题; 

    2. 私用密钥加密技术:对称式加密(Symmetric Key Encryption):对称式加密方式对加密和解密使用相同的密钥。通常,这种加密方式在应用中难以实施,因为用同一种安全方式共享密钥很难。如:RC4、RC2、DES 和 AES 系列加密算法。

    3. 公开密钥加密技术:非对称密钥加密(Asymmetric Key Encryption):非对称密钥加密使用一组公共/私人密钥系统,加密时使用一种密钥,解密时使用另一种密钥。公共密钥可以广泛的共享和透露。当需要用加密方式向服务器外部传送数据时,这种加密方式更方便。如: RSA

    4. 数字证书。(Certificate):数字证书是一种非对称密钥加密,但是,一个组织可以使用证书并通过数字签名将一组公钥和私钥与其拥有者相关联。 

    五、对称加密之DES加密与解密 

    1、  对称加密 

    对称加密,是一种比较传统的加密方式,其加密运算、解密运算使用的是同样的密钥,信息的发送者和信息的接收者在进行信息的传输与处理时,必须共同持有该密码(称为对称密码)。因此,通信双方都必须获得这把钥匙,并保持钥匙的秘密。

    单钥密码系统的安全性依赖于以下两个因素:

    第一、加密算法必须是足够强的,仅仅基于密文本身去解密信息在实践上是不可能的。

    第二、加密方法的安全性依赖于密钥的秘密性,而不是算法的秘密性,因此,我们没有必要确保算法的秘密性(事实上,现实中使用的很多单钥密码系统的算法都是公开的),但是我们一定要保证密钥的秘密性。

    DES(Data Encryption Standard)和TripleDES是对称加密的两种实现。

    DES和TripleDES基本算法一致,只是TripleDES算法提供的key位数更多,加密可靠性更高。

    DES使用的密钥key为8字节,初始向量IV也是8字节。

    TripleDES使用24字节的key,初始向量IV也是8字节。

    两种算法都是以8字节为一个块进行加密,一个数据块一个数据块的加密,一个8字节的明文加密后的密文也是8字节。如果明文长度不为8字节的整数倍,添加值为0的字节凑满8字节整数倍。所以加密后的密文长度一定为8字节的整数倍。

    2、  加密解密过程 

     

    Figure 1. DES加密解密过程

    上图是整个DES和TripleDES算法的加密解密过程,下面以TripleDES为例,结合dotnet分析加密解密的各个步骤,并给出相关实现代码。

    1、 生成key和IV

    System.Security.Cryptography. TripleDESCryptoServiceProvider类是dotnet中实现TripleDES算法的主要的类。

    TripleDESCryptoServiceProvider类只有一个构造方法TripleDESCryptoServiceProvider(),这个方法把一些属性初始化:

    KeySize(加密密钥长度,以位为单位)= 192(24字节)

    BlockSize(加密处理的数据块大小,以位为单位)= 64(8字节)

    FeedbackSize(加密数据块后返回的数据大小,以位为单位)= 64(8字节)

     

    TripleDESCryptoServiceProvider构造方法同时会初始化一组随机的key和IV。

    默认的TripleDESCryptoServiceProvider的key为24字节,IV为8字节,加密数据块为8字节。

    生成key和IV的代码很简单:

    TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
    byte[] keyArray = tDESalg.Key;
    byte[] IVArray = tDESalg.IV;

    生成的key和IV在加密过程和解密过程都要使用。

    2、 字符串明文转成某一代码页对应的编码字节流 

    待加密的数据可能有两种形式,一种是二进制的数据,本身就是一组字节流,这样的数据可以跳过这一步,直接进入加密步骤。还有一种情况是字符串数据,字符串中同样的字符使用不同的代码页会生成不同的字节码,所以从字符串到字节流的转换是需要指定使用何种编码的。在解密之后,要从字节流转换到字符串就要使用相同的代码页解码,否则就会出现乱码。

    // 待加密的字符串

    string plainTextString = "Here is some data to encrypt. 这里是一些要加密的数据。";

    // 使用utf-8编码(也可以使用其它的编码)

    Encoding sEncoding = Encoding.GetEncoding("utf-8");

    // 把字符串明文转换成utf-8编码的字节流

    byte[] plainTextArray = sEncoding.GetBytes(plainTextString);

     

    3、 加密操作 

    加密的原料是明文字节流,TripleDES算法对字节流进行加密,返回的是加密后的字节流。同时要给定加密使用的key和IV。

    // 把字符串明文转换成utf-8编码的字节流

    复制代码
    byte[] plainTextArray = sEncoding.GetBytes(plainTextString);
    public static byte[] EncryptString(byte[] plainTextArray, byte[] Key, byte[] IV)
    {
        // 建立一个MemoryStream,这里面存放加密后的数据流
        MemoryStream mStream = new MemoryStream();
        // 使用MemoryStream 和key、IV新建一个CryptoStream 对象
        CryptoStream cStream = new CryptoStream(mStream,
            new TripleDESCryptoServiceProvider().CreateEncryptor(Key, IV),
            CryptoStreamMode.Write);
        // 将加密后的字节流写入到MemoryStream
        cStream.Write(plainTextArray, 0, plainTextArray.Length);
        //把缓冲区中的最后状态更新到MemoryStream,并清除cStream的缓存区
        cStream.FlushFinalBlock();
        // 把解密后的数据流转成字节流
        byte[] ret = mStream.ToArray();
        // 关闭两个streams.
        cStream.Close();
        mStream.Close();
        return ret;
    }
    复制代码

    4、 解密操作 

    解密操作解密上面步骤生成的密文byte[],需要使用到加密步骤使用的同一组Key和IV。

    // 调用解密方法,返回已解密数据的byte[]

    复制代码
    byte[] finalPlainTextArray = DecryptTextFromMemory(Data, keyArray, IVArray);
    public static byte[] DecryptTextFromMemory(byte[] EncryptedDataArray, byte[] Key, byte[] IV)
    {
        // 建立一个MemoryStream,这里面存放加密后的数据流
        MemoryStream msDecrypt = new MemoryStream(EncryptedDataArray);
        // 使用MemoryStream 和key、IV新建一个CryptoStream 对象
        CryptoStream csDecrypt = new CryptoStream(msDecrypt,
            new TripleDESCryptoServiceProvider().CreateDecryptor(Key, IV),
            CryptoStreamMode.Read);
        // 根据密文byte[]的长度(可能比加密前的明文长),新建一个存放解密后明文的byte[]
        byte[] DecryptDataArray = new byte[EncryptedDataArray.Length];
        // 把解密后的数据读入到DecryptDataArray
        csDecrypt.Read(DecryptDataArray, 0, DecryptDataArray.Length);
        msDecrypt.Close();
        csDecrypt.Close();
        return DecryptDataArray;
    }
    复制代码

    有一点需要注意,DES加密是以数据块为单位加密的,8个字节一个数据块,如果待加密明byte[]的长度不是8字节的整数倍,算法先用值为“0”的byte补足8个字节,然后进行加密。所以加密后的密文长度一定是8的整数倍。这样的密文解密后如果补了0值的byte,则解密后这些0值的byte依然存在。比如上例中要加密的明文是:

    “Here is some data to encrypt. 这里是一些要加密的数据。”

    转成明文byte[]后是66个字节,DES算法就会补上6个0值的byte,补到72个字节。这样加密后再解密回来的密文byte[]解码后的字符串就是这样的:

    "Here is some data to encrypt. 这里是一些要加密的数据。\0\0\0\0\0\0"

    5、 从编码字节流转成字符串明文 

    // 使用前面定义的Encoding,utf-8的编码把byte[]转成字符串

    plainTextString = sEncoding.GetString(finalPlainTextArray);

    六、非对称加密之RSA加密和解密的讲解 

          RSA公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。

      RSA是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。RSA的安全性依赖于大数的因子分解,但并没有从理论上证明破译RSA的难度与大数分解难度等价。即RSA的重大缺陷是无法从理论上把握它的保密性能如何,而且密码学界多数人士倾向于因子分解不是NPC问题。

      RSA的缺点主要有:

    A)产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。

    B)分组长度太大,为保证安全性,n 至少也要 600bits以上,使运算代价很高,尤其是速度较慢,较对称密码算法慢几个数量级;且随着大数分解技术的发展,这个

    长度还在增加,不利于数据格式的标准化。目前,SET(Secure Electronic Transaction)协议中要求CA采用2048bits长的密钥,其他实体使用1024比特的密钥。C)RSA密钥长度随着保密级别提高,增加很快。下表列出了对同一安全级别所对应的密钥长度。

     

    保密级别

    对称密钥长度(bit)

    RSA密钥长度(bit)

    ECC密钥长度(bit)

    保密年限

    80

    80

    1024

    160

    2010

    112

    112

    2048

    224

    2030

    128

    128

    3072

    256

    2040

    192

    192

    7680

    384

    2080

    256

    256

    15360

    512

    2120

     这种算法1978年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。早在1973年,英国国家通信总局的数学家Clifford Cocks就发现了类似的算法。但是他的发现被列为绝密,直到1998年才公诸于世。

      RSA算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。

      RSA的算法涉及三个参数,n、e1、e2。

      其中,n是两个大质数p、q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度。

      e1和e2是一对相关的值,e1可以任意取,但要求e1与(p-1)*(q-1)互质;再选择e2,要求(e2*e1)mod((p-1)*(q-1))=1。

      (n及e1),(n及e2)就是密钥对。

      RSA加解密的算法完全相同,设A为明文,B为密文,则:A=B^e1 mod n;B=A^e2 mod n;

      e1和e2可以互换使用,即:

      A=B^e2 mod n;B=A^e1 mod n;

     

    C#代码实现

    需引用using System.Security.Cryptography;

    复制代码
    /// <summary>
            /// RSA加密
            /// </summary>
            /// <param name="publickey"></param>
            /// <param name="content"></param>
            /// <returns></returns>
            public static string RSAEncrypt(string publickey, string content)
            {
                publickey = @"<RSAKeyValue><Modulus>5m9m14XH3oqLJ8bNGw9e4rGpXpcktv9MSkHSVFVMjHbfv+SJ5v0ubqQxa5YjLN4vc49z7SVju8s0X4gZ6AzZTn06jzWOgyPRV54Q4I0DCYadWW4Ze3e+BOtwgVU1Og3qHKn8vygoj40J6U85Z/PTJu3hN1m75Zr195ju7g9v4Hk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                byte[] cipherbytes;
                rsa.FromXmlString(publickey);
                cipherbytes = rsa.Encrypt(Encoding.UTF8.GetBytes(content), false);
    
                return Convert.ToBase64String(cipherbytes);
            }
    
            /// <summary>
            /// RSA解密
            /// </summary>
            /// <param name="privatekey"></param>
            /// <param name="content"></param>
            /// <returns></returns>
            public static string RSADecrypt(string privatekey, string content)
            {
                privatekey = @"<RSAKeyValue><Modulus>5m9m14XH3oqLJ8bNGw9e4rGpXpcktv9MSkHSVFVMjHbfv+SJ5v0ubqQxa5YjLN4vc49z7SVju8s0X4gZ6AzZTn06jzWOgyPRV54Q4I0DCYadWW4Ze3e+BOtwgVU1Og3qHKn8vygoj40J6U85Z/PTJu3hN1m75Zr195ju7g9v4Hk=</Modulus><Exponent>AQAB</Exponent><P>/hf2dnK7rNfl3lbqghWcpFdu778hUpIEBixCDL5WiBtpkZdpSw90aERmHJYaW2RGvGRi6zSftLh00KHsPcNUMw==</P><Q>6Cn/jOLrPapDTEp1Fkq+uz++1Do0eeX7HYqi9rY29CqShzCeI7LEYOoSwYuAJ3xA/DuCdQENPSoJ9KFbO4Wsow==</Q><DP>ga1rHIJro8e/yhxjrKYo/nqc5ICQGhrpMNlPkD9n3CjZVPOISkWF7FzUHEzDANeJfkZhcZa21z24aG3rKo5Qnw==</DP><DQ>MNGsCB8rYlMsRZ2ek2pyQwO7h/sZT8y5ilO9wu08Dwnot/7UMiOEQfDWstY3w5XQQHnvC9WFyCfP4h4QBissyw==</DQ><InverseQ>EG02S7SADhH1EVT9DD0Z62Y0uY7gIYvxX/uq+IzKSCwB8M2G7Qv9xgZQaQlLpCaeKbux3Y59hHM+KpamGL19Kg==</InverseQ><D>vmaYHEbPAgOJvaEXQl+t8DQKFT1fudEysTy31LTyXjGu6XiltXXHUuZaa2IPyHgBz0Nd7znwsW/S44iql0Fen1kzKioEL3svANui63O3o5xdDeExVM6zOf1wUUh/oldovPweChyoAdMtUzgvCbJk1sYDJf++Nr0FeNW1RB1XG30=</D></RSAKeyValue>";
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                byte[] cipherbytes;
                rsa.FromXmlString(privatekey);
                cipherbytes = rsa.Decrypt(Convert.FromBase64String(content), false);
    
                return Encoding.UTF8.GetString(cipherbytes);
            }
    复制代码

    七、ASP.NET(C#)常用加密类调用的讲解

    1、C#常用加密解密类库代码如下: 

    复制代码
    /// <summary>
          /// MD5 加密静态方法
          /// </summary>
          /// <param name="EncryptString">待加密的密文</param>
          /// <returns>returns</returns>
          public static string MD5Encrypt(string EncryptString)
          {
              if (string.IsNullOrEmpty(EncryptString)) { throw (new Exception("密文不得为空")); }
              MD5 m_ClassMD5 = new MD5CryptoServiceProvider();
              string m_strEncrypt = "";
              try
              {
                  m_strEncrypt = BitConverter.ToString(m_ClassMD5.ComputeHash(Encoding.Default.GetBytes(EncryptString))).Replace("-", "");
              }
             catch (ArgumentException ex) { throw ex; }
             catch (CryptographicException ex) { throw ex; }
             catch (Exception ex) { throw ex; }
              finally { m_ClassMD5.Clear(); }
              return m_strEncrypt;
          }
    
          /// <summary>
          /// DES 加密(数据加密标准,速度较快,适用于加密大量数据的场合)
          /// </summary>
          /// <param name="EncryptString">待加密的密文</param>
          /// <param name="EncryptKey">加密的密钥</param>
          /// <returns>returns</returns>
          public static string DESEncrypt(string EncryptString, string EncryptKey)
          {
              if (string.IsNullOrEmpty(EncryptString)) { throw (new Exception("密文不得为空")); }
              if (string.IsNullOrEmpty(EncryptKey)) { throw (new Exception("密钥不得为空")); }
              if (EncryptKey.Length != 8) { throw (new Exception("密钥必须为8位")); }
              byte[] m_btIV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
              string m_strEncrypt = "";
             DESCryptoServiceProvider m_DESProvider = new DESCryptoServiceProvider();
              try
              {
                  byte[] m_btEncryptString = Encoding.Default.GetBytes(EncryptString);
                  MemoryStream m_stream = new MemoryStream();
                  CryptoStream m_cstream = new CryptoStream(m_stream, m_DESProvider.CreateEncryptor(Encoding.Default.GetBytes(EncryptKey), m_btIV), CryptoStreamMode.Write);
                  m_cstream.Write(m_btEncryptString, 0, m_btEncryptString.Length);
                  m_cstream.FlushFinalBlock();
                  m_strEncrypt = Convert.ToBase64String(m_stream.ToArray());
                  m_stream.Close(); m_stream.Dispose();
                  m_cstream.Close(); m_cstream.Dispose();
              }
              catch (IOException ex) { throw ex; }
              catch (CryptographicException ex) { throw ex; }
              catch (ArgumentException ex) { throw ex; }
              catch (Exception ex) { throw ex; }
              finally { m_DESProvider.Clear(); }
              return m_strEncrypt;
          }
    
          /// <summary>
          /// DES 解密(数据加密标准,速度较快,适用于加密大量数据的场合)
          /// </summary>
          /// <param name="DecryptString">待解密的密文</param>
          /// <param name="DecryptKey">解密的密钥</param>
          /// <returns>returns</returns>
          public static string DESDecrypt(string DecryptString, string DecryptKey)
          {
             if (string.IsNullOrEmpty(DecryptString)) { throw (new Exception("密文不得为空")); }
              if (string.IsNullOrEmpty(DecryptKey)) { throw (new Exception("密钥不得为空")); }
              if (DecryptKey.Length != 8) { throw (new Exception("密钥必须为8位")); }
              byte[] m_btIV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
             string m_strDecrypt = "";
             DESCryptoServiceProvider m_DESProvider = new DESCryptoServiceProvider();
             try
            {
                 byte[] m_btDecryptString = Convert.FromBase64String(DecryptString);
                 MemoryStream m_stream = new MemoryStream();
                 CryptoStream m_cstream = new CryptoStream(m_stream, m_DESProvider.CreateDecryptor(Encoding.Default.GetBytes(DecryptKey), m_btIV), CryptoStreamMode.Write);
                 m_cstream.Write(m_btDecryptString, 0, m_btDecryptString.Length);
                 m_cstream.FlushFinalBlock();
                 m_strDecrypt = Encoding.Default.GetString(m_stream.ToArray());
                 m_stream.Close(); m_stream.Dispose();
                 m_cstream.Close(); m_cstream.Dispose();
             }
             catch (IOException ex) { throw ex; }
             catch (CryptographicException ex) { throw ex; }
             catch (ArgumentException ex) { throw ex; }
             catch (Exception ex) { throw ex; }
             finally { m_DESProvider.Clear(); }
             return m_strDecrypt;
         }
         /// <summary>
         /// RC2 加密(用变长密钥对大量数据进行加密)
         /// </summary>
         /// <param name="EncryptString">待加密密文</param>
         /// <param name="EncryptKey">加密密钥</param>
         /// <returns>returns</returns>
         public static string RC2Encrypt(string EncryptString, string EncryptKey)
         {
             if (string.IsNullOrEmpty(EncryptString)) { throw (new Exception("密文不得为空")); }
             if (string.IsNullOrEmpty(EncryptKey)) { throw (new Exception("密钥不得为空")); }
             if (EncryptKey.Length < 5 || EncryptKey.Length > 16) { throw (new Exception("密钥必须为5-16位")); }
             string m_strEncrypt = "";
             byte[] m_btIV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
             RC2CryptoServiceProvider m_RC2Provider = new RC2CryptoServiceProvider();
             try
            {
                 byte[] m_btEncryptString = Encoding.Default.GetBytes(EncryptString);
                 MemoryStream m_stream = new MemoryStream();
                 CryptoStream m_cstream = new CryptoStream(m_stream, m_RC2Provider.CreateEncryptor(Encoding.Default.GetBytes(EncryptKey), m_btIV), CryptoStreamMode.Write);
                 m_cstream.Write(m_btEncryptString, 0, m_btEncryptString.Length);
                 m_cstream.FlushFinalBlock();
                 m_strEncrypt = Convert.ToBase64String(m_stream.ToArray());
                 m_stream.Close(); m_stream.Dispose();
                m_cstream.Close(); m_cstream.Dispose();
             }
             catch (IOException ex) { throw ex; }
             catch (CryptographicException ex) { throw ex; }
             catch (ArgumentException ex) { throw ex; }
             catch (Exception ex) { throw ex; }
             finally { m_RC2Provider.Clear(); }
             return m_strEncrypt;
         }
    
         /// <summary>
         /// RC2 解密(用变长密钥对大量数据进行加密)
         /// </summary>
         /// <param name="DecryptString">待解密密文</param>
         /// <param name="DecryptKey">解密密钥</param>
         /// <returns>returns</returns>
         public static string RC2Decrypt(string DecryptString, string DecryptKey)
         {
             if (string.IsNullOrEmpty(DecryptString)) { throw (new Exception("密文不得为空")); }
             if (string.IsNullOrEmpty(DecryptKey)) { throw (new Exception("密钥不得为空")); }
            if (DecryptKey.Length < 5 || DecryptKey.Length > 16) { throw (new Exception("密钥必须为5-16位")); }
             byte[] m_btIV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
             string m_strDecrypt = "";
             RC2CryptoServiceProvider m_RC2Provider = new RC2CryptoServiceProvider();
            try
            {
                byte[] m_btDecryptString = Convert.FromBase64String(DecryptString);
                 MemoryStream m_stream = new MemoryStream();
                 CryptoStream m_cstream = new CryptoStream(m_stream, m_RC2Provider.CreateDecryptor(Encoding.Default.GetBytes(DecryptKey), m_btIV), CryptoStreamMode.Write);
                 m_cstream.Write(m_btDecryptString, 0, m_btDecryptString.Length);
                 m_cstream.FlushFinalBlock();
                 m_strDecrypt = Encoding.Default.GetString(m_stream.ToArray());
                 m_stream.Close(); m_stream.Dispose();
                 m_cstream.Close(); m_cstream.Dispose();
             }
             catch (IOException ex) { throw ex; }
            catch (CryptographicException ex) { throw ex; }
             catch (ArgumentException ex) { throw ex; }
             catch (Exception ex) { throw ex; }
            finally { m_RC2Provider.Clear(); }
             return m_strDecrypt;
         }
    
         /// <summary>
         /// 3DES 加密(基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高)
         /// </summary>
         /// <param name="EncryptString">待加密密文</param>
         /// <param name="EncryptKey1">密钥一</param>
         /// <param name="EncryptKey2">密钥二</param>
        /// <param name="EncryptKey3">密钥三</param>
         /// <returns>returns</returns>
         public static string DES3Encrypt(string EncryptString, string EncryptKey1, string EncryptKey2, string EncryptKey3)
         {
             string m_strEncrypt = "";
             try
             {
                 m_strEncrypt = DESEncrypt(EncryptString, EncryptKey3);
                 m_strEncrypt = DESEncrypt(m_strEncrypt, EncryptKey2);
                m_strEncrypt = DESEncrypt(m_strEncrypt, EncryptKey1);
             }
             catch (Exception ex) { throw ex; }
             return m_strEncrypt;
         }
    
         /// <summary>
         /// 3DES 解密(基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高)
         /// </summary>
         /// <param name="DecryptString">待解密密文</param>
         /// <param name="DecryptKey1">密钥一</param>
         /// <param name="DecryptKey2">密钥二</param>
         /// <param name="DecryptKey3">密钥三</param>
         /// <returns>returns</returns>
         public static string DES3Decrypt(string DecryptString, string DecryptKey1, string DecryptKey2, string DecryptKey3)
         {
             string m_strDecrypt = "";
             try
             {
                m_strDecrypt = DESDecrypt(DecryptString, DecryptKey1);
                 m_strDecrypt = DESDecrypt(m_strDecrypt, DecryptKey2);
                 m_strDecrypt = DESDecrypt(m_strDecrypt, DecryptKey3);
             }
             catch (Exception ex) { throw ex; }
             return m_strDecrypt;
         }
    
         /// <summary>
         /// AES 加密(高级加密标准,是下一代的加密算法标准,速度快,安全级别高,目前 AES 标准的一个实现是 Rijndael 算法)
         /// </summary>
         /// <param name="EncryptString">待加密密文</param>
         /// <param name="EncryptKey">加密密钥</param>
        /// <returns></returns>
        public static string AESEncrypt(string EncryptString, string EncryptKey)
         {
            if (string.IsNullOrEmpty(EncryptString)) { throw (new Exception("密文不得为空")); }
             if (string.IsNullOrEmpty(EncryptKey)) { throw (new Exception("密钥不得为空")); }
             string m_strEncrypt = "";
             byte[] m_btIV = Convert.FromBase64String("Rkb4jvUy/ye7Cd7k89QQgQ==");
             Rijndael m_AESProvider = Rijndael.Create();
             try
             {
                 byte[] m_btEncryptString = Encoding.Default.GetBytes(EncryptString);
                 MemoryStream m_stream = new MemoryStream();
                 CryptoStream m_csstream = new CryptoStream(m_stream, m_AESProvider.CreateEncryptor(Encoding.Default.GetBytes(EncryptKey), m_btIV), CryptoStreamMode.Write);
                 m_csstream.Write(m_btEncryptString, 0, m_btEncryptString.Length); m_csstream.FlushFinalBlock();
                 m_strEncrypt = Convert.ToBase64String(m_stream.ToArray());
                 m_stream.Close(); m_stream.Dispose();
                 m_csstream.Close(); m_csstream.Dispose();
             }
             catch (IOException ex) { throw ex; }
             catch (CryptographicException ex) { throw ex; }
             catch (ArgumentException ex) { throw ex; }
             catch (Exception ex) { throw ex; }
             finally { m_AESProvider.Clear(); }
             return m_strEncrypt;
         }
    
         /// <summary>
         /// AES 解密(高级加密标准,是下一代的加密算法标准,速度快,安全级别高,目前 AES 标准的一个实现是 Rijndael 算法)
         /// </summary>
         /// <param name="DecryptString">待解密密文</param>
         /// <param name="DecryptKey">解密密钥</param>
         /// <returns></returns>
         public static string AESDecrypt(string DecryptString, string DecryptKey)
         {
             if (string.IsNullOrEmpty(DecryptString)) { throw (new Exception("密文不得为空")); }
             if (string.IsNullOrEmpty(DecryptKey)) { throw (new Exception("密钥不得为空")); }
             string m_strDecrypt = "";
             byte[] m_btIV = Convert.FromBase64String("Rkb4jvUy/ye7Cd7k89QQgQ==");
             Rijndael m_AESProvider = Rijndael.Create();
             try
             {
                 byte[] m_btDecryptString = Convert.FromBase64String(DecryptString);
                 MemoryStream m_stream = new MemoryStream();
                 CryptoStream m_csstream = new CryptoStream(m_stream, m_AESProvider.CreateDecryptor(Encoding.Default.GetBytes(DecryptKey), m_btIV), CryptoStreamMode.Write);
                 m_csstream.Write(m_btDecryptString, 0, m_btDecryptString.Length); m_csstream.FlushFinalBlock();
                 m_strDecrypt = Encoding.Default.GetString(m_stream.ToArray());
                 m_stream.Close(); m_stream.Dispose();
                 m_csstream.Close(); m_csstream.Dispose();
             }
             catch (IOException ex) { throw ex; }
             catch (CryptographicException ex) { throw ex; }
             catch (ArgumentException ex) { throw ex; }
             catch (Exception ex) { throw ex; }
             finally { m_AESProvider.Clear(); }
             return m_strDecrypt;
         }
    复制代码

    2、数据加密和解密简单代码调用如下: 

    Response.Write("<br>-----------MD5加密---------------<br>");        

    Response.Write(SDKSecurity.MD5Encrypt("仰天一笑"));
    Response.Write("<br>-----------DES加密---------------<br>");        

    Response.Write(SDKSecurity.DESEncrypt("仰天一笑", "anson-xu"));        

    Response.Write("<br>-----------DES解密---------------<br>");        

    Response.Write(SDKSecurity.DESDecrypt("l06JvJ45r/lb9iKzSXl47Q==", "anson-xu"));
    Response.Write("<br>-----------AES加密---------------<br>");    

    Response.Write(SDKSecurity.AESEncrypt("仰天一笑", "ansonxuyu"));        

    Response.Write("<br>-----------AES解密---------------<br>");        

    Response.Write(SDKSecurity.AESDecrypt("avwKL+MO8+zoLHvzk0+TBA==", "ansonxuyu"));

    3、数据加密和解密调用后运行效果图如下: 

    注:本篇为转载的文章!


    展开全文
  • 常用加密解密算法【RSA、AES、DES、MD5】介绍和使用

    万次阅读 多人点赞 2016-12-06 11:23:12
    为了防止我们的数据泄露,我们往往会对数据进行加密,特别是敏感数据,我们要求的安全性更高。下面将介绍几种常用的加密算法使用。这些算法的加密对象都是基于二进制数据,如果要加密字符串就使用统一编码(如:utf8...

    为了防止我们的数据泄露,我们往往会对数据进行加密,特别是敏感数据,我们要求的安全性更高。下面将介绍几种常用的加密算法使用。这些算法的加密对象都是基于二进制数据,如果要加密字符串就使用统一编码(如:utf8)进行编码后加密。

    1.摘要算法

    常用的摘要算法有MD5,SHA1。摘要算法是一个不可逆过程,就是无论多大数据,经过算法运算后都是生成固定长度的数据,一般结果使用16进制进行显示。
    MD5和SHA1的区别:MD5结果是128位摘要,SHa1是160位摘要。那么MD5的速度更快,而SHA1的强度更高。

    下面统一使用MD5算法进行说明,SHA1类似。
    主要用途有:验证消息完整性,安全访问认证,数据签名。

    • 消息完整性:由于每一份数据生成的MD5值不一样,因此发送数据时可以将数据和其MD5值一起发送,然后就可以用MD5验证数据是否丢失、修改。
    • 安全访问认证:这是使用了算法的不可逆性质,(就是无法从MD5值中恢复原数据)对账号登陆的密码进行MD5运算然后保存,这样可以保证除了用户之外,即使数据库管理人员都无法得知用户的密码。
    • 数字签名:这是结合非对称加密算法和CA证书的一种使用场景。

    一般破解方法:字典法,就是将常用密码生成MD5值字典,然后反向查找达到破解目的,因此建议使用强密码。

    MD5的使用—对文件进行摘要。

        //对文件进行MD5摘要
        public static String getMD5(String path){
    
            String pathName = path;
            String md5= "";
            try {
                File file = new File(pathName);
                FileInputStream ins = new FileInputStream(file);
                FileChannel ch = ins.getChannel();
                MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,file.length());       
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.update(byteBuffer);
                ins.close();
                md5 = toHexString(md.digest());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return md5;
        }
    
        //以16进制编码进行输出
        final static char hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        public static String toHexString(byte[] tmp){
            String s;
            char str[] = new char[tmp.length*2];
            int k =0;
            for (int i = 0; i < tmp.length; i++) {
                byte byte0 = tmp[i];
                str[k++] = hex[byte0>>>4&0xf];
                str[k++] = hex[byte0&0xf];
            }
            s=new String(str);
            return s;
        }
    

    SHA1的使用

    
        //对文件进行SHA1摘要
        public static String getSHA1(String path){
            String pathName = path;
            String sha1= "";
            try {
                File file = new File(pathName);
                FileInputStream ins = new FileInputStream(file);
                FileChannel ch = ins.getChannel();
                MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,file.length());       
                MessageDigest sha = MessageDigest.getInstance("SHA-1");
                sha.update(byteBuffer);
                ins.close();
                sha1 = toHexString(sha.digest());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return sha1;
        }

    可以发现我们的关键代码就是

        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        sha.update(byteBuffer);
        ins.close();
        byte[] r = sha.digest());

    只是不同的算法初始化时不同罢了。MessageDigest.getInstance("SHA-1")
    另外还可以使用

    DigestUtils.sha1(data);
    DigestUtils.md5Hex(data);

    上面实现使用的是Apache下面的一个加解密开发包commons-codec
    官方地址为:http://commons.apache.org/codec/
    官方下载地址:http://commons.apache.org/codec/download_codec.cgi

    2.对称加密算法

    对称加密算法只是为了区分非对称加密算法。其中鲜明的特点是对称加密是加密解密使用相同的密钥,而非对称加密加密和解密时使用的密钥不一样。对于大部分情况我们都使用对称加密,而对称加密的密钥交换时使用非对称加密,这有效保护密钥的安全。非对称加密加密和解密密钥不同,那么它的安全性是无疑最高的,但是它加密解密的速度很慢,不适合对大数据加密。而对称加密加密速度快,因此混合使用最好。
    常用的对称加密算法有:AES和DES.

    • DES:比较老的算法,一共有三个参数入口(原文,密钥,加密模式)。而3DES只是DES的一种模式,是以DES为基础更安全的变形,对数据进行了三次加密,也是被指定为AES的过渡算法。
    • AES:高级加密标准,新一代标准,加密速度更快,安全性更高(不用说优先选择)

    AES的使用

    AES密钥长度可以选择128位【16字节】,192位【24字节】和256位【32字节】密钥(其他不行,因此别乱设密码哦)。

        /**使用AES对字符串加密
         * @param str utf8编码的字符串
         * @param key 密钥(16字节)
         * @return 加密结果
         * @throws Exception
         */
        public static byte[] aesEncrypt(String str, String key) throws Exception { 
               if (str == null || key == null) return null; 
               Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 
               cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "AES")); 
               byte[] bytes = cipher.doFinal(str.getBytes("utf-8")); 
               return  bytes;
           } 
        /**使用AES对数据解密
         * @param bytes utf8编码的二进制数据
         * @param key 密钥(16字节)
         * @return 解密结果
         * @throws Exception
         */
           public static String aesDecrypt(byte[] bytes, String key) throws Exception { 
               if (bytes == null || key == null) return null; 
               Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 
               cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "AES")); 
               bytes = cipher.doFinal(bytes);
               return new String(bytes, "utf-8"); 
           } 

    上面代码是对字符串进行的加解密。但要注意的是AES算法的所有参数都是字节码的(包括密钥)。因此字符串字符需要转换成字节码后进行加密str.getBytes("utf-8")按照字符串的编码进行转换。另外参数:”AES/ECB/PKCS5Padding”在加密和解密时必须相同,可以直接写”AES”,这样就是使用默认模式(C#和java默认的模式不一样,C#中默认的是这种,java的默认待研究)。分别的意思为:AES是加密算法,ECB是工作模式,PKCS5Padding是填充方式。
    AES是分组加密算法,也称块加密。每一组16字节。这样明文就会分成多块。当有一块不足16字节时就会进行填充。
    一共有四种工作模式:

    • ECB 电子密码本模式:相同的明文块产生相同的密文块,容易并行运算,但也可能对明文进行攻击。
    • CBC 加密分组链接模式:一块明文加密后和上一块密文进行链接,不利于并行,但安全性比ECB好,是SSL,IPSec的标准。
    • CFB 加密反馈模式:将上一次密文与密钥运算,再加密。隐藏明文模式,不利于并行,误差传递。
    • OFB 输出反馈模式:将上一次处理过的密钥与密钥运算,再加密。隐藏明文模式,不利于并行,有可能明文攻击,误差传递。

    PKCS5Padding的填充方式是差多少字节就填数字多少;刚好每一不足16字节时,那么就会加一组填充为16.还有其他填充模式【Nopadding,ISO10126Padding】(不影响算法,加密解密时一致就行)。

    DES的使用

    和AES类似,指定为DES就行。3DES指定为”DESede”,DES密钥长度是56位,3DES加长了密钥长度,可以为112位或168位,所以安全性提高,速度降低。工作模式和填充模式标准和AES一样。

        /**使用DES对字符串加密
         * @param str utf8编码的字符串
         * @param key 密钥(56位,7字节)
         * @return 加密结果
         * @throws Exception
         */
        public static byte[] desEncrypt(String str, String key) throws Exception { 
               if (str == null || key == null) return null; 
               Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 
               cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "DES")); 
               byte[] bytes = cipher.doFinal(str.getBytes("utf-8")); 
               return  bytes;
           } 
        /**使用DES对数据解密
         * @param bytes utf8编码的二进制数据
         * @param key 密钥(16字节)
         * @return 解密结果
         * @throws Exception
         */
           public static String desDecrypt(byte[] bytes, String key) throws Exception { 
               if (bytes == null || key == null) return null; 
               Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 
               cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes("utf-8"), "DES")); 
               bytes = cipher.doFinal(bytes);
               return new String(bytes, "utf-8"); 
           } 

    3.非对称加密(RSA)

    这里主要对RSA进行介绍。
    对称加密加密解密使用的是相同的密钥,而非对称加密加密解密时使用的不同的密钥,分为公钥(public key)和私钥(private key).公钥可以公开,而私钥自己保存。它利用的是两个大质数相乘十分容易,而对其乘积进行因素分解十分困难。这样就可以将乘积作为密钥了,这个乘积为N值,根据两个大质数选择e和生成d,删掉两个大质数。这样(N,e)为公钥,(N,d)为私钥,公钥无法破解出私钥(不作详细介绍,我们不是研究算法的)。由于非对称加密的密钥生成麻烦,所以无法做到一次一密,而且其加密速度很慢,无法对大量数据加密。因此最常用的使用场景就是数字签名和密码传输,用作数字签名时使用私钥加密,公钥解密;用作加密解密时,使用公钥加密,私钥解密。

    需要注意的是RSA加密是有长度限制的,1024位密钥可以加密128字节(1024位),不满128字节的使用随机数填充,但是RSA实现中必须要加随机数(11字节以上),所以明文长度最大为117字节,然后剩下的加入随机数。这也产生了每次加密结果每一次都不一样的特点。

    如果明文长度超过限制怎么办?

    • 1.可以与对称加密混合使用,一次一密产生对称加密的密钥,然后使用此密钥进行数据对称加密,再使用RSA私钥对对称密钥加密,一起保存。解密时使用公钥解密出密钥,然后进行数据解密。
    • 2.可以分段加密。将明文按117字节分成多段,加密后再拼接起来。由于每一段密文长度都是128字节,所以解密时按照128字节分段解密。

    java的RSA密钥生成与使用

    简单使用

    下面是java中的使用方法,先是生成密钥对,然后加密,再解密。需要注意的是这个方法是不能跨语言使用的,因为里面对公钥和私钥用到的序列化是java的序列化。
    由于加密后的密文都是字节码形式的,我们要以字符串方式保存或传输的话,可以使用Base64编码。

    public class RSAUtil {
        /** 指定加密算法为RSA */
        private static String ALGORITHM = "RSA";
        /*指定加密模式和填充方式*/
        private static String ALGORITHM_MODEL = "RSA/ECB/PKCS1Padding";
        /** 指定key的大小,一般为1024,越大安全性越高 */
        private static int KEYSIZE = 1024;
        /** 指定公钥存放文件 */
        private static String PUBLIC_KEY_FILE = "PublicKey";
        /** 指定私钥存放文件 */
        private static String PRIVATE_KEY_FILE = "PrivateKey";
    
        /**
         * 生成密钥对
         */
        private static void generateKeyPair() throws Exception {
            /** RSA算法要求有一个可信任的随机数源 */
            SecureRandom sr = new SecureRandom();
            /** 为RSA算法创建一个KeyPairGenerator对象 */
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM);
            /** 利用上面的随机数据源初始化这个KeyPairGenerator对象 */
            kpg.initialize(KEYSIZE, sr);
            /** 生成密匙对 */
            KeyPair kp = kpg.generateKeyPair();
            /** 得到公钥 */
            Key publicKey = kp.getPublic();
            /** 得到私钥 */
            Key privateKey = kp.getPrivate();
            /** 用对象流将生成的密钥写入文件 */
            ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream(
                    PUBLIC_KEY_FILE));
            ObjectOutputStream oos2 = new ObjectOutputStream(new FileOutputStream(
                    PRIVATE_KEY_FILE));
            oos1.writeObject(publicKey);
            oos2.writeObject(privateKey);
            /** 清空缓存,关闭文件输出流 */
            oos1.close();
            oos2.close();
        }
    
        /**
         * 加密方法 source: 源数据
         */
        public static byte[] encrypt(String source) throws Exception {
    
            /** 将文件中的公钥对象读出 */
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                    PUBLIC_KEY_FILE));
            Key key = (Key) ois.readObject();
            ois.close();
            /** 得到Cipher对象来实现对源数据的RSA加密 */
            Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] b = source.getBytes();
            /** 执行加密操作 */
            byte[] b1 = cipher.doFinal(b);
            return b1;
        }
    
        /**
         * 解密算法 cryptograph:密文
         */
        public static String decrypt(byte[] cryptograph) throws Exception {
            /** 将文件中的私钥对象读出 */
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                    PRIVATE_KEY_FILE));
            Key key = (Key) ois.readObject();
            /** 得到Cipher对象对已用公钥加密的数据进行RSA解密 */
            Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL);
            cipher.init(Cipher.DECRYPT_MODE, key);
            /** 执行解密操作 */
            byte[] b = cipher.doFinal(cryptograph);
            return new String(b);
        }
    
        public static void main(String[] args) throws Exception {
            generateKeyPair();//生成密钥对
            String source = "Hello World!";// 要加密的字符串
            byte[] cryptograph = encrypt(source);// 生成的密文
    
            //可以将密文进行base64编码进行传输
            System.out.println(new String(Base64.encode(cryptograph)));
    
            String target = decrypt(cryptograph);// 解密密文
            System.out.println(target);
        }
    }

    RSA密钥使用Base64编码

    要灵活使用肯定不能使用java的序列化保存了,我们对上面的generateKeyPair()方法进行改写。通过密钥生成器生成公钥,私钥后,调用publicKey.getEncoded()和privateKey.getEncoded(),此时它生成的比特编码是有独特格式的(公钥是X.509,私钥是PKCS#8)可以使用publicKey.getFormat(),privateKey.getFormat();进行查看。之后对字节码进行Base64编码就行了。

    密钥生成方法

        //以base64编码密钥
        public Map<String ,String> generateKeyPair1() throws Exception{
            SecureRandom sr = new SecureRandom();
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(1024, sr);
            KeyPair kp = kpg.generateKeyPair();
            Key publicKey = kp.getPublic();
            Key privateKey = kp.getPrivate();
            byte[] pb = publicKey.getEncoded();
            String pbStr =  new String(Base64.encode(pb));
            byte[] pr = privateKey.getEncoded();
            String prStr =  new String(Base64.encode(pr));
            Map<String, String> map = new HashMap<String, String>();
            map.put("publicKey",pbStr);
            map.put("privateKey", prStr);
            return map;
        }

    恢复密钥方法,使用各自不同的编码形式恢复

        //从base64编码的公钥恢复公钥
        public PublicKey getPulbickey(String key_base64) throws Exception{
            byte[] pb = Base64.decode(key_base64).getBytes();
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pb);
            KeyFactory  keyfactory = KeyFactory.getInstance("RSA");
            return keyfactory.generatePublic(keySpec);
        }
        //从base64编码的私钥恢复私钥
        public PrivateKey getPrivatekey(String key_base64) throws Exception{
            byte[] pb = Base64.decode(key_base64).getBytes();
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pb);
            KeyFactory  keyfactory = KeyFactory.getInstance("RSA");
            return keyfactory.generatePrivate(keySpec);
        }

    加密解密方法都类似下面,PrivateKey和PublicKey是Key的子接口。

        /** 执行加密操作 */
        public static byte[] encrypt(Key key,byte[] source) throws Exception{
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] ciphertext = cipher.doFinal(source);
            return ciphertext;
        }
        /** 执行加密操作 */
        public static byte[] decrypt(Key key,byte[] ciphertext) throws Exception{
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] source = cipher.doFinal(ciphertext);
            return source;
        }

    记录RSA的密钥特征值并进行密码恢复

    所谓特征值就是RSA中公钥(N,e)私钥(N,d)的三个值:N,e,d。只要有这三个值我们就可以恢复密钥了。这是实际开发中常用的方法。首先是提取特征值,我们需要将PublicKey强制转换为RSAPublicKey.然后获取,看代码。

        //提取特征值保存,以base64编码密钥
            public static Map<String ,String> generateKeyPair2() throws Exception{
                SecureRandom sr = new SecureRandom();
                KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
                kpg.initialize(1024, sr);
                KeyPair kp = kpg.generateKeyPair();
                Key publicKey = kp.getPublic();
                Key privateKey = kp.getPrivate();
                RSAPublicKey rpk = (RSAPublicKey)publicKey;
                RSAPrivateKey rpr= (RSAPrivateKey)privateKey;
                //三个特征值都是BigInteger类型。
                BigInteger N = rpk.getModulus();//N值
                BigInteger e = rpk.getPublicExponent();//e值
                BigInteger d  = rpr.getPrivateExponent();//d值
                Map<String, String> map = new HashMap<String, String>();
                //将BigInteger转为byte[],然后以base64保存
                map.put("N",new String(Base64.decode(N.toByteArray())));
                map.put("e", new String(Base64.decode(e.toByteArray())));
                map.put("d", new String(Base64.decode(d.toByteArray())));
                return map;
            }

    利用三个特征值就可以非常容易恢复密钥了。

        //从base64编码的特征值(N,e)恢复公钥
            public static PublicKey getPulbickey(String N_Str,String e_Str) throws Exception{
                BigInteger N = new BigInteger(1, Base64.decode(N_Str.getBytes()));
                BigInteger e = new BigInteger(1, Base64.decode(e_Str.getBytes()));
                KeyFactory kf = KeyFactory.getInstance("RSA");
                RSAPublicKeySpec ps = new RSAPublicKeySpec(N, e);
                PublicKey pkey = kf.generatePublic(ps);
                return pkey;
            }
        //从base64编码的特征值(N,d)恢复私钥
        public static PrivateKey getPrivatekey(String N_Str,String d_Str) throws Exception{
            BigInteger N = new BigInteger(1, Base64.decode(N_Str.getBytes()));
            BigInteger d = new BigInteger(1, Base64.decode(d_Str.getBytes()));
            KeyFactory kf = KeyFactory.getInstance("RSA");
            RSAPrivateKeySpec ps = new RSAPrivateKeySpec(N, d);
            PrivateKey pkey = kf.generatePrivate(ps);
            return pkey;
        }

    C#生成的密钥java中使用–记录特征值的例子

    C#生成的公钥是保存在xml文件中的,使用的是Base64编码,因此我们先解析出密钥对象,然后再使用公钥加密,而让C#端服务器进行解密。Modulus就是N值,Exponent就是e值,然后组成(N,e)公钥。
    C#的密钥形式如:

    <RSAKeyValue>
    <Modulus>7gFGAUTUBiSi8j+oZ4JY4NUNCfdGIxFLhKE0c4SbiHvNAiD7rxWnmuqXK4nVzOyjJsmCViA1aRN3+Tf5xMqxtjjCKWNRWAp5LMp2AfL3DrDcWV/ZjwPIUO52yEa+q2PyJ0OMgRxBA80WWBzv+EJm7/rq8wP9gpVI+HY0ACH8Kmk=
    </Modulus>
    <Exponent>AQAB</Exponent>
    </RSAKeyValue>
    //从xml中获取公钥
        public static PublicKey getPublicKey(String xmlkey) throws Exception {
            Document doc = XmlUtil.parseXml(xmlkey);
            Node node = doc.getChildNodes().item(0);
            NodeList list = node.getChildNodes();
            String e = null, m = null;
            for (int i = 0; i < list.getLength(); i++) {
                String nodename = list.item(i).getNodeName();
                String value = list.item(i).getTextContent();
                if (nodename.equals("Modulus")) {
                    e = value;
                } else if (nodename.equals("Exponent")) {
                    m = value;
                }
            }
            BigInteger b1 = new BigInteger(1, Base64.decode(e.getBytes()));
            BigInteger b2 = new BigInteger(1, Base64.decode(m.getBytes()));
            System.out.println(b1 + "\n" + b2);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            RSAPublicKeySpec ps = new RSAPublicKeySpec(b1, b2);
            PublicKey pkey = kf.generatePublic(ps);
            return pkey;
        }
    
        //RSA加密
        public static byte[] encrypt(byte[] data,PublicKey publickey) {
            if (publickey == null || data == null) {
                return null;
            }
            try {
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(Cipher.ENCRYPT_MODE, publickey);
                return cipher.doFinal(data);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
        //字符串转Document
        public static Document parseXml(String str) throws ParserConfigurationException, SAXException, IOException{
            StringReader reader = new StringReader(str);
            InputSource source = new InputSource(reader);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            return builder.parse(source);
        }

    4.编码的使用

    常见的编码有Base64,HEX和对URL的编码。这都是为了实际需要才进行的编码。HEX是编码成16进制字符,MD5一般就是以HEX进行编码,这不说了。

    Base64

    Base64一开始是为了解决邮件中不能传文件和图片问题而使用的,将无法阅读的二进制码转化成字符形式,字符为(A-Za-z0-9+/)。它的原理是将3个8位字节(24位)转化为4个6位字节(24位),之后在6位的前面补两个0,形成8位一个字节形式,如果剩下的不足3字节,则用0填充,输出字符使用”=”,所以编码后文本可能出现1个或2个’=’.这样就将原本3个字节变成了4个字节,那就是64种编码了。当然,除了对二进制数据编码,还可以对字符串编码来隐藏明文,让别人不那么容易看懂。
    由于jdk中的base64是不开发使用了 ,所有需要下载到网上下载Base64包,我使用的是 javaBase64-1.2.jar,另外android sdk中是带有base64的,位置是android.util.Base64

    /*这里使用的是android.util.base64*/
    byte[] input = "hello world".getBytes("utf8");
    //编码    
    byte[] encodeData = Base64.encode(input , 0);
    //解码        
    byte[] result = Base64.decode(encodeData , 0);

    URL的编码

    url一般使用的都是英文、数字和某些符号,而对于特殊符号,中文等这些是不允许使用的。因此我们要在url请求中加入特殊符号,中文等就需要对它们进行编码。http请求时,url部分是必须编码的,get的请求字段可以不进行url编码。比如
    http://www.baidu.com/中文?wd=国际
    “中文”必须进行url编码,“国际”可以不用。
    那url编码到底是怎么进行编码的呢?
    都是在16进制前面加上‘%’表示。对于一些字符使用的是”%xx”,而对于中文,就是多个”%xx%xx%xx”,xx的数字有编码的16进制决定(没有指定字符编码(utf8),则使用默认编码),然后每一字节前面加”%”。
    Android 中提供的URL编码解码方法。

    String d = URLEncoder.encode('中文'"utf8");
    String f = URLDecoder.decode("%20");

    RSA参考文章:
    【1】 RSA算法使用介绍
    【2】使用X.509数字证书加密解密实务(二)– 证书的获得和管理

    展开全文
  • 浅谈常见的七种加密算法及实现

    万次阅读 多人点赞 2018-09-10 18:38:26
    数字签名、信息加密 是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、oauth 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 签名加密算法 来达到业务...

    前言

    数字签名信息加密 是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、oauth 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 签名加密算法 来达到业务目标。这里简单的给大家介绍几种常见的签名加密算法和一些典型场景下的应用。

    正文

    1. 数字签名

    数字签名,简单来说就是通过提供 可鉴别数字信息 验证 自身身份 的一种方式。一套 数字签名 通常定义两种 互补 的运算,一个用于 签名,另一个用于 验证。分别由 发送者 持有能够 代表自己身份私钥 (私钥不可泄露),由 接受者 持有与私钥对应的 公钥 ,能够在 接受 到来自发送者信息时用于 验证 其身份。

    注意:图中 加密过程 有别于 公钥加密,更多 介绍戳这里签名 最根本的用途是要能够唯一 证明发送方的身份,防止 中间人攻击CSRF 跨域身份伪造。基于这一点在诸如 设备认证用户认证第三方认证 等认证体系中都会使用到 签名算法 (彼此的实现方式可能会有差异)。

    2. 加密和解密

    2.1. 加密

    数据加密 的基本过程,就是对原来为 明文 的文件或数据按 某种算法 进行处理,使其成为 不可读 的一段代码,通常称为 “密文”。通过这样的途径,来达到 保护数据 不被 非法人窃取、阅读的目的。

    2.2. 解密

    加密逆过程解密,即将该 编码信息 转化为其 原来数据 的过程。

    3. 对称加密和非对称加密

    加密算法分 对称加密非对称加密,其中对称加密算法的加密与解密 密钥相同,非对称加密算法的加密密钥与解密 密钥不同,此外,还有一类 不需要密钥散列算法

    常见的 对称加密 算法主要有 DES3DESAES 等,常见的 非对称算法 主要有 RSADSA 等,散列算法 主要有 SHA-1MD5 等。

    3.1. 对称加密

    对称加密算法 是应用较早的加密算法,又称为 共享密钥加密算法。在 对称加密算法 中,使用的密钥只有一个,发送接收 双方都使用这个密钥对数据进行 加密解密。这就要求加密和解密方事先都必须知道加密的密钥。

    1. 数据加密过程:在对称加密算法中,数据发送方明文 (原始数据) 和 加密密钥 一起经过特殊 加密处理,生成复杂的 加密密文 进行发送。

    2. 数据解密过程:数据接收方 收到密文后,若想读取原数据,则需要使用 加密使用的密钥 及相同算法的 逆算法 对加密的密文进行解密,才能使其恢复成 可读明文

    3.2. 非对称加密

    非对称加密算法,又称为 公开密钥加密算法。它需要两个密钥,一个称为 公开密钥 (public key),即 公钥,另一个称为 私有密钥 (private key),即 私钥

    因为 加密解密 使用的是两个不同的密钥,所以这种算法称为 非对称加密算法

    1. 如果使用 公钥 对数据 进行加密,只有用对应的 私钥 才能 进行解密

    2. 如果使用 私钥 对数据 进行加密,只有用对应的 公钥 才能 进行解密

    例子:甲方生成 一对密钥 并将其中的一把作为 公钥 向其它人公开,得到该公钥的 乙方 使用该密钥对机密信息 进行加密 后再发送给甲方,甲方再使用自己保存的另一把 专用密钥 (私钥),对 加密 后的信息 进行解密

    4. 常见的签名加密算法

    4.1. MD5算法

    MD5 用的是 哈希函数,它的典型应用是对一段信息产生 信息摘要,以 防止被篡改。严格来说,MD5 不是一种 加密算法 而是 摘要算法。无论是多长的输入,MD5 都会输出长度为 128bits 的一个串 (通常用 16 进制 表示为 32 个字符)。

    public static final byte[] computeMD5(byte[] content) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            return md5.digest(content);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    4.2. SHA1算法

    SHA1 是和 MD5 一样流行的 消息摘要算法,然而 SHA1MD5安全性更强。对于长度小于 2 ^ 64 位的消息,SHA1 会产生一个 160 位的 消息摘要。基于 MD5SHA1 的信息摘要特性以及 不可逆 (一般而言),可以被应用在检查 文件完整性 以及 数字签名 等场景。

    public static byte[] computeSHA1(byte[] content) {
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
            return sha1.digest(content);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    4.3. HMAC算法

    HMAC 是密钥相关的 哈希运算消息认证码(Hash-based Message Authentication Code),HMAC 运算利用 哈希算法 (MD5SHA1 等),以 一个密钥一个消息 为输入,生成一个 消息摘要 作为 输出

    HMAC 发送方接收方 都有的 key 进行计算,而没有这把 key 的第三方,则是 无法计算 出正确的 散列值的,这样就可以 防止数据被篡改

    package net.pocrd.util;
    import net.pocrd.annotation.NotThreadSafe;
    import net.pocrd.define.ConstField;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import javax.crypto.Mac;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;
    import java.util.Arrays;
    
    
    @NotThreadSafe
    public class HMacHelper {
        private static final Logger logger = LoggerFactory.getLogger(HMacHelper.class);
        private Mac mac;
    
        /**
         * MAC算法可选以下多种算法
         * HmacMD5/HmacSHA1/HmacSHA256/HmacSHA384/HmacSHA512
         */
        private static final String KEY_MAC = "HmacMD5";
        public HMacHelper(String key) {
            try {
                SecretKey secretKey = new SecretKeySpec(key.getBytes(ConstField.UTF8), KEY_MAC);
                mac = Mac.getInstance(secretKey.getAlgorithm());
                mac.init(secretKey);
            } catch (Exception e) {
                logger.error("create hmac helper failed.", e);
            }
        }
        public byte[] sign(byte[] content) {
            return mac.doFinal(content);
        }
    
        public boolean verify(byte[] signature, byte[] content) {
            try {
                byte[] result = mac.doFinal(content);
                return Arrays.equals(signature, result);
            } catch (Exception e) {
                logger.error("verify sig failed.", e);
            }
            return false;
        }
    }

    测试结论HMAC 算法实例在 多线程环境 下是 不安全的。但是需要在 多线程访问 时,进行同步的辅助类,使用 ThreadLocal每个线程缓存 一个实例可以避免进行锁操作。

    4.4. AES/DES/3DES算法

    AESDES3DES 都是 对称块加密算法加解密 的过程是 可逆的。常用的有 AES128AES192AES256 (默认安装的 JDK 尚不支持 AES256,需要安装对应的 jce 补丁进行升级 jce1.7jce1.8)。

    4.4.1. DES算法

    DES 加密算法是一种 分组密码,以 64 位为 分组对数据 加密,它的 密钥长度56 位,加密解密同一算法

    DES 加密算法是对 密钥 进行保密,而 公开算法,包括加密和解密算法。这样,只有掌握了和发送方 相同密钥 的人才能解读由 DES加密算法加密的密文数据。因此,破译 DES 加密算法实际上就是 搜索密钥的编码。对于 56 位长度的 密钥 来说,如果用 穷举法 来进行搜索的话,其运算次数为 2 ^ 56 次。

    4.4.2. 3DES算法

    是基于 DES对称算法,对 一块数据三个不同的密钥 进行 三次加密强度更高

    4.4.3. AES算法

    AES 加密算法是密码学中的 高级加密标准,该加密算法采用 对称分组密码体制,密钥长度的最少支持为 128 位、 192 位、256 位,分组长度 128 位,算法应易于各种硬件和软件实现。这种加密算法是美国联邦政府采用的 区块加密标准

    AES 本身就是为了取代 DES 的,AES 具有更好的 安全性效率灵活性

    import net.pocrd.annotation.NotThreadSafe;
    import javax.crypto.Cipher;
    import javax.crypto.KeyGenerator;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.security.SecureRandom;
    
    @NotThreadSafe
    public class AesHelper {
        private SecretKeySpec keySpec;
        private IvParameterSpec iv;
    
        public AesHelper(byte[] aesKey, byte[] iv) {
            if (aesKey == null || aesKey.length < 16 || (iv != null && iv.length < 16)) {
                throw new RuntimeException("错误的初始密钥");
            }
            if (iv == null) {
                iv = Md5Util.compute(aesKey);
            }
            keySpec = new SecretKeySpec(aesKey, "AES");
            this.iv = new IvParameterSpec(iv);
        }
    
        public AesHelper(byte[] aesKey) {
            if (aesKey == null || aesKey.length < 16) {
                throw new RuntimeException("错误的初始密钥");
            }
            keySpec = new SecretKeySpec(aesKey, "AES");
            this.iv = new IvParameterSpec(Md5Util.compute(aesKey));
        }
    
        public byte[] encrypt(byte[] data) {
            byte[] result = null;
            Cipher cipher = null;
            try {
                cipher = Cipher.getInstance("AES/CFB/NoPadding");
                cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
                result = cipher.doFinal(data);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return result;
        }
    
        public byte[] decrypt(byte[] secret) {
            byte[] result = null;
            Cipher cipher = null;
            try {
                cipher = Cipher.getInstance("AES/CFB/NoPadding");
                cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
                result = cipher.doFinal(secret);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return result;
        }
    
        public static byte[] randomKey(int size) {
            byte[] result = null;
            try {
                KeyGenerator gen = KeyGenerator.getInstance("AES");
                gen.init(size, new SecureRandom());
                result = gen.generateKey().getEncoded();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return result;
        }
    }

    4.5. RSA算法

    RSA 加密算法是目前最有影响力的 公钥加密算法,并且被普遍认为是目前 最优秀的公钥方案 之一。RSA 是第一个能同时用于 加密数字签名 的算法,它能够 抵抗 到目前为止已知的 所有密码攻击,已被 ISO 推荐为公钥数据加密标准。

    RSA 加密算法 基于一个十分简单的数论事实:将两个大 素数 相乘十分容易,但想要对其乘积进行 因式分解 却极其困难,因此可以将 乘积 公开作为 加密密钥

    import net.pocrd.annotation.NotThreadSafe;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import javax.crypto.Cipher;
    import java.io.ByteArrayOutputStream;
    import java.security.KeyFactory;
    import java.security.Security;
    import java.security.Signature;
    import java.security.interfaces.RSAPrivateCrtKey;
    import java.security.interfaces.RSAPublicKey;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    @NotThreadSafe
    public class RsaHelper {
        private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class);
        private RSAPublicKey publicKey;
        private RSAPrivateCrtKey privateKey;
    
        static {
            Security.addProvider(new BouncyCastleProvider()); //使用bouncycastle作为加密算法实现
        }
    
        public RsaHelper(String publicKey, String privateKey) {
            this(Base64Util.decode(publicKey), Base64Util.decode(privateKey));
        }
    
        public RsaHelper(byte[] publicKey, byte[] privateKey) {
            try {
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                if (publicKey != null && publicKey.length > 0) {
                    this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
                }
                if (privateKey != null && privateKey.length > 0) {
                    this.privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public RsaHelper(String publicKey) {
            this(Base64Util.decode(publicKey));
        }
    
        public RsaHelper(byte[] publicKey) {
            try {
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                if (publicKey != null && publicKey.length > 0) {
                    this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public byte[] encrypt(byte[] content) {
            if (publicKey == null) {
                throw new RuntimeException("public key is null.");
            }
    
            if (content == null) {
                return null;
            }
    
            try {
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                int size = publicKey.getModulus().bitLength() / 8 - 11;
                ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 11));
                int left = 0;
                for (int i = 0; i < content.length; ) {
                    left = content.length - i;
                    if (left > size) {
                        cipher.update(content, i, size);
                        i += size;
                    } else {
                        cipher.update(content, i, left);
                        i += left;
                    }
                    baos.write(cipher.doFinal());
                }
                return baos.toByteArray();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public byte[] decrypt(byte[] secret) {
            if (privateKey == null) {
                throw new RuntimeException("private key is null.");
            }
    
            if (secret == null) {
                return null;
            }
    
            try {
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
                int size = privateKey.getModulus().bitLength() / 8;
                ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size - 12) / (size - 11) * size);
                int left = 0;
                for (int i = 0; i < secret.length; ) {
                    left = secret.length - i;
                    if (left > size) {
                        cipher.update(secret, i, size);
                        i += size;
                    } else {
                        cipher.update(secret, i, left);
                        i += left;
                    }
                    baos.write(cipher.doFinal());
                }
                return baos.toByteArray();
            } catch (Exception e) {
                logger.error("rsa decrypt failed.", e);
            }
            return null;
        }
    
        public byte[] sign(byte[] content) {
            if (privateKey == null) {
                throw new RuntimeException("private key is null.");
            }
            if (content == null) {
                return null;
            }
            try {
                Signature signature = Signature.getInstance("SHA1WithRSA");
                signature.initSign(privateKey);
                signature.update(content);
                return signature.sign();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public boolean verify(byte[] sign, byte[] content) {
            if (publicKey == null) {
                throw new RuntimeException("public key is null.");
            }
            if (sign == null || content == null) {
                return false;
            }
            try {
                Signature signature = Signature.getInstance("SHA1WithRSA");
                signature.initVerify(publicKey);
                signature.update(content);
                return signature.verify(sign);
            } catch (Exception e) {
                logger.error("rsa verify failed.", e);
            }
            return false;
        }
    }

    4.6. ECC算法

    ECC 也是一种 非对称加密算法,主要优势是在某些情况下,它比其他的方法使用 更小的密钥,比如 RSA 加密算法,提供 相当的或更高等级 的安全级别。不过一个缺点是 加密和解密操作 的实现比其他机制 时间长 (相比 RSA 算法,该算法对 CPU 消耗严重)。

    import net.pocrd.annotation.NotThreadSafe;
    import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
    import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import javax.crypto.Cipher;
    import java.io.ByteArrayOutputStream;
    import java.security.KeyFactory;
    import java.security.Security;
    import java.security.Signature;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    @NotThreadSafe
    public class EccHelper {
        private static final Logger logger = LoggerFactory.getLogger(EccHelper.class);
        private static final int SIZE = 4096;
        private BCECPublicKey  publicKey;
        private BCECPrivateKey privateKey;
    
        static {
            Security.addProvider(new BouncyCastleProvider());
        }
    
        public EccHelper(String publicKey, String privateKey) {
            this(Base64Util.decode(publicKey), Base64Util.decode(privateKey));
        }
    
        public EccHelper(byte[] publicKey, byte[] privateKey) {
            try {
                KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
                if (publicKey != null && publicKey.length > 0) {
                    this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
                }
                if (privateKey != null && privateKey.length > 0) {
                    this.privateKey = (BCECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
                }
            } catch (ClassCastException e) {
                throw new RuntimeException("", e);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public EccHelper(String publicKey) {
            this(Base64Util.decode(publicKey));
        }
    
        public EccHelper(byte[] publicKey) {
            try {
                KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
                if (publicKey != null && publicKey.length > 0) {
                    this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public byte[] encrypt(byte[] content) {
            if (publicKey == null) {
                throw new RuntimeException("public key is null.");
            }
            try {
                Cipher cipher = Cipher.getInstance("ECIES", "BC");
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                int size = SIZE;
                ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 45));
                int left = 0;
                for (int i = 0; i < content.length; ) {
                    left = content.length - i;
                    if (left > size) {
                        cipher.update(content, i, size);
                        i += size;
                    } else {
                        cipher.update(content, i, left);
                        i += left;
                    }
                    baos.write(cipher.doFinal());
                }
                return baos.toByteArray();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public byte[] decrypt(byte[] secret) {
            if (privateKey == null) {
                throw new RuntimeException("private key is null.");
            }
            try {
                Cipher cipher = Cipher.getInstance("ECIES", "BC");
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
                int size = SIZE + 45;
                ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size + 44) / (size + 45) * size);
                int left = 0;
                for (int i = 0; i < secret.length; ) {
                    left = secret.length - i;
                    if (left > size) {
                        cipher.update(secret, i, size);
                        i += size;
                    } else {
                        cipher.update(secret, i, left);
                        i += left;
                    }
                    baos.write(cipher.doFinal());
                }
                return baos.toByteArray();
            } catch (Exception e) {
                logger.error("ecc decrypt failed.", e);
            }
            return null;
        }
    
        public byte[] sign(byte[] content) {
            if (privateKey == null) {
                throw new RuntimeException("private key is null.");
            }
            try {
                Signature signature = Signature.getInstance("SHA1withECDSA", "BC");
                signature.initSign(privateKey);
                signature.update(content);
                return signature.sign();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public boolean verify(byte[] sign, byte[] content) {
            if (publicKey == null) {
                throw new RuntimeException("public key is null.");
            }
            try {
                Signature signature = Signature.getInstance("SHA1withECDSA", "BC");
                signature.initVerify(publicKey);
                signature.update(content);
                return signature.verify(sign);
            } catch (Exception e) {
                logger.error("ecc verify failed.", e);
            }
            return false;
        }
    }

    5. 各种加密算法对比

    5.1. 散列算法比较

    名称 安全性 速度
    SHA-1
    MD5

    5.2. 对称加密算法比较

    名称 密钥名称 运行速度 安全性 资源消耗
    DES 56位 较快
    3DES 112位或168位
    AES 128、192、256位

    5.3. 非对称加密算法比较

    名称 成熟度 安全性 运算速度 资源消耗
    RSA 高
    ECC

    5.4. 对称算法与非对称加密算法

    5.4.1. 对称算法

    1. 密钥管理:比较难,不适合互联网,一般用于内部系统

    2. 安全性:中

    3. 加密速度:快好 几个数量级 (软件加解密速度至少快 100 倍,每秒可以加解密数 M 比特 数据),适合大数据量的加解密处理

    5.4.2. 非对称算法

    1. 密钥管理:密钥容易管理

    2. 安全性:高

    3. 加密速度:比较慢,适合 小数据量 加解密或数据签名

    小结

    本文介绍了 数字签名加密和解密对称加密和非对称加密,然后详细介绍了 MD5SHA-1HMACDES/AESRSAECC 这几种加密算法和代码示例。


    欢迎关注技术公众号: 零壹技术栈

    零壹技术栈

    本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

    展开全文
  • 本套视频教程介绍加密解密相关的常见算法,指出每种算法的应用场景,并给出使用示例。具体包含: (1) 数据转换(HEX和base64) (2) 对称加密 (DES, 3DES, AES) (3) 校验 CRC, MD5摘要, SHA1摘要 (4) 数字签名...
  • 常见的3种加密方式

    千次阅读 2018-05-21 10:03:24
    对称加密;加密和解密使用的都是同一串秘钥,例如COOKIE,通信加密,DES算法和RC算法 单向散列加密:根据输入长度信息进行散列计算,得到固定长度输出,常用于密码保存,常见的是MD5,SHA等,通常会加盐处理 非对称...

    对称加密;加密和解密使用的都是同一串秘钥,例如COOKIE,通信加密,DES算法和RC算法

    单向散列加密:根据输入长度信息进行散列计算,得到固定长度输出,常用于密码保存,常见的是MD5,SHA等,通常会加盐处理

    非对称加密:加密和解密使用的是不同的秘钥,其中一个对外公开,称为公钥,另一个被称为私钥,例如RSA算法,https的数字证书

    展开全文
  • 171216—几种经典加密方法。

    千次阅读 2017-12-16 21:15:10
    此篇只打算梳理几种加密方式的大体思路,具体公式不熟,不在整理范围内。Caesar加密即凯撒密码,基础的基础,核心是“k”偏移量。 其原理在于,按既有字母表顺序,以k为偏移量,使用一个字母后固定数值位的另一个...
  • 几种加密方式

    千次阅读 2018-11-30 15:29:03
    1 Base64加密方式(可逆) Base64中的可打印字符包括字母A-Z/a-z/数组0-9/ 加号’+’斜杠’/’ 这样共有62个字符 Base64 ios7之后加入系统库   2 MD5加密 Message Digest Algorithm MD5(中文名为消息摘要算法...
  • 网络安全几种加密方式

    千次阅读 2019-06-10 10:53:06
    下面介绍几种网络传播加密方式: 1.密钥散列 采用MD5或者SHA1等散列算法,对明文进行加密(这里的加密仅对人,不对机器,因为这些算法机器可以用对应算法算出来) 适用场景:普通文件下载 缺点:不具备安全性...
  • 用户登录对密码进行加密

    万次阅读 2017-05-09 13:23:39
    加密: 我们使用网络探测器能轻易的获取用户的用户名密码,如果这些信息被不发份子获得,后果可想而知; 这里我们可以看到 密码是以明文的方式 被网络探测器 嗅到 现对密码进行加密,如果对用户也不希望...
  • 常见的几种加密方法和实现

    千次阅读 2018-11-27 16:09:40
    常见的几种加密方法
  • Unicdoe【真正的完整码表】对照表(一)

    万次阅读 多人点赞 2017-09-15 21:43:27
    真正完整的Unicode码表,其中CJK = Chinese-Japanese-Korean 下面列举了一些相关的索引。查看完整索引到:... 2E80-2EFF:CJK 部首补充 (CJK Radicals Supplement)  2F00-2FDF:康熙字典部首 (Kangxi Ra
  • 加密算法

    2018-08-14 14:56:53
    加密技术是对信息进行编码和解码的技术,编码是把原来可读信息(又称明文)译成代码形式(又称密文),其逆过程就是解码(解密),加密技术的要点是加密算法。加密算法主要分为摘要算法(不可逆算法)、对称算法以及...
  • 加密解密类

    2015-07-22 16:28:51
    import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; ...import org.apache.commons.codec.StringEncoder;...import org.apache.commons.co...
  • 对称加密和非对称加密的区别

    万次阅读 多人点赞 2019-07-09 10:40:25
    对称加密算法: 密钥较短,破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,且对计算机性能要求也没有那么高. 优点: 算法公开、计算量小...
  • 第一种用法:公钥加密,私钥解密。---用于加解密 第二种用法:私钥签名,公钥验签。---用于签名 有点混乱,不要去硬记,总结一下: 你只要想: 既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,...
  • exew文件加密:利用破解版exe文件加密器对exe文件进行加密保护(图文教程) 目录 加密过程步骤 解密过程步骤 加密过程步骤 解密过程步骤 ...
  • SS加密方式浅析

    万次阅读 2017-09-24 20:10:53
    理清概念:加密仅保证报文内容不被其他人探知,并不能保证报文的内容不被修改,所以需要消息完整性校验。 RC4-MD5 速度最快 但加密简单,易破解 rc4加密,md5校验 AES 区别 AES-XXX-CFB AES-XXX-CTR AES-XXX-...
  • linux 下文件加密压缩和解压的方法

    万次阅读 2012-11-21 09:49:47
    方法一:用tar命令 对文件加密压缩和解压 压缩: tar -zcf - filename |openssl des3 -salt -k password | dd of=filename.des3 此命令对filename文件进行加码压缩 生成filename.des3加密压缩文件, password ...
  • 移动硬盘加密软件的相关简介

    万次阅读 2019-04-11 14:26:44
    在网络时代,各类大数据的技术也在迅速发展,... 移动硬盘加密软件是根据在操作系统中运作某类软件加密完成对数据的加密和破译工作。软件加密的方式通常非常简单,保持起來较为经济。依靠加密软件能够实现U盘、移动...
  • API接口加密方式说明

    万次阅读 2016-12-15 16:03:53
    本文档适用于api接口开发人员、移动端开发人员,针对移动端请求API接口时加密方式以及加密规则进行说明。 二、加密方式 1.不采取非对称加密说明 好处: 相对于对称加密,非对称加密安全性远远高宇对称加密,...
1 2 3 4 5 ... 20
收藏数 855,159
精华内容 342,063
关键字:

加密