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. BloomFilter算法

    Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员.如果检测结果为是,该元素不一定 ...

  2. 6.Java集合总结系列:常见集合类的使用(List/Set/Map)

    MARK 总结: Set.List 和 Map 可以看做集合的三大类. List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问. Set集合是无序集合,集合中的元素不可 ...

  3. Framework7初始化

    /** * Framework7初始化 * 需引用jQuery */ /*---------------------------函数---------------------------*/ /** ...

  4. 新人小达之wpf

    Wpf学习之路-- 第一次写微博,可能内容不够精细,但目的就是把问题讲明白,让看到文章的小伙伴们少走弯路. 由于公司的需要,需要学习.net的一门新技术-wpf. 要说wpf是什么框架?模式?架构?  ...

  5. html静态页面实现微信分享思路

    微信分享网页的时候,希望分享出来的链接是标题+描述+缩略图,微信开发代码示例里已提供了方法,但只适用于动态页面.由于dedecms是生成了静态文件,其实我想使用ajax获取jssdk参数也能也能实现微 ...

  6. 什么是NoSQL

    NoSQL = Not Only SQL 不仅仅是SQL NoSQL,指的是非关系型的数据库(没有声明性查询语言,没有预定义的模式,可以为键 - 值对存储,列存储,文档存储,图形数据库).不同于传统的 ...

  7. C字符串输入输出函数

    下面就几个常用的字符串输入输出函数做个小小的总结TAT 使用时添加头文件:#include<stdio.h>. scanf("格式控制字符串",变量地址列表):(pri ...

  8. 【转载】rem自适应布局-移动端自适应必备

    原文链接:rem自适应布局-移动端自适应必备 版权所有,转载时请注明出处,违者必究. 由于移动端特殊性,本文讲的是如何使用rem实现自适应,或叫rem响应式布局,通过使用一个脚本就可以rem自适应,不 ...

  9. NodeJS+express+mogondb学习笔记01

    0.准备工作  安装nodejs环境  官网地址:https://nodejs.org/en/  下载好了 直接一路安装 也没有什么可以说的 不得不说nodejs对于新手上手还是很友好的,再加上现在n ...

  10. NODEJS环境搭建 第一篇 安装和部署NODEJS

    一.下载安装文件 根据自己当前系统环境,下载相对应的安装文件 https://nodejs.org/en/download/ 二.双击安装 都傻瓜式的安装步骤,一步一步安装就好了. 三.检查安装结果 ...