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. Centos安装MySql、Java、Tomcat

    一.安装MySql 安装MySql yum install -y mysql-server mysql mysql-devel 启动MySql服务 service mysqld start 为root ...

  2. Git基本操作命令2

    git本地仓库同步到远程仓库上 $ git remote add origin http://github.com/powfulCsharp/NuUserControl.git $ git push ...

  3. CSS 画三角形、圆

    <div class="square"></div> <style> .square { height: 0px; width: 0px; bo ...

  4. List分组 用于客服对话分组场景

    工作用可能会用到会话分组: Message是消息实体对象,里面有toId和fromId 指明接收方ID和发送方Id,通过组合形式"12-22-" 为map的key public M ...

  5. .NET遇上Docker - 使用Docker Compose组织Ngnix和.NETCore运行

    本文工具准备: Docker for Windows Visual Studio 2015 与 Visual Studio Tools for Docker 或 Visual Studio 2017 ...

  6. SQL Server函数​---Union与Union All的区别

    SQL Server函数---Union与Union All的区别 如果我们需要将两个select语句的结果作为一个整体显示出来,我们就需要用到union或者union all关键字.union(或称 ...

  7. linux命令之crontab定时执行任务

    一.crond简介 crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动c ...

  8. python入门编程之基础

    Python, 是一种面向对象.解释型计算机程序设计语言.Python语法简洁清晰,特色之一是强制用空白符作为语句缩进.Python的设计哲学是"优雅"."明确" ...

  9. Android Gradle 指定 Module 打包

    Android Gradle 指定 Module 打包 项目中有许多的可以直接独立运行的 Module ,如何在 Gradle 中将签名文件配置好了,那么就不需要普通的手动点击 Generate Si ...

  10. GitHub上非常受开发者欢迎的iOS开源项目(二)

    "每一次的改变总意味着新的开始."这句话用在iOS上可谓是再合适不过的了.GitHub上的iOS开源项目数不胜数,iOS每一次的改变,总会引发iOS开源项目的演变,从iOS 1.x ...