文章内容
实际工作中遇到的加密解密算法和算法之间的区别以及如何选择,包括:BASE64、DES、3DES、AES、RSA、MD5、SHA。
区别与选择:
- 加密算法可逆,用来保证数据安全,散列算法不可逆,用来验证身份。
- 对称加密算法,速度快,适合给大量数据加密,交互双方使用同一个密钥,存在被抓包破解的风险。
- 非对称加密算法使用公钥加密,私钥解密,私钥签名,公钥验签,安全性较高,但速度较慢。非对称加密使用两个密钥,服务端和客户端密匙不一样,私钥放在服务端,安全性高。
- 在实际的操作过程中,通常采用的方式是:采用非对称加密算法管理对称算法的密钥,然后用对称加密算法加密数据,这样就集成了两类加密算法的优点,既实现了加密速度快的优点,又实现了安全方便管理密钥的优点。
一、编码加解密
1、Base64
严格意义来说Base64并不是一种加密/解密算法,而是一种编码的方式。Base64不生成密钥,通过Base64编码后的密文就可以直接“翻译”为明文。在网络上传输数据时,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。所以就先把数据先做一个Base64编码,统统变成可见字符,这样出错的可能性就大降低,Base64转换后的字符串理论上将要比原来的长1/3。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | import java.util.Base64; /** * Base64使用例子 * 加密和解密 */ public class Base64Demo { public static void main(String[] args) { // 加密文字 String data = "EasyJava" ; // 加密 byte [] encode = Base64.getEncoder().encode(data.getBytes()); String enCodeResult = new String(encode); System.out.println( "加密结果:" + enCodeResult); // 解密 byte [] decode = Base64.getDecoder().decode(enCodeResult); String decodeResult = new String(decode); System.out.println( "解密结果:" + decodeResult); } } |
执行结果:
1 2 | 加密结果:RWFzeUphdmE= 解密结果:EasyJava |
二、对称加解密
1、DES
DES是一种基于56位密钥的对称算法,1976年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来。现在DES已经不是一种安全的加密算法,已被公开破解,现在DES已经被高级加密标准(AES)所代替。新项目不建议选择DES,这种加密方式只存在历史项目中。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import java.security.Key; /** * DES使用例子 * 加密和解密 */ public class DESDemo { /** * 密钥 */ private static String SECRET_KEY = "12345678" ; private static final char [] HEXES = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' }; /** * byte数组 转换成 16进制小写字符串 */ public static String bytes2Hex( byte [] bytes) { if (bytes == null || bytes.length == 0 ) { return null ; } StringBuilder hex = new StringBuilder(); for ( byte b : bytes) { hex.append(HEXES[(b >> 4 ) & 0x0F ]); hex.append(HEXES[b & 0x0F ]); } return hex.toString(); } public static void main(String[] args) throws Exception { // 加密字符串 String src = "EasyJava" ; //KEY转换 DESKeySpec desKeySpec = new DESKeySpec(SECRET_KEY.getBytes( "utf-8" )); SecretKeyFactory factory = SecretKeyFactory.getInstance( "DES" ); Key convertSecretKey = factory.generateSecret(desKeySpec); //加密 Cipher cipher = Cipher.getInstance( "DES/ECB/PKCS5Padding" ); cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey); byte [] result = cipher.doFinal(src.getBytes()); System.out.println( "加密结果: " + bytes2Hex(result)); //解密 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey); result = cipher.doFinal(result); System.out.println( "解密结果: " + new String(result)); } } |
执行结果:
1 2 | 加密结果: dcf185f57bb08208feb959b7d4642fcb 解密结果: EasyJava |
2、3DES
相当于是对每个数据块应用三次DES加密算法。由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解,3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新密码算法。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.IvParameterSpec; import java.nio.charset.StandardCharsets; import java.security.Key; /** * DES3使用例子 * 加密和解密 */ public class DES3Demo { /** * 密钥(24位) */ private static String SECRET_KEY = "123456781234567812345678" ; private static final String KEY_ALGORITHM = "DESede" ; private static final String CIPHER_ALGORITHM = "DESede/CBC/PKCS5Padding" ; /** * 3DES 加密 * * @param key 密钥(24位) * @param iv 偏移量 * @param data 需要加密的字符串 * @return 返回加密的字符串 */ public static String encrypt(String key, String iv, String data) { try { DESedeKeySpec spec = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8)); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM); Key desKey = keyFactory.generateSecret(spec); Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); IvParameterSpec ips = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.ENCRYPT_MODE, desKey, ips); byte [] bOut = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); return Base64.encodeBase64String(bOut); } catch (Exception e) { e.printStackTrace(); System.out.println( "3DES 解密错误" ); throw new RuntimeException( "3DES 解密错误" ); } } /** * 3DES 解密 * * @param key 密钥(24位) * @param iv 偏移量 * @param data 需要解密的密文 * @return 返回加密的字符串 */ public static String decrypt(String key, String iv, String data) { try { DESedeKeySpec spec = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8)); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM); Key desKey = keyFactory.generateSecret(spec); Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); IvParameterSpec ips = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.DECRYPT_MODE, desKey, ips); byte [] bOut = cipher.doFinal(Base64.decodeBase64(data.getBytes(StandardCharsets.UTF_8))); return new String(bOut, StandardCharsets.UTF_8); } catch (Exception e) { e.printStackTrace(); System.out.println( "3DES 解密错误" ); throw new RuntimeException( "3DES 解密错误" ); } } public static void main(String[] args) { String data = "EasyJava" ; String des3EncodeCBC = encrypt(SECRET_KEY, "53152654" , data); System.out.println( "加密结果:" + des3EncodeCBC); String des3DecodeCBC = decrypt(SECRET_KEY, "53152654" , des3EncodeCBC); System.out.println( "解密结果:" + des3DecodeCBC); } } |
执行结果:
1 2 | 加密结果:5gBjg/fcZ45YrGKm8nxHvQ== 解密结果:EasyJava |
3、AES
AES是目前对称加密算法中最流行的算法之一,是DES的替代者,未被破解,性能更优。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.util.Base64; /** * AES使用例子 * 加密和解密 */ public class AESDemo { /** * 密钥(24) */ private static String SECRET_KEY = "xxxxxxxxxxxxxxxxxxxxxxxx" ; public static void main(String[] args) throws Exception { // 加密字符串 String src = "EasyJava" ; // Key转换 Key key = new SecretKeySpec(SECRET_KEY.getBytes( "utf-8" ), "AES" ); // 加密 Cipher cipher = Cipher.getInstance( "AES/ECB/PKCS5Padding" ); cipher.init(Cipher.ENCRYPT_MODE, key); byte [] encryptBytes = cipher.doFinal(src.getBytes( "utf-8" )); System.out.println( "加密结果:" + Base64.getEncoder().encodeToString(encryptBytes)); // 解密 cipher.init(Cipher.DECRYPT_MODE, key); byte [] decodeBytes = cipher.doFinal(encryptBytes); System.out.println( "解密结果:" + new String(decodeBytes)); } } |
执行结果:
1 2 | 加密结果:ESuCYW2piOpf4Expba6IuA== 解密结果:EasyJava |
三、非对称加解密
1、RSA
广泛接受且实现,多用于数据加密和数字签名领域,支持公钥加密私钥解密,私钥加密公钥解密方式。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 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; import java.util.Base64; /** * RSA使用例子 * 私钥加密,公钥解密 * 公钥加密,私钥解密 */ public class RSADemo { private static void encode1(String src, RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey) throws Exception { // 加密 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance( "RSA" ); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Cipher cipher = Cipher.getInstance( "RSA" ); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte [] encodeResult = cipher.doFinal(src.getBytes( "utf-8" )); System.out.println( "【私钥加密、公钥解密】加密结果:" + Base64.getEncoder().encodeToString(encodeResult)); // 解密 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance( "RSA" ); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); cipher = Cipher.getInstance( "RSA" ); cipher.init(Cipher.DECRYPT_MODE, publicKey); byte [] decodeResult = cipher.doFinal(encodeResult); System.out.println( "【私钥加密、公钥解密】解密结果:" + new String(decodeResult, "utf-8" )); } private static void encode2(String src, RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey) throws Exception { // 加密 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance( "RSA" ); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); Cipher cipher = Cipher.getInstance( "RSA" ); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte [] encodeResult = cipher.doFinal(src.getBytes( "utf-8" )); System.out.println( "【公钥加密、私钥解密】加密结果:" + Base64.getEncoder().encodeToString(encodeResult)); // 解密 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); keyFactory = KeyFactory.getInstance( "RSA" ); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); cipher = Cipher.getInstance( "RSA" ); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte [] decodeResult = cipher.doFinal(encodeResult); System.out.println( "【公钥加密、私钥解密】解密结果:" + new String(decodeResult, "utf-8" )); } public static void main(String[] args) throws Exception { // 加密字符串 String src = "EasyJava" ; KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "RSA" ); keyPairGenerator.initialize( 512 ); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 公钥 & 私钥 RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); System.out.println( "公钥:" + Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded())); System.out.println( "私钥:" + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded())); // 【私钥加密、公钥解密】 encode1(src, rsaPublicKey, rsaPrivateKey); // 【公钥加密、私钥解密】 encode2(src, rsaPublicKey, rsaPrivateKey); } } |
执行结果:
1 2 3 4 5 6 | 公钥:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ68MgEMZD7X+5e9kYwLZfcXeCzlXlB3me/v214mON3ONbDjX++a3b7EqW+rVf49H9AmiY5bWuj335X/18/qyV8CAwEAAQ== 私钥:MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAnrwyAQxkPtf7l72RjAtl9xd4LOVeUHeZ7+/bXiY43c41sONf75rdvsSpb6tV/j0f0CaJjlta6Pfflf/Xz+rJXwIDAQABAkAnJACwgX22gUtofzYEcksSQpc/es9myBcNMvfEn2gmSO8wYDxTnyctYGC0slOjFXDSuTp5WoK/bRcAOWnDI/UBAiEA0uQJeMGpl5c8Qmbxs5NWJvXOntJE/S/Ka/1UgX/MJXUCIQDAsDf9rUDxzjF+hqY9Mv79STO3PYd5MZN/C3uHCGzVAwIgDf6d9kp7s5iQoiNstKr5U5qKPJXdiCOsvh/QhMtzQ6UCIFkiKwRh3KT+aM7KoqO0r1ejQRUGlWBumngua5nvP8jZAiBcdQ1czDuqBPQrua0DylFeP/3way2pKrQFg4xmNcM8Uw== 【私钥加密、公钥解密】加密结果:KjQIb91tWcau+FAwPF6AX6jw3QzYhXyaQQPG6roNWZ5wT1nMhanX2G1O2ZNPqVS2s3Ar4Cl1ROKYF6JmlEeYBw== 【私钥加密、公钥解密】解密结果:EasyJava 【公钥加密、私钥解密】加密结果:gw3PUe2zrCsZ+SyIgGBPX2ZnzQVnTZal7pvP4TZRGUzQho2IK91YjdbV8ec9Tp9HUHKVHMf96aNu2tRc7ZDMBg== 【公钥加密、私钥解密】解密结果:EasyJava |
2、MD5
MD5严格意义上不属于加密算法,属于信息摘要算法。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import java.security.MessageDigest; /** * MD5使用例子 * 生成消息摘要 */ public class MD5Demo { private static final char [] HEXES = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' }; /** * byte数组 转换成 16进制小写字符串 */ public static String bytes2Hex( byte [] bytes) { if (bytes == null || bytes.length == 0 ) { return null ; } StringBuilder hex = new StringBuilder(); for ( byte b : bytes) { hex.append(HEXES[(b >> 4 ) & 0x0F ]); hex.append(HEXES[b & 0x0F ]); } return hex.toString(); } public static void main(String[] args) throws Exception { // 加密字符 String src = "EasyJava" ; // 获取摘要 MessageDigest messageDigest = MessageDigest.getInstance( "MD5" ); byte [] md5Result = messageDigest.digest(src.getBytes( "utf-8" )); System.out.println( "摘要信息:" + bytes2Hex(md5Result)); } } |
执行结果:
1 | 摘要信息:0d50d2ea5aece0b7bc10643a3f72159c |
3、SHA
SHA家族的五个算法,分别是SHA-1、SHA-224、SHA-256、SHA-384,和 SHA-512,后四者有时并称为SHA-2。SHA-1在许多安全协定中广为使用。 MD5与SHA1都是Hash算法,MD5输出是128位的(转换为16进制则为32个字符),SHA1输出是160位的(转换为16进制则为40个字符),MD5比SHA1快,SHA1比MD5强度高。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import java.security.MessageDigest; /** * SHA使用例子 * 生成消息摘要 */ public class SHADemo { private static final char [] HEXES = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' }; /** * byte数组 转换成 16进制小写字符串 */ public static String bytes2Hex( byte [] bytes) { if (bytes == null || bytes.length == 0 ) { return null ; } StringBuilder hex = new StringBuilder(); for ( byte b : bytes) { hex.append(HEXES[(b >> 4 ) & 0x0F ]); hex.append(HEXES[b & 0x0F ]); } return hex.toString(); } public static void main(String[] args) throws Exception { // 加密字符 String src = "EasyJava" ; // 获取摘要 MessageDigest messageDigest = MessageDigest.getInstance( "SHA" ); messageDigest.update(src.getBytes( "utf-8" )); byte [] md5Result = messageDigest.digest(); System.out.println( "摘要信息:" + bytes2Hex(md5Result)); } } |
执行结果:
1 | 摘要信息:a59bcc9c6c15610a8fc0dc75a1551903f5583a90 |