Loading... # 信息加密技术 ## 一、加密的方式 ### 1. 对称加密 #### 1. 简介 加密和解密使用同一个密钥。  #### 2. 优缺点: - 算法公开,计算量小,加密速度快,加密效率高 - 双方使用相同的钥匙,安全性得不到保证 - 秘钥管理比较难,不适合互联网,一般用于内部系统 #### 3. 经典对称加密: 1. DES(Data Encryption Standard):数据加密标准(现在用的比较少,因为它的加密强度不够,能够暴力破解) 2. 3DES:原理和DES几乎是一样的,只是使用3个密钥,对相同的数据执行三次加密,增强加密强度。(缺点:要维护3个密钥,大大增加了维护成本) 3. AES(Advanced Encryption Standard):高级加密标准,目前美国国家安全局使用的,苹果的钥匙串访问采用的就AES加密。是现在公认的最安全的加密方式,是对称密钥加密中最流行的算法。 ##### DES DES(Data Encryption Standard)是目前最为流行的加密算法之一。DES是对称的,也就是说它使用同一个密钥来加密和解密数据。 ###### **原理** > DES 使用一个 56 位的密钥以及附加的 8 位奇偶校验位,产生最大 64 位的分组大小。这是一个迭代的分组密码,使用称为 Feistel 的技术,其中将加密的文本块分成两半。使用子密钥对其中一半应用循环功能,然后将输出与另一半进行"异或"运算;接着交换这两半,这一过程会继续下去,但最后一个循环不交换。DES 使用 16 个循环,使用异或,置换,代换,移位操作四种基本运算。 > > 不过,DES已可破解,所以针对保密级别特别高的数据推荐使用非对称加密算法。  ###### **优缺点** ##### AES  ###### 属性 1. 秘钥 > 用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取机密数据。 2. AES加密函数 > 设AES加密函数为E,则 C = E(K, P),其中P为明文,K为密钥,C为密文。也就是说,把明文P和密钥K作为加密函数的参数输入,则加密函数E会输出密文C。 3. AES解密函数 > 设AES解密函数为D,则 P = D(K, C),其中C为密文,K为密钥,P为明文。也就是说,把密文C和密钥K作为解密函数的参数输入,则解密函数会输出明文P。 ###### 原理 [基于原理逐步实现AES加密算法](http://geekeye.cc/post/impl-of-aes/) --- ### 2. 非对称加密 #### 1. 用法 > (1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。 > > (2)甲方获取乙方的公钥,然后用它对信息加密。 > > (3)乙方得到加密后的信息,用私钥解密。 #### 2. 典型非对称加密——RSA RSA非常[可靠](http://en.wikipedia.org/wiki/RSA_Factoring_Challenge),密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。 #### 3. 实现原理 [RSA算法原理](http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html) #### 4. 特点 - 算法强度复杂,安全性依赖于算法与密钥。 - 加密解密速度慢。适合 **小数据量** 加解密或数据签名 - 密钥容易管理 #### 5. 对比 **与对称加密算法的对比:** - 对称加密只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。 - 非对称加密有两种密钥,其中一个是公开的。 #### 6. RSA应用场景: 由于RSA算法的加密解密速度要比对称算法速度慢很多,在实际应用中,通常采取 数据本身的加密和解密使用对称加密算法(AES)。 用RSA算法加密并传输对称算法所需的密钥。  ### 3. 单向散列加密 不可逆,只能加密,不能解密 > 单向散列加密是指通过对不同输入长度的信息进行散列计算,得到固定长度的输出,这个散列计算过程是单向的,即不能对固定长度的输出进行计算从而获得输入信息。 > > 利用单向散列加密的这个特性,可以进行密码加密保存,即用户注册时输入的密码不直接保存到数据库,而是对密码进行单向散列加密,将密文存入数据库,用户登录时,进行密码验证,同样计算得到输入密码的密文,并和数据库中的密文比较,如果一致,则密码验证成功。为了加强单向散列计算的安全性,还会给散列算法加点salt,salt相当于加密的密钥,增加破解的难度。常用的单向散列算法有MD5、SHA等。 #### 特点 - 算法强度复杂 - 无秘钥 ## 二、加密算法实现 ### 1. 非对称加密(RSA) 前端公钥加密,后端私钥解密。  #### 1. 生成秘钥 利用在线生成秘钥工具生成非对称加密公钥私钥对 [在线生成非对称公钥私钥对](http://web.chacuo.net/netrsakeypair) #### 2. 前端对利用公钥对数据进行加密 **工具类** ```javascript import JSEncrypt from 'jsencrypt/bin/jsencrypt' // 密钥对生成 http://web.chacuo.net/netrsakeypair const publicKey = '...' const privateKey = '...' // 加密 export function encrypt(txt) { const encryptor = new JSEncrypt() encryptor.setPublicKey(publicKey) // 设置公钥 return encryptor.encrypt(txt) // 对需要加密的数据进行加密 } // 解密 export function decrypt(txt) { const encryptor = new JSEncrypt() encryptor.setPrivateKey(privateKey) return encryptor.decrypt(txt) } ``` **使用公钥加密** ```javascript user.password = encrypt(user.password) ``` #### 3. 后端利用私钥对数据进行解密 **工具类** ```java package com.advance.utils; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; /** * description Rsa 工具类,公钥私钥生成,加解密 * @author https://www.cnblogs.com/nihaorz/p/10690643.html * @date 2020-12-15 **/ public class RsaUtils { private static final String SRC = "123456"; public static void main(String[] args) throws Exception { System.out.println("\n"); RsaKeyPair keyPair = generateKeyPair(); System.out.println("公钥:" + keyPair.getPublicKey()); System.out.println("私钥:" + keyPair.getPrivateKey()); System.out.println("\n"); test1(keyPair); System.out.println("\n"); test2(keyPair); System.out.println("\n"); } /** * 公钥加密私钥解密 */ private static void test1(RsaKeyPair keyPair) throws Exception { System.out.println("***************** 公钥加密私钥解密开始 *****************"); String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC); String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1); System.out.println("加密前:" + RsaUtils.SRC); System.out.println("加密后:" + text1); System.out.println("解密后:" + text2); if (RsaUtils.SRC.equals(text2)) { System.out.println("解密字符串和原始字符串一致,解密成功"); } else { System.out.println("解密字符串和原始字符串不一致,解密失败"); } System.out.println("***************** 公钥加密私钥解密结束 *****************"); } /** * 私钥加密公钥解密 * @throws Exception / */ private static void test2(RsaKeyPair keyPair) throws Exception { System.out.println("***************** 私钥加密公钥解密开始 *****************"); String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC); String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1); System.out.println("加密前:" + RsaUtils.SRC); System.out.println("加密后:" + text1); System.out.println("解密后:" + text2); if (RsaUtils.SRC.equals(text2)) { System.out.println("解密字符串和原始字符串一致,解密成功"); } else { System.out.println("解密字符串和原始字符串不一致,解密失败"); } System.out.println("***************** 私钥加密公钥解密结束 *****************"); } /** * 公钥解密 * * @param publicKeyText 公钥 * @param text 待解密的信息 * @return / * @throws Exception / */ public static String decryptByPublicKey(String publicKeyText, String text) throws Exception { X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, publicKey); byte[] result = cipher.doFinal(Base64.decodeBase64(text)); return new String(result); } /** * 私钥加密 * * @param privateKeyText 私钥 * @param text 待加密的信息 * @return / * @throws Exception / */ public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception { PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] result = cipher.doFinal(text.getBytes()); return Base64.encodeBase64String(result); } /** * 私钥解密 * * @param privateKeyText 私钥 * @param text 待解密的文本 * @return / * @throws Exception / */ public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception { PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] result = cipher.doFinal(Base64.decodeBase64(text)); return new String(result); } /** * 公钥加密 * * @param publicKeyText 公钥 * @param text 待加密的文本 * @return / */ public static String encryptByPublicKey(String publicKeyText, String text) throws Exception { X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] result = cipher.doFinal(text.getBytes()); return Base64.encodeBase64String(result); } /** * 构建RSA密钥对 * * @return / * @throws NoSuchAlgorithmException / */ public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(1024); KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded()); String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); return new RsaKeyPair(publicKeyString, privateKeyString); } /** * RSA密钥对对象 */ public static class RsaKeyPair { private final String publicKey; private final String privateKey; public RsaKeyPair(String publicKey, String privateKey) { this.publicKey = publicKey; this.privateKey = privateKey; } public String getPublicKey() { return publicKey; } public String getPrivateKey() { return privateKey; } } } ``` **使用私钥解密** ```java String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, user.getPassword()); ``` ### 2. 对称加密 [java实现AES加密解密算法](http://www.asfx.xyz/p/e3124067382f4c6a9fb1b43fc799b5e5) #### AES ```java package com.marchosft.security; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.SecureRandom; import java.util.Base64; /** * Description:java实现AES加密解密算法 AES加密结果用Base64再做加密处理 * Date: 2020/12/16 10:59 **/ public class AesUtil { private static final String DEFAULT_KEY = "12345678"; static final Base64.Decoder DECODER = Base64.getDecoder(); static final Base64.Encoder ENCODER = Base64.getEncoder(); static final String CHARSET = "utf-8"; static final String AES = "AES"; /** * 先AES加密,再Base64加密 * * @param content 待加密的内容 * @return / */ public static String encodeBase64(String content) { //AES加密 String encode = encode(content); //加密失败返回空 if (encode == null) { return null; } try { //Base64加密 return ENCODER.encodeToString(encode.getBytes(CHARSET)); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } /** * 先Base64解密,再AES解密 * * @param content 待解密的内容 * @return / */ public static String decodeBase64(String content) { try { //Base64解密 String s = new String(DECODER.decode(content), CHARSET); //AES解密 return decode(s); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //解密失败返回空 return null; } /** * AES加密 * * @param content 待加密的内容 * @return / */ public static String encode(String content) { return encode(DEFAULT_KEY, content); } /** * AES解密 * * @param content 待解密的内容 * @return / */ public static String decode(String content) { return decode(DEFAULT_KEY, content); } /** * AES加密 * 1.构造密钥生成器 * 2.根据ecnodeRules规则初始化密钥生成器 * 3.产生密钥 * 4.创建和初始化密码器 * 5.内容加密 * 6.返回字符串 */ public static String encode(String encodeRules, String content) { try { //1.构造密钥生成器,指定为AES算法,不区分大小写 KeyGenerator keygen = KeyGenerator.getInstance(AES); //2.根据ecnodeRules规则初始化密钥生成器 //生成一个128位的随机源,根据传入的字节数组 SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.setSeed(encodeRules.getBytes()); keygen.init(128, random); //3.产生原始对称密钥 SecretKey originalKey = keygen.generateKey(); // 4.获得原始对称密钥的字节数组 byte[] raw = originalKey.getEncoded(); //5.根据字节数组生成AES密钥 SecretKey key = new SecretKeySpec(raw, AES); //6.根据指定算法AES自成密码器 Cipher cipher = Cipher.getInstance(AES); //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY cipher.init(Cipher.ENCRYPT_MODE, key); //8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码 byte[] byteEncode = content.getBytes(CHARSET); //9.根据密码器的初始化方式--加密:将数据加密 byte[] byteAes = cipher.doFinal(byteEncode); //10.将加密后的数据转换为字符串 //这里用Base64Encoder中会找不到包 //解决办法: //在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。 //11.将字符串返回 return new String(new BASE64Encoder().encode(byteAes)); } catch (Exception e) { e.printStackTrace(); } //加密失败返回空 return null; } /** * AES解密 * 解密过程: * 1.同加密1-4步 * 2.将加密后的字符串反纺成byte[]数组 * 3.将加密内容解密 */ public static String decode(String encodeRules, String content) { try { //1.构造密钥生成器,指定为AES算法,不区分大小写 KeyGenerator keygen = KeyGenerator.getInstance(AES); //2.根据ecnodeRules规则初始化密钥生成器 //生成一个128位的随机源,根据传入的字节数组 // keygen.init(128, new SecureRandom(encodeRules.getBytes())); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.setSeed(encodeRules.getBytes()); keygen.init(128, random); //3.产生原始对称密钥 SecretKey originalKey = keygen.generateKey(); //4.获得原始对称密钥的字节数组 byte[] raw = originalKey.getEncoded(); //5.根据字节数组生成AES密钥 SecretKey key = new SecretKeySpec(raw, AES); //6.根据指定算法AES自成密码器 Cipher cipher = Cipher.getInstance(AES); //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY cipher.init(Cipher.DECRYPT_MODE, key); //8.将加密并编码后的内容解码成字节数组 byte[] byteContent = new BASE64Decoder().decodeBuffer(content); /* * 解密 */ byte[] byteDecode = cipher.doFinal(byteContent); return new String(byteDecode, CHARSET); } catch (Exception e) { e.printStackTrace(); } //解密失败返回空 return null; } public static void main(String[] args) { String a = "1236"; String s = encodeBase64(a); System.out.println(s); String s1 = decodeBase64(s); System.out.println(s1); } } ``` ### 3. 单向散列加密 #### MD5 ```java public static final byte[] computeMD5(byte[] content) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); return md5.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } ``` #### SHA1 ```java public static byte[] computeSHA1(byte[] content) { try { MessageDigest sha1 = MessageDigest.getInstance("SHA1"); return sha1.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } ``` ## 三、加密算法比较 **1、散列算法比较** | 名称 | 安全性 | 速度 | | :------ | :------- | :----- | | SHA-1 | 高 | 慢 | | MD5 | 中 | 快 | **2、对称加密算法比较** | 名称 | 密钥名称 | 运行速度 | 安全性 | 资源消耗 | | :----- | :---------------- | :--------- | :------- | :--------- | | DES | 56位 | 较快 | 低 | 中 | | 3DES | 112位或168位 | 慢 | 中 | 高 | | AES | 128、192、256位 | 快 | 高 | 低 | **3、非对称加密算法比较** | 名称 | 成熟度 | 安全性 | 运算速度 | 资源消耗 | | :----- | :------- | :------- | :--------- | :--------- | | RSA | 高 | 高 | 中 | 中 | | ECC | 高 | 高 | 慢 | 高 | Last modification:July 31st, 2021 at 10:59 am © 允许规范转载