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

计算‘text’的 HMAC:

  • H (K XOR opad, H (K XOR ipad, text))

计算步骤

  • 在密钥 K 后面添加 0 来创建一个子长为 B 的字符串。(例如,如果 K 的字长是 20 字节,B=60 字节,则 K 后会加入 44 个零字节0x00)
  • 将上一步生成的 B 字长的字符串与 ipad 做异或运算
  • 将数据流 text 填充至第二步的结果字符串中
  • 用 H 作用于第三步生成的数据流
  • 将第一步生成的 B 字长字符串与 opad 做异或运算
  • 再将第四步的结果填充进第五步的结果中
  • 用 H 作用于第六步生成的数据流,输出最终结果

密钥

用于 HMAC 的密钥可以是任意长度(比 B 长的密钥将首先被 H 处理)。但当密钥 长度小于 L 时,会降低函数的安全强度。长度大于 L 的密钥也是可以的,但额外的长度并不能显著的提高函数的安全强度。

密钥必须随机选取(或使用强大的基于随机种子的伪随机生成方法),并且要周期性的更新。目前的攻击没有指出一个有效的更换密钥的频率,因为那些攻击实际上并不可行。然而,周期性更新密钥是一个对付函数和密钥所存在的潜在缺陷的基本的安全措施,并可以降低泄漏密钥带来的危害。

代码实现

<span style="font-family:Comic Sans MS;font-size:12px;">package com.sica.hmac;

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));
    }
}</span>

结束语

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

时间: 2024-10-15 02:25:26

Java 加解密技术系列之 HMAC的相关文章

4.Java 加解密技术系列之 HMAC

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

Java 加解密技术系列文章

Java 加解密技术系列之 总结 Java 加解密技术系列之 DH Java 加解密技术系列之 RSA Java 加解密技术系列之 PBE Java 加解密技术系列之 AES Java 加解密技术系列之 3DES Java 加解密技术系列之 DES Java 加解密技术系列之 HMAC Java 加解密技术系列之 SHA Java 加解密技术系列之 MD5 Java 加解密技术系列之 BASE64

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

Java 加解密技术系列之 DES 序 背景 概念 基本原理 主要流程 分组模式 代码实现 结束语 序 前 几篇文章讲的都是单向加密算法,其中涉及到了 BASE64.MD5.SHA.HMAC 等几个比较常见的加解密算法.这篇文章,以及后面几篇,打算介绍几个对称加密算法,比如:DES.3DES(TripleDES).AES 等.那么,这篇文章主要是对 DES 大概讲一下. 背景 在 讨论 DES 之前,首先了解一下什么是对称加密算法吧.对于对称加密算法,他应用的时间比较早,技术相对来说比较成熟,在

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

Java 加解密技术系列之 DH 序 概念 原理 代码实现 结果 结束语 序 上一篇文章中简单的介绍了一种非对称加密算法 — — RSA,今天这篇文章,继续介绍另一种非对称加密算法 — — DH.当然,可能有很多人对这种加密算法并不是很熟悉,不过没关系,希望今天这篇文章能帮助你熟悉他. 原理 整个通信过程中g.g^a.g^b是公开的,但由于g.a.b都是整数,通过g和g^a得到a还是比较容易的,b也是如此,所以最终的“密钥”g^(a*b)还是可以被计算出来的.所以实际的过程还需要在基本原理上加入

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

Java 加解密技术系列之 PBE 序 概念 原理 代码实现 结束语 序 前 边的几篇文章,已经讲了几个对称加密的算法了,今天这篇文章再介绍最后一种对称加密算法 — — PBE,这种加密算法,对我的认知来说,并没有 DES.3DES.AES 那么流行,也不尽然,其实是我之前并没有这方面的需求,当然接触他的机会也就很少了,因此,可想而知,没听过显然在正常不过了. 概念 PBE,全称为“Password Base Encryption”,中文名“基于口令加密”,是一种基于密码的加密算法,其特点是使用

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

Java 加解密技术系列之 总结 序 背景 分类 常用算法 原理 关于代码 结束语 序 上一篇文章中简单的介绍了第二种非对称加密算法 — — DH,这种算法也经常被叫做密钥交换协议,它主要是针对密钥的保护.同时,由于水平的限制,打算这个系列就到此为止了,这篇文章就算是一个总结吧,回顾一下这几个月来都写了些什么. 背景 其 实,在开始写这个系列之前,我对于 Java 的加解密也并不是那么了解.之所以要写这些文章,还主要是由于工作的原因.记得几个月以前,当时项目要做一个数字证书,证书的生成.存储.传

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

Java 加解密技术系列之 3DES 序 背景 概念 原理 代码实现 结束语 序 上一篇文章讲的是对称加密算法 — — DES,这篇文章打算在 DES 的基础上,继续多讲一点,也就是 3 重 DES — — Triple DES. 背景 至于 3DES 为什么会出现呢?其实,这个不难想到.由于 DES 是一种非常简便的加密算法,但是密钥长度比较短,计算量比较小,相对来说,比较容易被破解.因此,在 DES 的基础上,使用三重数据加密算法,对数据进行加密,这样来说,破解的概率就小了很多. 概念 3D

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

Java 加解密技术系列之 RSA 序 概念 工作流程 RSA 代码实现 加解密结果 结束语 序 距 离上一次写博客感觉已经很长时间了,先吐槽一下,这个月以来,公司一直在加班,又是发版.上线,又是新项目太紧,具体的就不多说了,想听我吐槽的小伙伴, 可以私信给我(*^__^*) .上一篇文章,已经把对称加密的算法讲完了.从今天开始,要说说非对称加密了.因为,非对称加密真的是太重要了,我们的日常生活中,都离不开非对称加密. 概念 在说 RSA 之前,首先聊聊什么是非对称加密.在讲对称加密的时候,就曾

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

java 加解密技术系列之 AES 序 概念 原理 应用 代码实现 结束语 序 这篇文章继续介绍对称加密算法,至于今天的主角,不用说,也是个厉害的角色 — — AES.AES 的出现,就是为了来替代原先的 DES 标准.现在来说,AES 的用途还是非常广泛的. 概念 AES, 全称为“Advanced Encryption Standard”,中文名“高级加密标准”,在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准.AES 加密算法作为新一代的数据加密标准汇聚了强安