配置Maven依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId> <!-- 提供Hex、DigestUtils、Base64等 -->
<version>1.11</version>
</dependency>
URL编码
System.out.println(URLEncoder.encode("原文", "UTF-8"));
应用:要放在url里的值,需要URL编码
摘要 MD5
MessageDigest md = MessageDigest.getInstance("MD5"); // JDK提供的MD5方法
byte[] md5Bytes = md.digest("原文".getBytes()); // 加密字节序列 得到 128个比特的摘要,即16字节
System.out.println(Hex.encodeHex(md5Bytes)); // 每4个比特转成一位16进制数
System.out.println( DigestUtils.md5Hex("原文")); // Apache common codec 提供的MD5方法,最终调用的还是JDK
应用:用户密码加密
摘要 SHA
SHA-1(160比特)、SHA2(SHA-256、SHA-384、SHA-512)
MessageDigest md = MessageDigest.getInstance("SHA-256"); // JDK提供的SHA方法
byte[] md5Bytes = md.digest("原文".getBytes());
System.out.println(Hex.encodeHex(md5Bytes));
System.out.println( DigestUtils.sha256Hex("原文")); // Apache common codec 提供的SHA方法,最终调用的还是JDK
应用:数字签名
带有密钥的摘要 Hmac
SecretKey sk = KeyGenerator.getInstance("HmacMD5").generateKey(); // 得到密钥,通信双方共享这个密钥
SecretKey rsk = new SecretKeySpec(sk.getEncoded(), "HmacMD5"); // 格式化密钥
Mac mac = Mac.getInstance("HmacMD5"); // 确定算法
mac.init(rsk); // 确定密钥
byte[] digest = mac.doFinal("原文".getBytes()); // 加密
System.out.println(Hex.encodeHexString(digest));
相当于对摘要进行对称加密,通信双方共享这个对称密钥;或者,相当于对原文加盐再摘要,通信双方共享这个盐;从而能识别原文是否被篡改
适用场景:摘要的原文公开的场景
应用:会话认证MAC
对称密码 之 Base64编码
每6比特转成一位64进制数
BASE64Encoder encoder = new BASE64Encoder(); // JDK提供
String enStr = encoder.encode("原文".getBytes()); // 编码
enStr = enStr.replaceAll("[\s\t\n\r]", ""); // 去掉可能有的换行符
System.out.println(enStr);
BASE64Decoder decoder = new BASE64Decoder();
System.out.println(new String(decoder.decodeBuffer(enStr))); // 解码
byte[] result = Base64.encodeBase64("原文".getBytes()); // Apache common codec提供,不会带换行符
String enStr = new String(result); // 编码
enStr = enStr.replaceAll("=", ""); // 去掉末尾可能有的等号
System.out.println(new String(Base64.decodeBase64(result))); // 解码
应用:字节数组对应的字符串是乱码时,可以进行Base64编码,使得能显示成简单的字符串
对称密码之 AES
// 生成key
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128); // 确定密钥长度
byte[] keyBytes = kg.generateKey().getEncoded(); // 可以引入已由的密钥,Base64.decodeBase64(aesKeyString);
// 格式化key
Key key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // 确定算法
cipher.init(Cipher.ENCRYPT_MODE, key); // 确定密钥,进入加密模式
byte[] result = cipher.doFinal("原文".getBytes()); // 加密
System.out.println(Base64.encodeBase64String(result)); // 不进行Base64编码的话,那么这个字节数组对应的字符串就是乱码
cipher.init(Cipher.DECRYPT_MODE, key); // 确定密钥,进入解密模式
System.out.println(new String(cipher.doFinal(result))); // 解密
加盐散列 BCrypt
new BCryptPasswordEncoder().encode("原文")
往明文里加入盐(随机数),再进行散列,盐与密文保存在一起
应用:用户设置密码时,对用户密码进行加盐散列,服务端保存密码密文和盐。用户登录时,取出盐,对用户提交的密码进行加盐散列,和数据库里的密码密文对比。
加盐目的:不加盐的情况下,用户设置的密码常见,那么其散列值也常见,攻击者拿到密码密文后,可以通过撞库得知其明文。加盐的情况下,攻击者拿到盐和密码密文,由于密码密文不常见,无法撞库。
对称加密之 基于口令的密码PBE
String pwd = "hogen"; // 口令
PBEKeySpec keySpec = new PBEKeySpec(pwd.toCharArray()); // 密钥格式化
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); // 密钥工厂
Key key = factory.generateSecret(keySpec);
SecureRandom random = new SecureRandom(); // 强随机数生成器
byte[] salt = random.generateSeed(8); // 盐
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 100); // PBE参数格式化
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES"); // 确定算法
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); // 确定口令 和 盐
byte[] result = cipher.doFinal("原文".getBytes()); // 加密
System.out.println(Base64.encodeBase64String(result));
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); // 进入解密模式
System.out.println(new String(cipher.doFinal(result))); // 解密
应用:用户保管口令,服务端保存盐和密文,即可实现只有用户的口令才能解密敏感数据;加盐是为了复杂化口令,否则一个拿到密文的攻击者,容易猜出简单的口令
非对称加密 RSA
以下示例为私钥加密,公钥解密
// 生成密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512); // 能加密明文字节数为 512/8-11=53
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
// 格式化私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); // 引入已有的私钥 可以用 传入 Base64.decodeBase64(privateKeyString)
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // 确定算法
cipher.init(Cipher.ENCRYPT_MODE, privateKey); // 确定加密密钥
byte[] result = cipher.doFinal("原文".getBytes()); // 加密
System.out.println(Base64.encodeBase64String(result));
// 格式化公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); // 引入已有的公钥 可以用 传入 Base64.decodeBase64(publicKeyString)
keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // 确定算法
cipher.init(Cipher.DECRYPT_MODE, publicKey); // 确定公钥
System.out.println(new String(cipher.doFinal(result))); // 解密
阿里云
加密服务:即一台专门用于加解密的虚拟机