zoukankan      html  css  js  c++  java
  • 第五章 消息摘要算法--MAC

    注意:本节内容主要参考自《Java加密与解密的艺术(第2版)》第6章“验证数据完整性--消息摘要算法”

    5.1、mac(又称为Hmac)

    原理:在md与sha系列算法的基础上加入了密钥,是三大常用的消息摘要算法中最安全的一个。

    常用的mac算法:

    • HmacMD5
    • HmacSHA1
    • HmacSHA256

    5.2、实现方式

    • JDK(缺少二进制字节数组转十六进制的工具,可借助CC或BC的工具类完成)
    • Commons Codec(CC,在1.10版本中加入的,其中,产生相应算法密钥的API没有找到,如果有人找到了,请和我讲一下,谢谢
    • Bouncy Castle(BC,比较麻烦,具体参考“慕课网”)

    5.2.1、基于JDK实现的Hmac系列算法

     1 package com.util.mac;
     2 
     3 import java.io.UnsupportedEncodingException;
     4 import java.security.InvalidKeyException;
     5 import java.security.NoSuchAlgorithmException;
     6 
     7 import javax.crypto.KeyGenerator;
     8 import javax.crypto.Mac;
     9 import javax.crypto.SecretKey;
    10 import javax.crypto.spec.SecretKeySpec;
    11 
    12 import org.bouncycastle.util.encoders.Hex;
    13 
    14 /**
    15  * 基于JDK的HmacMD5算法
    16  */
    17 public class HmacMD5JDK {
    18     private static final String ENCODING = "UTF-8";
    19     private static final String ALGORITHM = "HmacMD5";//指定具体算法HmacMD5,HmacSHA1,HmacSHA256
    20     
    21     /**
    22      * 产生密钥两种方式 1)是由jdk自己来产生的,2)我们可以自己指定一个字节数组
    23      * 注意:密钥是以二进制字节数组存储的
    24      */
    25     public static byte[] getKey() throws NoSuchAlgorithmException{
    26         SecretKey key = KeyGenerator.getInstance(ALGORITHM).generateKey();
    27         return key.getEncoded();
    28     }
    29     
    30     /**
    31      * HmacMD5加密
    32      * @param data     带加密数据
    33      * @param keyByte  密钥
    34      */
    35     public static byte[] encode(String data, byte[] keyByte) throws NoSuchAlgorithmException, 
    36                                                                     InvalidKeyException, 
    37                                                                     IllegalStateException, 
    38                                                                     UnsupportedEncodingException {
    39         SecretKey key = new SecretKeySpec(keyByte, ALGORITHM);//还原密钥
    40         Mac mac = Mac.getInstance(key.getAlgorithm());
    41         mac.init(key);//为mac实例初始化密钥
    42         return mac.doFinal(data.getBytes(ENCODING));
    43     }
    44     
    45     /**
    46      * HmacMD5加密,并转为16进制
    47      */
    48     public static String encodeHmacMD5Hex(String data, byte[] keyByte) throws NoSuchAlgorithmException, 
    49                                                                               UnsupportedEncodingException, 
    50                                                                               InvalidKeyException, 
    51                                                                               IllegalStateException {
    52         byte[] encodedByte = encode(data, keyByte);
    53         return new String(Hex.encode(encodedByte));//借助BC
    54         //return new String(org.apache.commons.codec.binary.Hex.encodeHexString(encodedByte));//借助CC
    55     }
    56     
    57     /**
    58      * 测试
    59      * @throws IllegalStateException 
    60      * @throws InvalidKeyException 
    61      */
    62     public static void main(String[] args) throws UnsupportedEncodingException, 
    63                                                   NoSuchAlgorithmException, 
    64                                                   InvalidKeyException, 
    65                                                   IllegalStateException {
    66         String data = "找一个好姑娘做老婆是我的梦 想!";
    67         /*************测试encode()**************/
    68         System.out.println("原文-->"+data);
    69         byte[] keyByte = HmacMD5JDK.getKey(); 
    70         byte[] encodedByte = HmacMD5JDK.encode(data, keyByte);
    71         System.out.println("加密后-->"+encodedByte);
    72         byte[] encodedByte2 = HmacMD5JDK.encode(data, keyByte);
    73         System.out.println("加密后-->"+encodedByte2);
    74         for(int i=0;i<encodedByte.length;i++){
    75             System.out.println(encodedByte[i]==encodedByte2[i]);
    76         }
    77         /*************测试encodeHmacMD5Hex()**************/
    78         System.out.println("原文-->"+data);
    79         String encodedStr = HmacMD5JDK.encodeHmacMD5Hex(data, keyByte);
    80         System.out.println("加密后-->"+encodedStr);
    81         String encodedStr2 = HmacMD5JDK.encodeHmacMD5Hex(data, keyByte);
    82         System.out.println("加密后-->"+encodedStr2);
    83         System.out.println(encodedStr.equals(encodedStr2));
    84     }
    85 }

    注意几点:

    • 产生密钥两种方式:1)直接使用JDK的类(如上边代码所示)2)自己指定字节数组(参考5.2.1)
    • 密钥是一个二进制数组,当然为了提高可读性,可以使用Base64加密后,在传递可对方
    • 在实际使用中,我们可以将密钥产生后,发送者通过安全途径(线下传递等)传给接收方。
    • 在上述的测试中,去测一下同一个消息在使用同一个密钥的情况下,多次mac后结果是否相同。
    • 想切换算法,只需要修改ALGORITHM常数即可,当然如果在实际项目中需要用到多种算法,并且需要实现平滑切换,可以采用策略模式,这个以后会再讲。

    5.2.1、基于CC实现的Hmac系列算法

     1 package com.util.mac;
     2 
     3 import java.io.UnsupportedEncodingException;
     4 import java.security.InvalidKeyException;
     5 import java.security.NoSuchAlgorithmException;
     6 
     7 import org.apache.commons.codec.DecoderException;
     8 import org.apache.commons.codec.binary.Hex;
     9 import org.apache.commons.codec.digest.HmacUtils;
    10 
    11 /**
    12  * 基于CC的HmacMD5算法
    13  */
    14 public class HmacMD5CC {
    15     private static final String ENCODING = "UTF-8";
    16     /**
    17      * 产生密钥
    18      */
    19     public static byte[] getKey() throws NoSuchAlgorithmException, DecoderException{
    20         return Hex.decodeHex(new char[]{'a','b','c','d'}); 
    21     }
    22     
    23     /**
    24      * HmacMD5加密
    25      * @param data     带加密数据
    26      * @param keyByte  密钥
    27      */
    28     public static byte[] encode(String data, byte[] keyByte) throws NoSuchAlgorithmException, 
    29                                                                     InvalidKeyException, 
    30                                                                     IllegalStateException, 
    31                                                                     UnsupportedEncodingException {
    32         return HmacUtils.hmacMd5(keyByte, data.getBytes(ENCODING));
    33     }
    34     
    35     /**
    36      * HmacMD5加密,并转为16进制
    37      */
    38     public static String encodeHmacMD5Hex(String data, byte[] keyByte) throws NoSuchAlgorithmException, 
    39                                                                               UnsupportedEncodingException, 
    40                                                                               InvalidKeyException, 
    41                                                                               IllegalStateException {
    42         return HmacUtils.hmacMd5Hex(keyByte, data.getBytes(ENCODING));
    43     }
    44     
    45     /**
    46      * 测试
    47      * @throws IllegalStateException 
    48      * @throws InvalidKeyException 
    49      * @throws DecoderException 
    50      */
    51     public static void main(String[] args) throws UnsupportedEncodingException, 
    52                                                   NoSuchAlgorithmException, 
    53                                                   InvalidKeyException, 
    54                                                   IllegalStateException, 
    55                                                   DecoderException {
    56         String data = "找一个好姑娘做老婆是我的梦 想!";
    57         /*************测试encode()**************/
    58         System.out.println("原文-->"+data);
    59         byte[] keyByte = HmacMD5CC.getKey(); 
    60         byte[] encodedByte = HmacMD5CC.encode(data, keyByte);
    61         System.out.println("加密后-->"+encodedByte);
    62         byte[] encodedByte2 = HmacMD5CC.encode(data, keyByte);
    63         System.out.println("加密后-->"+encodedByte2);
    64         for(int i=0;i<encodedByte.length;i++){
    65             System.out.println(encodedByte[i]==encodedByte2[i]);
    66         }
    67         /*************测试encodeHmacMD5Hex()**************/
    68         System.out.println("原文-->"+data);
    69         String encodedStr = HmacMD5CC.encodeHmacMD5Hex(data, keyByte);
    70         System.out.println("加密后-->"+encodedStr);
    71         String encodedStr2 = HmacMD5CC.encodeHmacMD5Hex(data, keyByte);
    72         System.out.println("加密后-->"+encodedStr2);
    73         System.out.println(encodedStr.equals(encodedStr2));
    74     }
    75 }

    注意:

    • CC很好的封装了jdk的底层,但是CC是在1.10版本中才添加了hmac系列算法
    • 在使用中,若想切换其他算法,只需要调用不同的方法即可,具体的查看本文最下边的CC1.10的API文档链接地址
    • 其中,生成密钥在这里是自己指定了一个字节数组,具体的有CC来产生相应算法的密钥的API并没有看到,如果需要,可以自己去封装,当然,如果在CC中有相应的API,请大家和我讲一下,谢谢!

    CC API文档:

    http://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/HmacUtils.html

     

  • 相关阅读:
    单用户模式启动SQL Server实例总结
    MySQL下perror工具查看System Error Code信息
    ERROR 1050 (42S01): Table xxx already exists
    RMAN-06172 Troubleshooting
    [翻译]LVM中逻辑卷的最大大小限制
    如何定位那些SQL产生了大量的redo日志
    MySQL的自动提交模式
    MySQL服务读取参数文件my.cnf的规律研究探索
    SQL Server等待事件—RESOURCE_SEMAPHORE_QUERY_COMPILE
    Azure SQL Virtual Machine报Login failed for user 'NT ServiceSqlIaaSExtension'. Reason: Could not find a login matching the name provided
  • 原文地址:https://www.cnblogs.com/java-zhao/p/5085347.html
Copyright © 2011-2022 走看看