Java加密解密之MAC(消息认证码)

转载于:https://blog.csdn.net/mn960mn/article/details/78174234

上一篇帖子,我们讲了消息摘要(数字摘要),它是把一个文本/文件 通过摘要函数(hash函数)计算出一个结果。然后把文本/文件和摘要结果一同发给接受者

接受者接收到文件之后,也进行摘要,把两个摘要结果进行对比。如果一致就说明文本/文件和摘要是一致的


但是,这里有个问题,假设A把文件和摘要发给B,中途被C截获了。C把文件改了,同时把改后的文件进行摘要。然后把改后的文件和重新生成的摘要发给B。

B收到结果之后,进行摘要,对比发现,是一致的。但是此时文件是被篡改过的,B也不知道。接收方并不能察觉到数据被篡改。


所以说,普通的消息摘要不能验证身份和防篡改


为了解决这个问题,我们可以使用MAC(消息认证码(带密钥的hash函数))去解决

MAC,全称 Message Authentication Code,也称为消息认证码(带密钥的Hash函数),通信实体双方使用的一种验证机制,保证消息数据完整性的一种工具

在发送数据之前,发送方首先使用通信双方协商好的散列函数计算其摘要值。在双方共享的会话密钥作用下,由摘要值获得消息验证码。之后,它和数据一起被发送。接收方收到报文后,首先利用会话密钥还原摘要值,同时利用散列函数在本地计算所收到数据的摘要值,并将这两个数据进行比对。若两者相等,则报文通过认证。


说白了就是计算摘要的时候,需要一个秘钥key,没有秘钥key就无法计算

注意:相同的消息,不同的key,摘要结果不同。


下面使用Java(1.8.0_144)演示计算apache-tomcat-8.5.23.zip文件的消息摘要

[java]  view plain  copy
  1. package com.security.dgst;  
  2.   
  3. import java.nio.file.Files;  
  4. import java.nio.file.Paths;  
  5. import java.security.Key;  
  6.   
  7. import javax.crypto.Mac;  
  8. import javax.crypto.spec.SecretKeySpec;  
  9.   
  10. import org.apache.commons.codec.binary.Hex;  
  11.   
  12. public class MacTest {  
  13.   
  14.     //秘钥(必须要是通信双方共享的)  
  15.     static final String STR_KEY = "266f5fe18e714688a083df4ca9f78064";  
  16.       
  17.     /** 
  18.      * 其中,Mac.getInstance支持的算法有:HmacMD5、HmacSHA1、HmacSHA256等等 
  19.      * 全部支持的算法见官方文档: 
  20.      * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac 
  21.      */  
  22.     public static byte[] mac(String algorithm, Key key, byte[] data) throws Exception {  
  23.         Mac mac = Mac.getInstance(algorithm);  
  24.         //这里是关键,需要一个key(这里就是和普通的消息摘要的区别点)  
  25.         mac.init(key);  
  26.           
  27.         byte[] result = mac.doFinal(data);  
  28.         return result;  
  29.     }  
  30.       
  31.     public static void main(String[] args) throws Exception {  
  32.         byte[] data = Files.readAllBytes(Paths.get("c:/tmp/apache-tomcat-8.5.23.zip"));  
  33.           
  34.         Key key = new SecretKeySpec(STR_KEY.getBytes(), "");  
  35.           
  36.         //使用MD5算法计算摘要  
  37.         byte[] md5Digest = mac("HmacMD5", key, data);  
  38.           
  39.         //使用SHA256算法计算摘要  
  40.         byte[] shaDigest = mac("HmacSHA256", key, data);  
  41.           
  42.         //把摘要后的结果转换成十六进制的字符串(也可以使用Base64进行编码)  
  43.         System.out.println(Hex.encodeHexString(md5Digest));  
  44.         System.out.println(Hex.encodeHexString(shaDigest));  
  45.     }  
  46. }  
输出结果为:

[plain]  view plain  copy
  1. ce078fe3134fa8b50c595e4e984f88e0  
  2. d90eec24b04b81cd235ff8d4e5a9aeb00183e253e44b6ed763328ff97f856200  

然后,我们可以使用OpenSSL,加上上面使用的秘钥key,计算摘要


对比结果,发现是一致的。


上面的,Mac.getInstance(algorithm)  参数algorithm可以支持的值除了参考官方文档,还可以通过如下代码得出

[java]  view plain  copy
  1. Security.getAlgorithms("Mac").forEach(System.out::println);  
在Java8中,输出结果如下:

[plain]  view plain  copy
  1. PBEWITHHMACSHA512  
  2. PBEWITHHMACSHA224  
  3. PBEWITHHMACSHA256  
  4. HMACSHA384  
  5. PBEWITHHMACSHA384  
  6. HMACSHA256  
  7. HMACPBESHA1  
  8. HMACSHA224  
  9. HMACMD5  
  10. PBEWITHHMACSHA1  
  11. SSLMACSHA1  
  12. HMACSHA512  
  13. SSLMACMD5  
  14. HMACSHA1  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中常用的密码加密方法有MD5、SHA、AES等,下面分别介绍它们的使用方法。 MD5加密: MD5是一种不可逆的加密方式,将明文密码转换成一串固定长度的字符串,一般用于存储密码的加密。Java中可以通过java.security.MessageDigest类实现MD5加密。示例代码如下: ```java import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Util { public static String md5(String password) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] bytes = md5.digest(password.getBytes()); StringBuilder stringBuilder = new StringBuilder(); for (byte b : bytes) { int temp = b & 0xff; String hexString = Integer.toHexString(temp); if (hexString.length() == 1) { stringBuilder.append("0").append(hexString); } else { stringBuilder.append(hexString); } } return stringBuilder.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } public static void main(String[] args) { String password = "123456"; String encryptedPassword = md5(password); System.out.println(encryptedPassword); } } ``` SHA加密: SHA也是一种不可逆的加密方式,与MD5类似,但SHA更安全。Java中可以通过java.security.MessageDigest类实现SHA加密。示例代码如下: ```java import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class SHAUtil { public static String sha(String password) { try { MessageDigest sha = MessageDigest.getInstance("SHA"); byte[] bytes = sha.digest(password.getBytes()); StringBuilder stringBuilder = new StringBuilder(); for (byte b : bytes) { int temp = b & 0xff; String hexString = Integer.toHexString(temp); if (hexString.length() == 1) { stringBuilder.append("0").append(hexString); } else { stringBuilder.append(hexString); } } return stringBuilder.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } public static void main(String[] args) { String password = "123456"; String encryptedPassword = sha(password); System.out.println(encryptedPassword); } } ``` AES加密: AES是一种对称加密方式,即加密和解密使用相同的密钥。Java中可以通过javax.crypto.Cipher类实现AES加密。示例代码如下: ```java import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; public class AESUtil { private static final String AES = "AES"; public static String encrypt(String content, String password) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance(AES); SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); secureRandom.setSeed(password.getBytes()); keyGenerator.init(128, secureRandom); SecretKey secretKey = keyGenerator.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, AES); Cipher cipher = Cipher.getInstance(AES);// 创建密码器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(byteContent); return Base64.getEncoder().encodeToString(result);// 加密 } catch (Exception e) { e.printStackTrace(); } return null; } public static String decrypt(String content, String password) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance(AES); SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); secureRandom.setSeed(password.getBytes()); keyGenerator.init(128, secureRandom); SecretKey secretKey = keyGenerator.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, AES); Cipher cipher = Cipher.getInstance(AES);// 创建密码器 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(Base64.getDecoder().decode(content)); return new String(result, "utf-8"); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { String content = "123456"; String password = "password"; String encryptedContent = encrypt(content, password); System.out.println("加密后:" + encryptedContent); String decryptedContent = decrypt(encryptedContent, password); System.out.println("解密后:" + decryptedContent); } } ``` Spring Boot中可以使用Spring Security提供的PasswordEncoder接口来实现密码加密和解密。示例代码如下: ```java import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; public class PasswordUtil { public static String encode(String password) { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder.encode(password); } public static boolean match(String password, String encodedPassword) { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder.matches(password, encodedPassword); } public static void main(String[] args) { String password = "123456"; String encodedPassword = encode(password); System.out.println("加密后:" + encodedPassword); boolean match = match(password, encodedPassword); System.out.println("匹配结果:" + match); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值