Java 加解密技术系列之 HMAC

  • 背景
  • 正文
  • 代码
  • 结束语

上一篇文章中简单的介绍了第二种单向加密算法 — —SHA,同时也给出了 SHA-1 的 Java 代码。有这方面需求的童鞋可以去参考一下。今天这篇文章将要介绍第三种单向加密算法
— — HMAC,其实,这种加密算法并不是那么常用,最起码,在我写系列博客之前,我是没有听说过它的。当然,这并不是说 HMAC 不出名,肯定是我孤落寡闻了。

背景

之所以在单向加密算法中介绍 HMAC 这种“不常见的”算法,一是因为“没见过”,二是因为毕竟是同属于单向加密算法中的一种,而且还是基于密钥的哈希算法的认证协议。因此,还是决定简单的介绍一下。

正文

HMAC,全称为“Hash Message Authentication Code”,中文名“散列消息鉴别码”,主要是利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。一般的,消息鉴别码用于验证传输于两个共 同享有一个密钥的单位之间的消息。HMAC 可以与任何迭代散列函数捆绑使用。MD5
和 SHA-1 就是这种散列函数。HMAC 还可以使用一个用于计算和确认消息鉴别值的密钥。
HMAC,
散列消息鉴别码,是基于密钥的 Hash
算法的认证协议。它的实现原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数
据块,即 MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。 
这种结构的主要作用是:
    并且源码是公开和通用的。

  • 可以保持散列函数原有的性能而不致使其退化。
  • 可以使得基于合理的关于底层散列函数假设的消息鉴别机制的加密强度分析 便于理解。
  • 当发现或需要运算速度更快或更安全的散列函数时,可以很容易的实现底层 散列函数的替换。
定义 HMAC 需要一个加密用散列函数(表示为 H)和一个密钥 K。我们假设 H 是 一个将数据块用一个基本的迭代压缩函数来加密的散列函数。我们用 B 来表示数据块的字长。(以上提到的散列函数的分割数据块字长
B = 64),用 L 来表示散列函数的输出数据字长(MD5中 L = 16 , SHA-1 中 L = 20)。鉴别密钥的长度可以是小于等于数据块字长的任何正整数值。应用程序中使用的密钥长度若是比 B 大,则首先用使用散列函数
H 作用于它,然后用 H 输出的 L 长度字符串作为在 HMAC 中实际使用的密钥。一般情况下,推荐的最小密钥 K 长度是 L 个字长。(与 H 的输出数据长度相等)。
我们将定义两个固定且不同的字符串 ipad,opad:(‘i’,‘o’表示内部与外部) 

    ipad = the byte 0x36 repeated B times

  • opad = the byte 0x5C repeated B times
    H (K XOR opad, H (K XOR ipad, text))
计算步骤

    字节,B=60 字节,则 K 后会加入 44 个零字节0x00)

  • 将上一步生成的 B 字长的字符串与 ipad 做异或运算
  • 将数据流 text 填充至第二步的结果字符串中
  • 用 H 作用于第三步生成的数据流
  • 将第一步生成的 B 字长字符串与 opad 做异或运算
  • 再将第四步的结果填充进第五步的结果中
  • 用 H 作用于第六步生成的数据流,输出最终结果
密钥
用于 HMAC 的密钥可以是任意长度(比 B 长的密钥将首先被 H 处理)。但当密钥 长度小于 L 时,会降低函数的安全强度。长度大于 L 的密钥也是可以的,但额外的长度并不能显著的提高函数的安全强度。
密钥必须随机选取(或使用强大的基于随机种子的伪随机生成方法),并且要周期性的更新。目前的攻击没有指出一个有效的更换密钥的频率,因为那些攻击实际上并不可行。然而,周期性更新密钥是一个对付函数和密钥所存在的潜在缺陷的基本的安全措施,并可以降低泄漏密钥带来的危害。

代码实现

import com.google.common.base.Strings;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder; import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException; /**
* Created by xiang.li on 2015/2/27.
*/
public class HMAC {
/**
* 定义加密方式
* MAC算法可选以下多种算法
* <pre>
* HmacMD5
* HmacSHA1
* HmacSHA256
* HmacSHA384
* HmacSHA512
* </pre>
*/
private final static String KEY_MAC = "HmacMD5"; /**
* 全局数组
*/
private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; /**
* 构造函数
*/
public HMAC() { } /**
* BASE64 加密
* @param key 需要加密的字节数组
* @return 字符串
* @throws Exception
*/
public static String encryptBase64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
} /**
* BASE64 解密
* @param key 需要解密的字符串
* @return 字节数组
* @throws Exception
*/
public static byte[] decryptBase64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
} /**
* 初始化HMAC密钥
* @return
*/
public static String init() {
SecretKey key;
String str = "";
try {
KeyGenerator generator = KeyGenerator.getInstance(KEY_MAC);
key = generator.generateKey();
str = encryptBase64(key.getEncoded());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return str;
} /**
* HMAC加密
* @param data 需要加密的字节数组
* @param key 密钥
* @return 字节数组
*/
public static byte[] encryptHMAC(byte[] data, String key) {
SecretKey secretKey;
byte[] bytes = null;
try {
secretKey = new SecretKeySpec(decryptBase64(key), KEY_MAC);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
bytes = mac.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return bytes;
} /**
* HMAC加密
* @param data 需要加密的字符串
* @param key 密钥
* @return 字符串
*/
public static String encryptHMAC(String data, String key) {
if (Strings.isNullOrEmpty(data)) {
return null;
}
byte[] bytes = encryptHMAC(data.getBytes(), key);
return byteArrayToHexString(bytes);
} /**
* 将一个字节转化成十六进制形式的字符串
* @param b 字节数组
* @return 字符串
*/
private static String byteToHexString(byte b) {
int ret = b;
//System.out.println("ret = " + ret);
if (ret < 0) {
ret += 256;
}
int m = ret / 16;
int n = ret % 16;
return hexDigits[m] + hexDigits[n];
} /**
* 转换字节数组为十六进制字符串
* @param bytes 字节数组
* @return 十六进制字符串
*/
private static String byteArrayToHexString(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
sb.append(byteToHexString(bytes[i]));
}
return sb.toString();
} /**
* 测试方法
* @param args
*/
public static void main(String[] args) throws Exception {
String key = HMAC.init();
System.out.println("Mac密钥:\n" + key);
String word = "123";
System.out.println(encryptHMAC(word, key));
}
}

结束语

看完这篇文章之
后,HMAC
你已经了解了很多了,以后再遇到这个名词,当然你也可以说出个一二三来。不过,在应用中,或许一般情况下用不到,如果考虑安全方面的因素,我想,这种不可
逆的加密算法还是不错的,因为你需要额外提供一组密钥,而这组密钥对于外人来说是不知道的,因此,安全性相比较来说还是很可靠的。

4.Java 加解密技术系列之 HMAC的更多相关文章

  1. Java 加解密技术系列文章

    Java 加解密技术系列之 总结 Java 加解密技术系列之 DH Java 加解密技术系列之 RSA Java 加解密技术系列之 PBE Java 加解密技术系列之 AES Java 加解密技术系列 ...

  2. 5.Java 加解密技术系列之 DES

    Java 加解密技术系列之 DES 序 背景 概念 基本原理 主要流程 分组模式 代码实现 结束语 序 前 几篇文章讲的都是单向加密算法,其中涉及到了 BASE64.MD5.SHA.HMAC 等几个比 ...

  3. 2.Java 加解密技术系列之 MD5

    Java 加解密技术系列之 MD5 序 背景 正文 结束语 序 上一篇文章中,介绍了最基础的编码方式 — — BASE64,也简单的提了一下编码的原理.这篇文章继续加解密的系列,当然也是介绍比较基础的 ...

  4. 11.Java 加解密技术系列之 总结

    Java 加解密技术系列之 总结 序 背景 分类 常用算法 原理 关于代码 结束语 序 上一篇文章中简单的介绍了第二种非对称加密算法 — — DH,这种算法也经常被叫做密钥交换协议,它主要是针对密钥的 ...

  5. 10.Java 加解密技术系列之 DH

    Java 加解密技术系列之 DH 序 概念 原理 代码实现 结果 结束语 序 上一篇文章中简单的介绍了一种非对称加密算法 — — RSA,今天这篇文章,继续介绍另一种非对称加密算法 — — DH.当然 ...

  6. 9.Java 加解密技术系列之 RSA

    Java 加解密技术系列之 RSA 序 概念 工作流程 RSA 代码实现 加解密结果 结束语 序 距 离上一次写博客感觉已经很长时间了,先吐槽一下,这个月以来,公司一直在加班,又是发版.上线,又是新项 ...

  7. 8.Java 加解密技术系列之 PBE

    Java 加解密技术系列之 PBE 序 概念 原理 代码实现 结束语 序 前 边的几篇文章,已经讲了几个对称加密的算法了,今天这篇文章再介绍最后一种对称加密算法 — — PBE,这种加密算法,对我的认 ...

  8. 7.java 加解密技术系列之 AES

    java 加解密技术系列之 AES 序 概念 原理 应用 代码实现 结束语 序 这篇文章继续介绍对称加密算法,至于今天的主角,不用说,也是个厉害的角色 — — AES.AES 的出现,就是为了来替代原 ...

  9. 6. Java 加解密技术系列之 3DES

    Java 加解密技术系列之 3DES 序 背景 概念 原理 代码实现 结束语 序 上一篇文章讲的是对称加密算法 — — DES,这篇文章打算在 DES 的基础上,继续多讲一点,也就是 3 重 DES ...

随机推荐

  1. 转接口IC GM7150BN/ GM7150BC:CVBS转BT656芯片 低功耗NTSC/PAL 视频解码器

    1 概述    GM7150 是一款9 位视频输入预处理芯片,该芯片采用CMOS 工艺,通过I2C 总线与PC 或DSP 相连构成应用系统.    它内部包含1 个模拟处理通道,能实现CVBS.S-V ...

  2. Gym 101257G 24 (概率+二分)

    题意: 有一道分值为sa的题,n个人比赛写这道题,按照递减的顺序给出每个人的当前分数,和每个人写不出这道题的概率,让你输出有反超现象出现的期望 思路:由于之前把题目翻译错了导致很久没有相通,后来看了别 ...

  3. 1.自定义控制器切换<一>

    一.自定义控制器切换:在同一个控制器上,展示不同的控制器,类似于tabbar一样 二.怎么做?(问题解决步骤) 1.创建若干控制器:OneViewController TwoViewControlle ...

  4. 使用IO流实现音频的剪切和拼接

    需求: 使用IO流将指定目录下的若干个音频文件的高潮部分,进行剪切,并重新拼接成一首新的音频文件 思路(以两首歌为例): 第一首歌有一个输入流对象bis1.第二首歌有一个输入流对象bis2,他们公用一 ...

  5. 关于苹果真机 getFullYear()返回值为NAN的问题

    问题描述: 在html页面中获得后台传过来的一个时间并显示在页面上,我用getFullYear() ,getMonth(),getDate()分别获得了年月日在电脑上和三星手机上页面都能正确的显示时间 ...

  6. 转账示例(二):service层面实现(本例采用QueryRunner来执行sql语句,数据源为C3P0)

    缺点:Service层面把Dao层面的开启事务操作完成了1.自行创建C3P0Uti,account数据库,导入Jar包 2.Dao层面 接口: package com.learning.dao; im ...

  7. selenium结合docker构建分布式测试环境

    selenium是目前web和app自动化测试的主要框架.对于web自动化测试而言,由于selenium2.0以后socker服务器由本地浏览器自己启动且直接通过浏览器原生API操作页面,故越来越多的 ...

  8. CSS border实现各个方向等腰直角三角

    CSS代码: .border_cort, .border_corr, .border_corb, .border_corl { display: inline-block; ; ; border-wi ...

  9. angular中路由的实现(针对新手)

    最近搜了一下网上的教程,看完总感觉有点糊涂,对于新手来说可能云里雾里,所以写一个最简单的路由来给大家做个指引. 首先当然需要准备angular,下载地址:https://angular.io/ 现在a ...

  10. redis skiplist (跳跃表)

    redis skiplist (跳跃表) 概述 redis skiplist 是有序的, 按照分值大小排序 节点中存储多个指向其他节点的指针 结构 zskiplist 结构 // 跳跃表 typede ...