概述

Hutool-crypto概述

加密分为三种:

  1. 对称加密(symmetric),例如:AES、DES等
  2. 非对称加密(asymmetric),例如:RSA、DSA等
  3. 摘要加密(digest),例如:MD5、SHA-1、SHA-256、HMAC等

hutool-crypto针对这三种加密类型分别封装,并提供常用的大部分加密算法。
对于非对称加密,实现了:

  • RSA
  • DSA

对于对称加密,实现了:

  • AES
  • ARCFOUR
  • Blowfish
  • DES
  • DESede
  • RC2
  • PBEWithMD5AndDES
  • PBEWithSHA1AndDESede
  • PBEWithSHA1AndRC2_40

对于摘要算法实现了:

  • MD2
  • MD5
  • SHA-1
  • SHA-256
  • SHA-384
  • SHA-512
  • HmacMD5
  • HmacSHA1
  • HmacSHA256
  • HmacSHA384
  • HmacSHA512

其中,针对常用到的算法,模块还提供SecureUtil工具类用于快速实现加密。

加密解密工具-SecureUtil

介绍

SecureUtil主要针对常用加密算法构建快捷方式,还有提供一些密钥生成的快捷工具方法。

方法介绍

对称加密

  • SecureUtil.aes
  • SecureUtil.des

    摘要算法

  • SecureUtil.md5

  • SecureUtil.sha1
  • SecureUtil.hmac
  • SecureUtil.hmacMd5
  • SecureUtil.hmacSha1

    非对称加密

  • SecureUtil.rsa

  • SecureUtil.dsa

    UUID

  • SecureUtil.simpleUUID 方法提供无“-”的UUID

    密钥生成

  • SecureUtil.generateKey 针对对称加密生成密钥

  • SecureUtil.generateKeyPair 生成密钥对(用于非对称加密)
  • SecureUtil.generateSignature 生成签名(用于非对称加密)

其它方法为针对特定加密方法的一些密钥生成和签名相关方法,详细请参阅API文档。

对称加密-SymmetricCrypto

介绍

对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。而在大多数的对称算法中,加密密钥和解密密钥是相同的,所以也称这种加密算法为秘密密钥算法或单密钥算法。它要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要。
对于对称加密,封装了JDK的,具体介绍见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyGenerator

  • AES (默认AES/ECB/PKCS5Padding)
  • ARCFOUR
  • Blowfish
  • DES (默认DES/ECB/PKCS5Padding)
  • DESede
  • RC2
  • PBEWithMD5AndDES
  • PBEWithSHA1AndDESede
  • PBEWithSHA1AndRC2_40

    使用

    通用使用

    以AES算法为例:
    1. String content = "test中文";
    2. //随机生成密钥
    3. byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
    4. //构建
    5. SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
    6. //加密
    7. byte[] encrypt = aes.encrypt(content);
    8. //解密
    9. byte[] decrypt = aes.decrypt(encrypt);
    10. //加密为16进制表示
    11. String encryptHex = aes.encryptHex(content);
    12. //解密为字符串
    13. String decryptStr = aes.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);

    DESede实现

    1. String content = "test中文";
    2. byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DESede.getValue()).getEncoded();
    3. SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DESede, key);
    4. //加密
    5. byte[] encrypt = des.encrypt(content);
    6. //解密
    7. byte[] decrypt = des.decrypt(encrypt);
    8. //加密为16进制字符串(Hex表示)
    9. String encryptHex = des.encryptHex(content);
    10. //解密为字符串
    11. String decryptStr = des.decryptStr(encryptHex);

    AES封装

    AES全称高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法。
    对于Java中AES的默认模式是:AES/ECB/PKCS5Padding,如果使用CryptoJS,请调整为:padding: CryptoJS.pad.Pkcs7
  1. 快速构建

    1. String content = "test中文";
    2. // 随机生成密钥
    3. byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
    4. // 构建
    5. AES aes = SecureUtil.aes(key);
    6. // 加密
    7. byte[] encrypt = aes.encrypt(content);
    8. // 解密
    9. byte[] decrypt = aes.decrypt(encrypt);
    10. // 加密为16进制表示
    11. String encryptHex = aes.encryptHex(content);
    12. // 解密为字符串
    13. String decryptStr = aes.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);
  2. 自定义模式和偏移

    1. AES aes = new AES(Mode.CTS, Padding.PKCS5Padding, "0CoJUm6Qyw8W8jud".getBytes(),
    2. "0102030405060708".getBytes());

    DES封装

    DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,Java中默认实现为:DES/CBC/PKCS5Padding
    DES使用方法与AES一致,构建方法为:

  3. 快速构建

    1. byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue()).getEncoded();
    2. DES des = SecureUtil.des(key);
  4. 自定义模式和偏移

    1. DES des = new DES(Mode.CTS, Padding.PKCS5Padding, "0CoJUm6Qyw8W8jud".getBytes(),
    2. "01020304".getBytes());

    SM4

    在4.2.1之后,Hutool借助Bouncy Castle库可以支持国密算法,以SM4为例:
    我们首先需要引入Bouncy Castle库:

    1. <dependency>
    2. <groupId>org.bouncycastle</groupId>
    3. <artifactId>bcpkix-jdk15on</artifactId>
    4. <version>1.60</version>
    5. </dependency>

    然后可以调用SM4算法,调用方法与其它算法一致:

    1. String content = "test中文";
    2. SymmetricCrypto sm4 = new SymmetricCrypto("SM4");
    3. String encryptHex = sm4.encryptHex(content);
    4. String decryptStr = sm4.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);//test中文

    同样我们可以指定加密模式和偏移:

    1. String content = "test中文";
    2. SymmetricCrypto sm4 = new SymmetricCrypto("SM4/ECB/PKCS5Padding");
    3. String encryptHex = sm4.encryptHex(content);
    4. String decryptStr = sm4.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);//test中文

    非对称加密-AsymmetricCrypto

介绍

对于非对称加密,最常用的就是RSA和DSA,在Hutool中使用AsymmetricCrypto对象来负责加密解密。
非对称加密有公钥和私钥两个概念,私钥自己拥有,不能给别人,公钥公开。根据应用的不同,我们可以选择使用不同的密钥加密:

  1. 签名:使用私钥加密,公钥解密。用于让所有公钥所有者验证私钥所有者的身份并且用来防止私钥所有者发布的内容被篡改,但是不用来保证内容不被他人获得。
  2. 加密:用公钥加密,私钥解密。用于向公钥所有者发布信息,这个信息可能被他人篡改,但是无法被他人获得。

Hutool封装了JDK的,详细见https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator

  • RSA
  • DSA
  • EC

    使用

    在非对称加密中,我们可以通过AsymmetricCrypto(AsymmetricAlgorithm algorithm)构造方法,通过传入不同的算法枚举,获得其加密解密器。
    当然,为了方便,我们针对最常用的RSA和DSA算法构建了单独的对象:RSADSA

    基本使用

    我们以RSA为例,介绍使用RSA加密和解密 在构建RSA对象时,可以传入公钥或私钥,当使用无参构造方法时,Hutool将自动生成随机的公钥私钥密钥对:
    1. RSA rsa = new RSA();
    2. //获得私钥
    3. rsa.getPrivateKey()
    4. rsa.getPrivateKeyBase64()
    5. //获得公钥
    6. rsa.getPublicKey()
    7. rsa.getPublicKeyBase64()
    8. //公钥加密,私钥解密
    9. byte[] encrypt = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
    10. byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
    11. //Junit单元测试
    12. //Assert.assertEquals("我是一段测试aaaa", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
    13. //私钥加密,公钥解密
    14. byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
    15. byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);
    16. //Junit单元测试
    17. //Assert.assertEquals("我是一段测试aaaa", StrUtil.str(decrypt2, CharsetUtil.CHARSET_UTF_8));

    对于加密和解密可以完全分开,对于RSA对象,如果只使用公钥或私钥,另一个参数可以为null

自助生成密钥对

有时候我们想自助生成密钥对可以:

  1. KeyPair pair = SecureUtil.generateKeyPair("RSA");
  2. pair.getPrivate();
  3. pair.getPublic();

自助生成的密钥对是byte[]形式,我们可以使用Base64.encode方法转为Base64,便于存储为文本。
当然,如果使用RSA对象,也可以使用encryptStrdecryptStr加密解密为字符串。

案例

案例一:

已知私钥和密文,如何解密密文?

  1. String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIL7pbQ+5KKGYRhw7jE31hmA"
  2. + "f8Q60ybd+xZuRmuO5kOFBRqXGxKTQ9TfQI+aMW+0lw/kibKzaD/EKV91107xE384qOy6IcuBfaR5lv39OcoqNZ"
  3. + "5l+Dah5ABGnVkBP9fKOFhPgghBknTRo0/rZFGI6Q1UHXb+4atP++LNFlDymJcPAgMBAAECgYBammGb1alndta"
  4. + "xBmTtLLdveoBmp14p04D8mhkiC33iFKBcLUvvxGg2Vpuc+cbagyu/NZG+R/WDrlgEDUp6861M5BeFN0L9O4hz"
  5. + "GAEn8xyTE96f8sh4VlRmBOvVdwZqRO+ilkOM96+KL88A9RKdp8V2tna7TM6oI3LHDyf/JBoXaQJBAMcVN7fKlYP"
  6. + "Skzfh/yZzW2fmC0ZNg/qaW8Oa/wfDxlWjgnS0p/EKWZ8BxjR/d199L3i/KMaGdfpaWbYZLvYENqUCQQCobjsuCW"
  7. + "nlZhcWajjzpsSuy8/bICVEpUax1fUZ58Mq69CQXfaZemD9Ar4omzuEAAs2/uee3kt3AvCBaeq05NyjAkBme8SwB0iK"
  8. + "kLcaeGuJlq7CQIkjSrobIqUEf+CzVZPe+AorG+isS+Cw2w/2bHu+G0p5xSYvdH59P0+ZT0N+f9LFAkA6v3Ae56OrI"
  9. + "wfMhrJksfeKbIaMjNLS9b8JynIaXg9iCiyOHmgkMl5gAbPoH/ULXqSKwzBw5mJ2GW1gBlyaSfV3AkA/RJC+adIjsRGg"
  10. + "JOkiRjSmPpGv3FOhl9fsBPjupZBEIuoMWOC8GXK/73DHxwmfNmN7C9+sIi4RBcjEeQ5F5FHZ";
  11. RSA rsa = new RSA(PRIVATE_KEY, null);
  12. String a = "2707F9FD4288CEF302C972058712F24A5F3EC62C5A14AD2FC59DAB93503AA0FA17113A020EE4EA35EB53F"
  13. + "75F36564BA1DABAA20F3B90FD39315C30E68FE8A1803B36C29029B23EB612C06ACF3A34BE815074F5EB5AA3A"
  14. + "C0C8832EC42DA725B4E1C38EF4EA1B85904F8B10B2D62EA782B813229F9090E6F7394E42E6F44494BB8";
  15. byte[] aByte = HexUtil.decodeHex(a);
  16. byte[] decrypt = rsa.decrypt(aByte, KeyType.PrivateKey);
  17. //Junit单元测试
  18. //Assert.assertEquals("虎头闯杭州,多抬头看天,切勿只管种地", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));

摘要加密-Digester和HMac

介绍

摘要算法介绍

摘要算法是一种能产生特殊输出格式的算法,这种算法的特点是:无论用户输入什么长度的原始数据,经过计算后输出的密文都是固定长度的,这种算法的原理是根据一定的运算规则对原数据进行某种形式的提取,这种提取就是摘要,被摘要的数据内容与原数据有密切联系,只要原数据稍有改变,输出的“摘要”便完全不同,因此,基于这种原理的算法便能对数据完整性提供较为健全的保障。
但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证。

HMAC介绍

HMAC,全称为“Hash Message Authentication Code”,中文名“散列消息鉴别码”,主要是利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。一般的,消息鉴别码用于验证传输于两个共 同享有一个密钥的单位之间的消息。HMAC 可以与任何迭代散列函数捆绑使用。MD5 和 SHA-1 就是这种散列函数。HMAC 还可以使用一个用于计算和确认消息鉴别值的密钥。

Hutool支持的摘要算法类型

详细见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest

摘要算法

  • MD2
  • MD5
  • SHA-1
  • SHA-256
  • SHA-384
  • SHA-512

    Hmac算法

  • HmacMD5

  • HmacSHA1
  • HmacSHA256
  • HmacSHA384
  • HmacSHA512

    摘要算法抽象

    摘要对象被抽象为两个对象:

  • Digester

  • HMac

    使用

    Digester

    以MD5为例:
    1. Digester md5 = new Digester(DigestAlgorithm.MD5);
    2. String digestHex = md5.digestHex(testStr);//5393554e94bf0eb6436f240a4fd71282
    当然,做为最为常用的方法,MD5等方法被封装为工具方法在DigestUtil中,以上代码可以进一步简化为:
    1. String md5Hex1 = DigestUtil.md5Hex(testStr);
    2. //Junit单元测试
    3. //Assert.assertEquals("5393554e94bf0eb6436f240a4fd71282", md5Hex1);

    HMac

    以HmacMD5为例:
    1. String testStr = "test中文";
    2. byte[] key = "password".getBytes();
    3. HMac mac = new HMac(HmacAlgorithm.HmacMD5, key);
    4. String macHex1 = mac.digestHex(testStr);//b977f4b13f93f549e06140971bded384

    SM3

    在4.2.1之后,Hutool借助Bouncy Castle库可以支持国密算法,以SM3为例:
    我们首先需要引入Bouncy Castle库:
    1. <dependency>
    2. <groupId>org.bouncycastle</groupId>
    3. <artifactId>bcpkix-jdk15on</artifactId>
    4. <version>1.60</version>
    5. </dependency>
    然后可以调用SM3算法,调用方法与其它摘要算法一致:
    1. Digester digester = DigestUtil.digester("sm3");
    2. //136ce3c86e4ed909b76082055a61586af20b4dab674732ebd4b599eef080c9be
    3. String digestHex = digester.digestHex("aaaaa");

    签名和验证-Sign

介绍

Hutool针对java.security.Signature做了简化包装,包装类为:Sign,用于生成签名和签名验证。
对于签名算法,Hutool封装了JDK的,具体介绍见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature

  1. // The RSA signature algorithm
  2. NONEwithRSA
  3. // The MD2/MD5 with RSA Encryption signature algorithm
  4. MD2withRSA
  5. MD5withRSA
  6. // The signature algorithm with SHA-* and the RSA
  7. SHA1withRSA
  8. SHA256withRSA
  9. SHA384withRSA
  10. SHA512withRSA
  11. // The Digital Signature Algorithm
  12. NONEwithDSA
  13. // The DSA with SHA-1 signature algorithm
  14. SHA1withDSA
  15. // The ECDSA signature algorithms
  16. NONEwithECDSA
  17. SHA1withECDSA
  18. SHA256withECDSA
  19. SHA384withECDSA
  20. SHA512withECDSA

使用

  1. byte[] data = "我是一段测试字符串".getBytes();
  2. Sign sign = SecureUtil.sign(SignAlgorithm.MD5withRSA);
  3. //签名
  4. byte[] signed = sign.sign(data);
  5. //验证签名
  6. boolean verify = sign.verify(data, signed);

国密算法工具-SmUtil

介绍

Hutool针对Bouncy Castle做了简化包装,用于实现国密算法中的SM2、SM3、SM4。
国密算法工具封装包括:

  • 非对称加密和签名:SM2
  • 摘要签名算法:SM3
  • 对称加密:SM4

国密算法需要引入Bouncy Castle库的依赖。

使用

引入Bouncy Castle依赖

  1. <dependency>
  2. <groupId>org.bouncycastle</groupId>
  3. <artifactId>bcprov-jdk15on</artifactId>
  4. <version>${bouncycastle.version}</version>
  5. </dependency>

说明 bcprov-jdk15on的版本请前往Maven中央库搜索,查找对应JDK的版本。

非对称加密SM2

  1. 使用随机生成的密钥对加密或解密

    1. String text = "我是一段测试aaaa";
    2. SM2 sm2 = SmUtil.sm2();
    3. // 公钥加密,私钥解密
    4. String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
    5. String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));
  2. 使用自定义密钥对加密或解密

    1. String text = "我是一段测试aaaa";
    2. KeyPair pair = SecureUtil.generateKeyPair("SM2");
    3. byte[] privateKey = pair.getPrivate().getEncoded();
    4. byte[] publicKey = pair.getPublic().getEncoded();
    5. SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
    6. // 公钥加密,私钥解密
    7. String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
    8. String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));

    摘要加密算法SM3

    1. //结果为:136ce3c86e4ed909b76082055a61586af20b4dab674732ebd4b599eef080c9be
    2. String digestHex = SmUtil.sm3("aaaaa");

    对称加密SM4

    ```java String content = “test中文”; SymmetricCrypto sm4 = SmUtil.sm4();

String encryptHex = sm4.encryptHex(content); String decryptStr = sm4.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8); ```