赞
踩
JAVA能够实现各种加解密方式,这里对各种加密方式做了封装,包括AES、DES、BCrypt、MD5、SHA、RSA、SM3、SM4、Http Basic等。
ASE为对称加密。
工具类:
import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.math.BigInteger; import java.nio.charset.StandardCharsets; /** * <p>AES加解密工具类</p> * * @author zhuquanwen * @version 1.0 * @date 2018/7/13 17:31 * @since jdk1.8 **/ @SuppressWarnings("unused") public final class AesUtils { private AesUtils() { } private static final String CKEY = "encryptionIntVec"; /** * 密钥 */ public static final String CRYPT_KEY = "10f5dd7c2d45d247"; private static final String ALGORITHMSTR = "AES/CBC/PKCS5Padding"; public static String doDecrypt(String source) { String target = ""; try { target = aesDecrypt(source); } catch (Exception ignored) { } return StringUtils.isNotBlank(target) ? target : source; } public static String doEncrypt(String source) { String target = ""; try { target = aesEncrypt(source, CRYPT_KEY); } catch (Exception ignored) { } return StringUtils.isNotBlank(target) ? target : source; } /** * aes解密 * * @param encrypt 内容 * @return String * @throws Exception 异常 */ public static String aesDecrypt(String encrypt) throws Exception { return aesDecrypt(encrypt, CRYPT_KEY); } /** * aes加密 * * @param content content * @return String * @throws Exception 异常 */ public static String aesEncrypt(String content) throws Exception { return aesEncrypt(content, CRYPT_KEY); } /** * 将byte[]转为各种进制的字符串 * * @param bytes byte[] * @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制 * @return 转换后的字符串 */ public static String binary(byte[] bytes, int radix) { // 这里的1代表正数 return new BigInteger(1, bytes).toString(radix); } /** * common 64 encode * * @param bytes 待编码的byte[] * @return 编码后的base 64 code */ public static String base64Encode(byte[] bytes) { return Base64.encodeBase64String(bytes); } /** * common 64 decode * * @param base64Code 待解码的base 64 code * @return 解码后的byte[] */ public static byte[] base64Decode(String base64Code) { return StringUtils.isEmpty(base64Code) ? null : java.util.Base64.getDecoder().decode(base64Code); } public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { byte[] raw = encryptKey.getBytes(StandardCharsets.UTF_8); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance(ALGORITHMSTR); //使用CBC模式,需要一个向量iv,可以增加加密算法的强度 IvParameterSpec iv = new IvParameterSpec(CKEY.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); } /** * AES加密为base 64 code * * @param content 待加密的内容 * @param encryptKey 加密密钥 * @return 加密后的base 64 code * @throws Exception 异常 */ public static String aesEncrypt(String content, String encryptKey) throws Exception { return base64Encode(aesEncryptToBytes(content, encryptKey)); } public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { byte[] raw = decryptKey.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance(ALGORITHMSTR); IvParameterSpec iv = new IvParameterSpec(CKEY.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv); byte[] decryptBytes = cipher.doFinal(encryptBytes); return new String(decryptBytes, StandardCharsets.UTF_8); } /** * 将base 64 code AES解密 * * @param encryptStr 待解密的base 64 code * @param decryptKey 解密密钥 * @return 解密后的string * @throws Exception 异常 */ public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); } }
单元测试:
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * AES加解密测试类 * @author zhuquanwen **/ public class AesUtilsTests { /** * 测试AES 默认加密 * */ @Test public void aesEncrpty() throws Exception { String sec = AesUtils.aesEncrypt("USER2"); System.out.println(sec); Assertions.assertNotNull(sec); } /** * * 测试AES默认解密 * */ @Test public void aesDecrpty() throws Exception { String ori = AesUtils.aesDecrypt("Cg2jBQvUGJJUMfalO+HF5g=="); Assertions.assertEquals("gfdx", ori); } /** * 测试AES 带KEY加密 * */ @Test public void aesEncrptyWithKey() throws Exception { String k = "6x9o67h5BO205Cfv"; String sec = AesUtils.aesEncrypt("admin", k); Assertions.assertNotNull(sec); } /** * 测试AES 带KEY解密 * */ @Test public void aesDecrptyWithKey() throws Exception { String k = "6x9o67h5BO205Cfv"; String ori = AesUtils.aesDecrypt("yij1zaxI6X10t7v6OpW7gw==", k); Assertions.assertEquals("admin", ori); } }
DES为对称加密
工具类:
import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import java.io.*; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.Base64; /** * @author zhuquanwen * @version 1.0 * @date 2023/2/23 17:29 */ public class DesUtils { private DesUtils() { } /** * iv向量 */ private static final String DESIV = "12345678"; // /** // * AlgorithmParameterSpec // */ // private static AlgorithmParameterSpec IV = null; /** * SHA1PRNG */ private static final String SHA1PRNG = "SHA1PRNG"; /** * 密钥 */ public static final String CRYPT_KEY = "10f5dd7c2d45d247"; /** * CBC加密模式 */ private static final String DES_CBC_PKCS5PADDING = "DES/CBC/PKCS5Padding"; /** * OFB加密模式 */ private static final String DES_OFB_PKCS5PADDING = "DES/OFB/PKCS5Padding"; /** * CFB加密模式 */ private static final String DES_CFB_PKCS5_PADDING = "DES/CFB/PKCS5Padding"; /** * ECB加密模式 */ private static final String DES_ECB_PKCS5_PADDING = "DES/ECB/PKCS5Padding"; /** * DES模式 */ private static final String DES = "DES"; /** * 加密模式 */ private static final int ENCRYPT_MODE = 1; /** * 解密模式 */ private static final int DECRYPT_MODE = 2; /** * 字符串des加密 * * @param data 需要加密的字符串 * @param mode 加密模式 (ECB/CBC/OFB/CFB) * @param secret 密钥 * @return 加密结果 * @throws Exception 异常 */ public static String encrypt(String data, Mode mode, String secret) throws Exception { Cipher cipher = getPattern(mode, ENCRYPT_MODE, secret); byte[] pasByte = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(pasByte); } /** * 字符串des加密 * * @param data 需要加密的字符串 * @param secret 密钥 * @return 加密结果 * @throws Exception 异常 */ public static String encrypt(String data, String secret) throws Exception { Cipher cipher = getPattern(Mode.CBC, ENCRYPT_MODE, secret); byte[] pasByte = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(pasByte); } /** * 字符串des加密 * * @param data 需要加密的字符串 * @return 加密结果 * @throws Exception 异常 */ public static String encrypt(String data) throws Exception { Cipher cipher = getPattern(Mode.CBC, ENCRYPT_MODE, CRYPT_KEY); byte[] pasByte = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(pasByte); } /** * 字符串des解密 * * @param data 需要解密的字符串 * @param mode 解密模式 (ECB/CBC/OFB/CFB) * @return 解密结果 * @throws Exception 异常 * @author sucb */ public static String decrypt(String data, Mode mode, String secret) throws Exception { Cipher cipher = getPattern(mode, DECRYPT_MODE, secret); byte[] pasByte = cipher.doFinal(Base64.getDecoder().decode(data)); return new String(pasByte, StandardCharsets.UTF_8); } /** * 字符串des解密 * * @param data 需要解密的字符串 * @return 解密结果 * @throws Exception 异常 * @author sucb */ public static String decrypt(String data, String secret) throws Exception { Cipher cipher = getPattern(Mode.CBC, DECRYPT_MODE, secret); byte[] pasByte = cipher.doFinal(Base64.getDecoder().decode(data)); return new String(pasByte, StandardCharsets.UTF_8); } /** * 字符串des解密 * * @param data 需要解密的字符串 * @return 解密结果 * @throws Exception 异常 * @author sucb */ public static String decrypt(String data) throws Exception { Cipher cipher = getPattern(Mode.CBC, DECRYPT_MODE, CRYPT_KEY); byte[] pasByte = cipher.doFinal(Base64.getDecoder().decode(data)); return new String(pasByte, StandardCharsets.UTF_8); } /** * 文件 file 进行加密并保存目标文件 destFile 中 * * @param file 要加密的文件 如 c:/test/file.txt * @param destFile 加密后存放的文件名 如 c:/ 加密后文件 .txt * @param mode 加密模式 (ECB/CBC/OFB/CFB) * @return 加密结果 0:异常 1:加密成功; 5:未找到需要加密的文件 */ public int encryptFile(String file, String destFile, Mode mode) throws Exception { int result = 0; try { Cipher cipher = getPattern(mode, ENCRYPT_MODE, CRYPT_KEY); InputStream is = new FileInputStream(file); OutputStream out = new FileOutputStream(destFile); CipherInputStream cis = new CipherInputStream(is, cipher); byte[] buffer = new byte[1024]; int r; while ((r = cis.read(buffer)) > 0) { out.write(buffer, 0, r); } cis.close(); is.close(); out.close(); result = 1; } catch (FileNotFoundException e) { result = 5; } return result; } /** * 文件 file 进行解密并保存目标文件 destFile 中 * * @param file 要解密的文件 如 c:/test/file.txt * @param destFile 解密后存放的文件名 如 c:/ 解密后文件 .txt * @param mode 解密模式 (ECB/CBC/OFB/CFB) * @return 解密结果 0:解密异常;1:解密正常;5:未找到需要解密的文件 * @author sucb * @date 2017年3月2日下午7:58:56 */ public int decryptFile(String file, String destFile, Mode mode) throws Exception { int result = 0; try { Cipher cipher = getPattern(mode, DECRYPT_MODE, CRYPT_KEY); InputStream is = new FileInputStream(file); OutputStream out = new FileOutputStream(destFile); CipherOutputStream cos = new CipherOutputStream(out, cipher); byte[] buffer = new byte[1024]; int r; while ((r = is.read(buffer)) >= 0) { cos.write(buffer, 0, r); } cos.close(); out.close(); is.close(); result = 1; } catch (FileNotFoundException e) { result = 5; } return result; } /** * 通过密钥获得key * * @param secretKey 密钥 */ public static Key getKey(String secretKey) throws NoSuchAlgorithmException { SecureRandom secureRandom = SecureRandom.getInstance(SHA1PRNG); secureRandom.setSeed(secretKey.getBytes()); KeyGenerator generator = KeyGenerator.getInstance("DES"); generator.init(secureRandom); return generator.generateKey(); } /** * 初始化cipher * * @param mode 加密/解密模式 (ECB/CBC/OFB/CFB) * @param cipherMode cipher工作模式 1:加密; 2:解密 * @return cipher * @throws Exception 异常 */ private static Cipher getPattern(Mode mode, int cipherMode, String secret) throws Exception { AlgorithmParameterSpec IV = new IvParameterSpec(DESIV.getBytes(StandardCharsets.UTF_8)); secret = secret == null ? CRYPT_KEY : secret; Key key = getKey(secret); Cipher cipher; switch (mode) { case OFB: cipher = Cipher.getInstance(DES_OFB_PKCS5PADDING); cipher.init(cipherMode, key, IV); break; case CFB: cipher = Cipher.getInstance(DES_CFB_PKCS5_PADDING); cipher.init(cipherMode, key, IV); break; case DES: cipher = Cipher.getInstance(DES); cipher.init(cipherMode, key); break; case ECB: cipher = Cipher.getInstance(DES_ECB_PKCS5_PADDING); cipher.init(cipherMode, key); break; default: cipher = Cipher.getInstance(DES_CBC_PKCS5PADDING); cipher.init(cipherMode, key, IV); break; } return cipher; } public enum Mode { /** * DES */ DES, /** * CFB */ CFB, /** * OFB */ OFB, /** * CBC,默认模式 */ CBC, /** * ECB */ ECB; } }
单元测试:
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * @author zhuquanwen * @version 1.0 * @date 2023/2/23 18:00 */ class DesUtilsTest { @Test public void test1() throws Exception { String encrypt = DesUtils.encrypt("123456"); System.out.println(encrypt); Assertions.assertNotNull(encrypt); } @Test public void test2() throws Exception { String decrypt = DesUtils.decrypt("yGj4sGSmFxs="); System.out.println(decrypt); Assertions.assertNotNull(decrypt); } /** * DES模式 * */ @Test public void test3() throws Exception { String encrypt = DesUtils.encrypt("123456", DesUtils.Mode.DES, "abcdefgh"); System.out.println(encrypt); String data = DesUtils.decrypt(encrypt, DesUtils.Mode.DES, "abcdefgh"); System.out.println(data); } /** * CBC模式 * */ @Test public void test4() throws Exception { String encrypt = DesUtils.encrypt("123456", DesUtils.Mode.CBC, "abcdefgh"); System.out.println(encrypt); String data = DesUtils.decrypt(encrypt, DesUtils.Mode.CBC, "abcdefgh"); System.out.println(data); } /** * ECB模式 * */ @Test public void test5() throws Exception { String encrypt = DesUtils.encrypt("123456", DesUtils.Mode.ECB, "abcdefgh"); System.out.println(encrypt); String data = DesUtils.decrypt(encrypt, DesUtils.Mode.ECB, "abcdefgh"); System.out.println(data); } /** * CFB模式 * */ @Test public void test6() throws Exception { String encrypt = DesUtils.encrypt("123456", DesUtils.Mode.CFB, "abcdefgh"); System.out.println(encrypt); String data = DesUtils.decrypt(encrypt, DesUtils.Mode.CFB, "abcdefgh"); System.out.println(data); } /** * OFB模式 * */ @Test public void test7() throws Exception { String encrypt = DesUtils.encrypt("123456", DesUtils.Mode.OFB, "abcdefgh"); System.out.println(encrypt); String data = DesUtils.decrypt(encrypt, DesUtils.Mode.OFB, "abcdefgh"); System.out.println(data); } }
BCrpty为单向加密
工具类:
package com.iscas.common.tools.core.security; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.regex.Pattern; /** * <p>BCrypt加解密工具类</p> * @author zhuquanwen * @version 1.0 * @since jdk1.8 * @date 2021/03/14 13:02 **/ @SuppressWarnings({"CStyleArrayDeclaration", "unused", "AliArrayNamingShouldHaveBracket", "AlibabaLowerCamelCaseVariableNaming", "AlibabaClassNamingShouldBeCamel", "AlibabaConstantFieldShouldBeUpperCase", "RegExpSimplifiable"}) public class BCryptUtils { private static final Pattern BCRYPT_PATTERN = Pattern .compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}"); private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; private static final int BCRYPT_SALT_LEN = 16; /**Blowfish parameters*/ private static final int BLOWFISH_NUM_ROUNDS = 16; /**Initial contents of key schedule*/ private static final int P_orig[] = {0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b}; private static final int S_orig[] = {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6}; static private final int bf_crypt_ciphertext[] = {0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274}; static private final char base64_code[] = {'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; static private final byte index_64[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1}; private static int P[]; private static int S[]; private static void init_key() { P = P_orig.clone(); S = S_orig.clone(); } static void encode_base64(byte d[], int len, StringBuilder rs) throws IllegalArgumentException { int off = 0; int c1, c2; if (len <= 0 || len > d.length) { throw new IllegalArgumentException("Invalid len"); } while (off < len) { c1 = d[off++] & 0xff; rs.append(base64_code[(c1 >> 2) & 0x3f]); c1 = (c1 & 0x03) << 4; if (off >= len) { rs.append(base64_code[c1 & 0x3f]); break; } c2 = d[off++] & 0xff; c1 |= (c2 >> 4) & 0x0f; rs.append(base64_code[c1 & 0x3f]); c1 = (c2 & 0x0f) << 2; if (off >= len) { rs.append(base64_code[c1 & 0x3f]); break; } c2 = d[off++] & 0xff; c1 |= (c2 >> 6) & 0x03; rs.append(base64_code[c1 & 0x3f]); rs.append(base64_code[c2 & 0x3f]); } } static byte[] decode_base64(String s) throws IllegalArgumentException { ByteArrayOutputStream out = new ByteArrayOutputStream(BCryptUtils.BCRYPT_SALT_LEN); int off = 0, slen = s.length(), olen = 0; byte c1, c2, c3, c4, o; if (BCryptUtils.BCRYPT_SALT_LEN <= 0) { throw new IllegalArgumentException("Invalid maxolen"); } while (off < slen - 1 && olen < BCryptUtils.BCRYPT_SALT_LEN) { c1 = char64(s.charAt(off++)); c2 = char64(s.charAt(off++)); if (c1 == -1 || c2 == -1) { break; } o = (byte) (c1 << 2); o |= (c2 & 0x30) >> 4; out.write(o); if (++olen >= BCryptUtils.BCRYPT_SALT_LEN || off >= slen) { break; } c3 = char64(s.charAt(off++)); if (c3 == -1) { break; } o = (byte) ((c2 & 0x0f) << 4); o |= (c3 & 0x3c) >> 2; out.write(o); if (++olen >= BCryptUtils.BCRYPT_SALT_LEN || off >= slen) { break; } c4 = char64(s.charAt(off++)); o = (byte) ((c3 & 0x03) << 6); o |= c4; out.write(o); ++olen; } return out.toByteArray(); } private static byte char64(char x) { if (x > index_64.length) { return -1; } return index_64[x]; } public static String gensalt(int log_rounds, SecureRandom random) { if (log_rounds < 4 || log_rounds > 31) { throw new IllegalArgumentException("Bad number of rounds"); } StringBuilder rs = new StringBuilder(); byte rnd[] = new byte[BCRYPT_SALT_LEN]; random.nextBytes(rnd); rs.append("$2a$"); if (log_rounds < 10) { rs.append("0"); } rs.append(log_rounds); rs.append("$"); encode_base64(rnd, rnd.length, rs); return rs.toString(); } public static String hashpw(String password, String salt) { String real_salt; byte passwordb[], saltb[], hashed[]; char minor = (char) 0; int rounds, off; StringBuilder rs = new StringBuilder(); int saltLength = salt.length(); if (saltLength < 28) { throw new IllegalArgumentException("Invalid salt"); } if (salt.charAt(0) != '$' || salt.charAt(1) != '2') { throw new IllegalArgumentException("Invalid salt version"); } if (salt.charAt(2) == '$') { off = 3; } else { minor = salt.charAt(2); if (minor != 'a' || salt.charAt(3) != '$') { throw new IllegalArgumentException("Invalid salt revision"); } off = 4; } if (saltLength - off < 25) { throw new IllegalArgumentException("Invalid salt"); } // Extract number of rounds if (salt.charAt(off + 2) > '$') { throw new IllegalArgumentException("Missing salt rounds"); } rounds = Integer.parseInt(salt.substring(off, off + 2)); real_salt = salt.substring(off + 3, off + 25); passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes(StandardCharsets.UTF_8); saltb = decode_base64(real_salt); hashed = crypt_raw(passwordb, saltb, rounds); rs.append("$2"); if (minor >= 'a') { rs.append(minor); } rs.append("$"); if (rounds < 10) { rs.append("0"); } rs.append(rounds); rs.append("$"); encode_base64(saltb, saltb.length, rs); encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs); return rs.toString(); } private static byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { int cdata[] = bf_crypt_ciphertext.clone(); int clen = cdata.length; byte ret[]; long rounds = roundsForLogRounds(log_rounds); init_key(); ekskey(salt, password); for (long i = 0; i < rounds; i++) { key(password); key(salt); } for (int i = 0; i < 64; i++) { for (int j = 0; j < (clen >> 1); j++) { encipher(cdata, j << 1); } } ret = new byte[clen * 4]; for (int i = 0, j = 0; i < clen; i++) { ret[j++] = (byte) ((cdata[i] >> 24) & 0xff); ret[j++] = (byte) ((cdata[i] >> 16) & 0xff); ret[j++] = (byte) ((cdata[i] >> 8) & 0xff); ret[j++] = (byte) (cdata[i] & 0xff); } return ret; } static long roundsForLogRounds(int log_rounds) { if (log_rounds < 4 || log_rounds > 31) { throw new IllegalArgumentException("Bad number of rounds"); } return 1L << log_rounds; } private static void ekskey(byte data[], byte key[]) { int i; int koffp[] = {0}, doffp[] = {0}; int lr[] = {0, 0}; int plen = P.length, slen = S.length; for (i = 0; i < plen; i++) { P[i] = P[i] ^ streamtoword(key, koffp); } for (i = 0; i < plen; i += 2) { lr[0] ^= streamtoword(data, doffp); lr[1] ^= streamtoword(data, doffp); encipher(lr, 0); P[i] = lr[0]; P[i + 1] = lr[1]; } for (i = 0; i < slen; i += 2) { lr[0] ^= streamtoword(data, doffp); lr[1] ^= streamtoword(data, doffp); encipher(lr, 0); S[i] = lr[0]; S[i + 1] = lr[1]; } } private static int streamtoword(byte data[], int offp[]) { int i; int word = 0; int off = offp[0]; for (i = 0; i < 4; i++) { word = (word << 8) | (data[off] & 0xff); off = (off + 1) % data.length; } offp[0] = off; return word; } private static void encipher(int lr[], int off) { int i, n, l = lr[off], r = lr[off + 1]; l ^= P[0]; for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2; ) { // Feistel substitution on left word n = S[(l >> 24) & 0xff]; n += S[0x100 | ((l >> 16) & 0xff)]; n ^= S[0x200 | ((l >> 8) & 0xff)]; n += S[0x300 | (l & 0xff)]; r ^= n ^ P[++i]; // Feistel substitution on right word n = S[(r >> 24) & 0xff]; n += S[0x100 | ((r >> 16) & 0xff)]; n ^= S[0x200 | ((r >> 8) & 0xff)]; n += S[0x300 | (r & 0xff)]; l ^= n ^ P[++i]; } lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; lr[off + 1] = l; } private static void key(byte key[]) { int i; int koffp[] = {0}; int lr[] = {0, 0}; int plen = P.length, slen = S.length; for (i = 0; i < plen; i++) { P[i] = P[i] ^ streamtoword(key, koffp); } for (i = 0; i < plen; i += 2) { encipher(lr, 0); P[i] = lr[0]; P[i + 1] = lr[1]; } for (i = 0; i < slen; i += 2) { encipher(lr, 0); S[i] = lr[0]; S[i + 1] = lr[1]; } } public static boolean matches(CharSequence rawPassword, String encodedPassword) { if (encodedPassword == null || encodedPassword.length() == 0) { return false; } if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) { return false; } return checkpw(rawPassword.toString(), encodedPassword); } public static boolean checkpw(String plaintext, String hashed) { return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed)); } static boolean equalsNoEarlyReturn(String a, String b) { char[] caa = a.toCharArray(); char[] cab = b.toCharArray(); if (caa.length != cab.length) { return false; } byte ret = 0; for (int i = 0; i < caa.length; i++) { ret |= caa[i] ^ cab[i]; } return ret == 0; } }
单元测试:
package com.iscas.common.tools.core.security; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.security.SecureRandom; /** * BcryptUtils测试 * * @author zhuquanwen * @version 1.0 * @date 2021/3/14 13:03 * @since jdk1.8 */ public class BCryptUtilsTests { /** * 测试Bcrypt加解密 * */ @Test public void testBcrypt() { //10是不确定的 此处只是个例子 String salt = BCryptUtils.gensalt(10, new SecureRandom()); String str = BCryptUtils.hashpw("123456", salt); boolean result = BCryptUtils.checkpw("123456", str); Assertions.assertEquals(result, true); } }
工具类:
import java.util.Base64; /** * @author zhuquanwen * @version 1.0 * @date 2023/1/20 8:42 */ public class HttpBasicUtils { private HttpBasicUtils() {} public static String createAuthorizationVal(String username, String pwd) { String authStr = username + ":" + pwd; byte[] authEncBytes = Base64.getEncoder().encode(authStr.getBytes()); return "Basic " + new String(authEncBytes); } }
MD5为单向加密
引入依赖(MAVEN):
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
引入依赖(gradle):
// https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
工具类:
package com.iscas.common.tools.core.security; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.MD5Digest; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Random; /** * MD5加密解密工具类 * * @author zhuquanwen * @date 2018/7/13 **/ @SuppressWarnings({"unused", "AlibabaLowerCamelCaseVariableNaming", "AlibabaClassNamingShouldBeCamel"}) public final class MD5Utils { private static final int LENGTH_16 = 16; private static final int LENGTH_48 = 48; private static final int NUMBER_3 = 3; private static final String DEFAULT_CHARSET = "UTF-8"; private MD5Utils() { } /** * MD5加密,不带盐 * * @param input 输入字符串 * @param charset 编码 * @return java.lang.String * @throws NoSuchAlgorithmException 获取MessageDigest对象失败 {@link MessageDigest} * @date 2018/7/14 */ @SuppressWarnings("UnusedAssignment") public static String md5(String input, String charset) throws NoSuchAlgorithmException, UnsupportedEncodingException { assert StringUtils.isNotBlank(input); byte[] byteArray = input.getBytes(charset); return doMd5(byteArray); } /** * MD5加密,不带盐 * * @param input 输入字符串 * @return java.lang.String * @throws NoSuchAlgorithmException 获取MessageDigest对象失败 {@link MessageDigest} * @date 2018/7/14 */ @SuppressWarnings("UnusedAssignment") public static String md5(String input) throws NoSuchAlgorithmException { try { return md5(input, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * MD5加密,自动生成盐 * * @param str 要加密的串 * @param charset 编码格式 * @return java.lang.String * @date 2018/7/14 */ @SuppressWarnings("AlibabaUndefineMagicConstant") public static String saltMD5(String str, String charset) throws NoSuchAlgorithmException, UnsupportedEncodingException { assert StringUtils.isNotBlank(str); Random r = new Random(); StringBuilder sb = new StringBuilder(16); sb.append(r.nextInt(99999999)).append(r.nextInt(99999999)); int len = sb.length(); if (len < LENGTH_16) { sb.append("0".repeat(Math.max(0, LENGTH_16 - len))); } String salt = sb.toString(); str = doMd5((str + salt).getBytes(charset)); char[] cs = new char[LENGTH_48]; for (int i = 0; i < LENGTH_48; i += NUMBER_3) { cs[i] = str.charAt(i / 3 * 2); char c = salt.charAt(i / 3); cs[i + 1] = c; cs[i + 2] = str.charAt(i / 3 * 2 + 1); } return new String(cs); } /** * MD5加密,自动生成盐 * * @param str 要加密的串 * @return java.lang.String * @date 2018/7/14 */ @SuppressWarnings("AlibabaUndefineMagicConstant") public static String saltMD5(String str) throws NoSuchAlgorithmException { try { return saltMD5(str, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 加密校验 * * @param str 明文 * @param md5 密文 * @return boolean 校验成功与否 * @date 2018/7/14 */ @SuppressWarnings("AlibabaUndefineMagicConstant") public static boolean saltVerify(String str, String md5) throws NoSuchAlgorithmException { try { return saltVerify(str, md5, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 加密校验 * * @param str 明文 * @param md5 密文 * @param charset 编码格式 * @return boolean 校验成功与否 * @date 2018/7/14 */ @SuppressWarnings("AlibabaUndefineMagicConstant") public static boolean saltVerify(String str, String md5, String charset) throws NoSuchAlgorithmException, UnsupportedEncodingException { assert StringUtils.isNotBlank(str); assert StringUtils.isNotBlank(md5); char[] cs1 = new char[32]; char[] cs2 = new char[16]; for (int i = 0; i < LENGTH_48; i += NUMBER_3) { cs1[i / 3 * 2] = md5.charAt(i); cs1[i / 3 * 2 + 1] = md5.charAt(i + 2); cs2[i / 3] = md5.charAt(i + 1); } String salt = new String(cs2); return doMd5((str + salt).getBytes(charset)).equals(new String(cs1)); } /** * hmacMD5 * * @param key 密钥 * @param input 输入字符串 * @param charset 编码格式 * @return java.lang.String * @date 2018/7/14 */ @SuppressWarnings("UnusedAssignment") public static String hmacMd5(String key, String input, String charset) throws UnsupportedEncodingException { byte[] bytes = input.getBytes(charset); byte[] keyBytes = key.getBytes(charset); byte[] hmacResult = doHmacMd5(keyBytes, bytes); return ByteUtils.toHexString(hmacResult); } /** * hmacMD5 * * @param key 密钥 * @param input 输入字符串 * @return java.lang.String * @date 2018/7/14 */ @SuppressWarnings("UnusedAssignment") public static String hmacMd5(String key, String input) { try { return hmacMd5(key, input, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * HMAC校验 * * @param str 明文 * @param md5 密文 * @param charset 编码格式 * @return boolean 校验成功与否 * @date 2018/7/14 */ @SuppressWarnings("AlibabaUndefineMagicConstant") public static boolean hmacVerify(String key, String str, String md5, String charset) throws UnsupportedEncodingException { assert StringUtils.isNotBlank(str); assert StringUtils.isNotBlank(md5); assert StringUtils.isNotBlank(key); assert StringUtils.isNotBlank(charset); byte[] src = ByteUtils.fromHexString(md5); byte[] keyBytes = key.getBytes(charset); byte[] strBytes = str.getBytes(charset); byte[] target = doHmacMd5(keyBytes, strBytes); return Arrays.equals(src, target); } /** * HMAC校验 * * @param str 明文 * @param md5 密文 * @return boolean 校验成功与否 * @date 2018/7/14 */ @SuppressWarnings("AlibabaUndefineMagicConstant") public static boolean hmacVerify(String key, String str, String md5) { try { return hmacVerify(key, str, md5, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } // /** // * 获取十六进制字符串形式的MD5摘要 // */ // private static String md5Hex(String src) throws NoSuchAlgorithmException { // MessageDigest md5 = MessageDigest.getInstance("md5"); // byte[] bs = md5.digest(src.getBytes()); // return new String(new Hex().encode(bs)); // // } /** * MD5加密,不带盐 * * @param input 输入字符串 * @return java.lang.String * @throws NoSuchAlgorithmException 获取MessageDigest对象失败 {@link MessageDigest} * @date 2018/7/14 */ public static String doMd5(byte[] input) throws NoSuchAlgorithmException { assert input != null; MessageDigest md5 = MessageDigest.getInstance("md5"); byte[] md5Bytes = md5.digest(input); return toHexString(md5Bytes); } /** * 获取一个文件的md5值(可处理大文件) * * @return md5 value */ public static String getFileMD5(InputStream is) { try { MessageDigest MD5 = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[8192]; int length; while ((length = is.read(buffer)) != -1) { MD5.update(buffer, 0, length); } return new String(Hex.encodeHex(MD5.digest())); } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (is != null) { is.close(); } } catch (IOException e) { e.printStackTrace(); } } } private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static String toHexString(byte[] input) { String result = ""; for (int i = 0; i < input.length; i++) { result += HEX_CHARS[(input[i] >>> 4) & 0x0f]; result += HEX_CHARS[(input[i]) & 0x0f]; } return result; } @NotNull private static byte[] doHmacMd5(byte[] keyBytes, byte[] bytes) { Digest mg = new MD5Digest(); HMac hMac = new HMac(mg); hMac.init(new KeyParameter(keyBytes)); hMac.update(bytes, 0, bytes.length); byte[] hmacResult = new byte[hMac.getMacSize()]; hMac.doFinal(hmacResult, 0); return hmacResult; } }
单元测试:
package com.iscas.common.tools.core.security; import com.iscas.common.tools.core.reflect.reflectTest.A; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; /** * MD5工具类测试 * @author zhuquanwen **/ public class MD5UtilsTests { /** * 普通加密 * */ @Test public void MD51() throws NoSuchAlgorithmException { String sec = MD5Utils.md5("apply"); Assertions.assertNotNull(sec); } /** * 加盐加密 * */ @Test public void MD52() throws NoSuchAlgorithmException { String sec = MD5Utils.saltMD5("admin"); System.out.println(sec); Assertions.assertNotNull(sec); } /** * 加盐加密的校验 * */ @Test public void MD53() throws NoSuchAlgorithmException { boolean sec = MD5Utils.saltVerify("admin" ,"f2020e118a4aa8fd39a4560c79b16b30024fb29b64242b5e"); Assertions.assertEquals(true, sec); } /** * hmac加密 * */ @Test public void MD54() { String iscas = MD5Utils.hmacMd5("iscas", "123456"); System.out.println(iscas); Assertions.assertNotNull(iscas); } /** * hmac校验 * */ @Test public void MD55() { String data = MD5Utils.hmacMd5("iscas", "123456"); System.out.println(data); boolean res = MD5Utils.hmacVerify("iscas", "123456", data); Assertions.assertTrue(res); } /** * 获取大文件MD5码 * */ @Test @Disabled public void testFileMD5() throws FileNotFoundException { long start = System.currentTimeMillis(); String fileMD5 = MD5Utils.getFileMD5(new FileInputStream("G:/cs-video/dist/fe8fc15215877f8422c43dfa408c1518.store")); Assertions.assertNotNull(fileMD5); System.out.println(fileMD5); long end = System.currentTimeMillis(); System.out.printf("耗时:%dms", (end - start)); } }
RSA为非对称加密
工具类:
package com.iscas.common.tools.core.security; import javax.crypto.Cipher; import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; /** * RSA工具类 * * @author zhuquanwen * @version 1.0 * @date 2019/9/27 15:48 * @since jdk1.8 */ @SuppressWarnings("unused") public class RsaUtils { private static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjh7unmMBs" + "QrAxJRHd3iXZD65fZClHxyEjxTzQB5F/UbYmyrEBzZfgw7u0UgSqnc+SQX+HUScHuMG+eC+KotoNPY1JlOC8x" + "A358khG6rBB3WDIqoZJ3PnZP3DkJ0IM8RO70xMFmqnQhWuGKrC28vOpqwopo8GvEaCoq7ec39/oJhV7skoYt+X" + "fYGLld9HVaKZyAvpbZSXhoX8GAOlXpAukhjuRZfyX0+313ZS4DUYibqczwHS6KNR8Cv5bz6lZACwaLIruZtdyD" + "7XacjHsDlHEqSgPPqRCRJNk3KC82dyyLWfEAEwb/CHMfQrTAUeCJuMbwmcZ5HJ9P5lXb6zBH5rgwIDAQAB"; private static final String PRIVATE_KEY = "RSA私钥Base64编码:MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjA" + "gEAAoIBAQCOHu6eYwGxCsDElEd3eJdkPrl9kKUfHISPFPNAHkX9RtibKsQHNl+DDu7RSBKqdz5JBf4dRJwe4wb5" + "4L4qi2g09jUmU4LzEDfnySEbqsEHdYMiqhknc+dk/cOQnQgzxE7vTEwWaqdCFa4YqsLby86mrCimjwa8RoKirt5" + "zf3+gmFXuyShi35d9gYuV30dVopnIC+ltlJeGhfwYA6VekC6SGO5Fl/JfT7fXdlLgNRiJupzPAdLoo1HwK/lvPq" + "VkALBosiu5m13IPtdpyMewOUcSpKA8+pEJEk2TcoLzZ3LItZ8QATBv8Icx9CtMBR4Im4xvCZxnkcn0/mVdvrME" + "fmuDAgMBAAECggEAA4Ye0oyP6SzkFLu8fejekBzCCqwAfCH/43BYi7l0cNBF5KsNy0P84EoJf+TymYl1YOgmIe" + "GmoVltvdplvLZSMiX8sWOWtqIrULL7AC2etamjQ8PF9eV40lc8dyR9pJL0hhh1NoUUep4BABmT1VFbYWSZaW/Yc" + "eipqpD9cQ2zQ28aDvQR0j1B0qEeWnfGqYYQKzR/v7MoXqOfrVJ1Jo7CbUbBuvwkr01RRl03k0u+cGFpq6gbp9i" + "GXJ7M7ss83+bZXKdjTzrXCEASQ68YJ4MvexL7mp+4L79eBrUOh/TGCOJazDTxa1PfKCE5FaD8TBrDFtle07EPV" + "I0XYASsvFp6KQKBgQDMf5qlwL4s524Eeg/W/xxFdpNX8TrWlGWrrGb/CCsKwIepRCNkwP98cCG75pB0EpyCRwGh" + "wbDOhzgbt7TbfqafpvIB5LyeQNAktYiPbMZTXDV1o3SVa5RKPiT/tW2Ponhbqp17kjJdSJyDD9eYXxrLQTsQ88" + "milC6nD3HalKme7QKBgQCx6bn3or+TP8EjS13VRd5xGwPW7csDTS87AO/pwvQHnRQGgOx987Lq856kcVJy+ocs" + "es1qWIsiqJKT5TKQh6jzYB2/PPq/rB9gCPM1fV6hb8iy7rNiQbe1LrFawK9fgEo7RewxmBCjN0OdtA22AyIL" + "IC+jtBNL1LGB84zYRl12LwKBgBXi2kQ/GptnsWidP7C84OO2SxKwaKGqhC8ZZnSJBUJDVMGS307bMPy6a4HWrUM" + "e8s0mmFAdkLSp2CFvSdXr+h1AGsqFFoLBYQVswE7JT3iAd+A9PC75soc3m3Iakr06oDL/UZd2EBnXuZh1S5etJg" + "r20kGANeZGga+zgXXpTzYJAoGBAI0bbmbtSvqz5zBiF0MPTlTw80OliI3OyvYGUUJbYIclW3upB2kCP1a/8IRGa" + "PlOoKVzpLaDEZ9kihUJBOjC4ApfolhKOiqJjrzxfExhaguqiEj6r4Xvz4/BP+NVzgJ10upeE+5lyFRbgaJz6yg" + "AJiEi3wX8zg0n3b3O+FeUv437AoGAVzH3ITlUWkiv438TxUREJlppZLOW/vDzhuueU5BnbwSP21tmClpWnM0lYZ" + "KgpagUVG0AWklv+HOGP9OaTwW9Pectz3nCtOoAVUdPFNAlHgu+I6wsNVMAlQALfLMMh9CJssbWG+21O196oO6Q" + "QsGqllLZFDy77TJQy0hgWnttcWM="; /** * 生成秘钥对 * @since jdk1.8 * @date 2021/1/6 * @throws Exception 异常 * @return java.security.KeyPair */ public static KeyPair getKeyPair() throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); return keyPairGenerator.generateKeyPair(); } /** * 获取公钥(Base64编码) * @since jdk1.8 * @date 2021/1/6 * @param keyPair 密钥对 * @return java.lang.String */ public static String getPublicKey(KeyPair keyPair){ PublicKey publicKey = keyPair.getPublic(); byte[] bytes = publicKey.getEncoded(); return byte2Base64(bytes); } /** * 获取私钥(Base64编码) * @since jdk1.8 * @date 2021/1/6 * @param keyPair 密钥对 * @return java.lang.String */ public static String getPrivateKey(KeyPair keyPair){ PrivateKey privateKey = keyPair.getPrivate(); byte[] bytes = privateKey.getEncoded(); return byte2Base64(bytes); } /** * 将Base64编码后的公钥转换成PublicKey对象 * @since jdk1.8 * @date 2021/1/6 * @param pubStr 公钥 * @return java.security.PublicKey */ public static PublicKey string2PublicKey(String pubStr) throws Exception{ byte[] keyBytes = base642Byte(pubStr); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(keySpec); } /** * 将Base64编码后的私钥转换成PrivateKey对象 * @since jdk1.8 * @date 2021/1/6 * @param priStr 私钥 * @return java.security.PrivateKey */ public static PrivateKey string2PrivateKey(String priStr) throws Exception{ byte[] keyBytes = base642Byte(priStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } /**公钥加密*/ private static byte[] publicEncrypt(byte[] content, PublicKey publicKey) throws Exception{ Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(content); } /**私钥解密*/ private static byte[] privateDecrypt(byte[] content, PrivateKey privateKey) throws Exception{ Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(content); } /**字节数组转Base64编码*/ private static String byte2Base64(byte[] bytes){ Base64.Encoder encoder = Base64.getEncoder(); return encoder.encodeToString(bytes); } /**Base64编码转字节数组*/ private static byte[] base642Byte(String base64Key) { Base64.Decoder decoder = Base64.getDecoder(); return decoder.decode(base64Key); } /** * RSA 加密 * @since jdk1.8 * @date 2019/9/27 * @param str 待加密的字符串 * @param publicKeyStr 公钥字符串 * @param charset 编码格式 * @return java.lang.String */ public static String encrypt(String str, String publicKeyStr, String charset) throws Exception { //将Base64编码后的公钥转换成PublicKey对象 if (publicKeyStr == null) { publicKeyStr = PUBLIC_KEY; } if (charset == null) { charset = "utf-8"; } PublicKey publicKey = string2PublicKey(publicKeyStr); //用公钥加密 byte[] publicEncrypt = RsaUtils.publicEncrypt(str.getBytes(charset), publicKey); //加密后的内容Base64编码 return RsaUtils.byte2Base64(publicEncrypt); } /** * RSA解密 * @since jdk1.8 * @date 2019/9/27 * @param str 待解密的字符串 * @param privateKeyStr 私钥字符串 * @param charset 编码格式 * @return java.lang.String */ public static String decrypt(String str, String privateKeyStr, String charset) throws Exception { if (privateKeyStr == null) { privateKeyStr = PRIVATE_KEY; } if (charset == null) { charset = "utf-8"; } PrivateKey privateKey = RsaUtils.string2PrivateKey(privateKeyStr); //加密后的内容Base64解码 byte[] base642Byte = RsaUtils.base642Byte(str); //用私钥解密 byte[] privateDecrypt = RsaUtils.privateDecrypt(base642Byte, privateKey); return new String(privateDecrypt, charset); } }
单元测试:
package com.iscas.common.tools.core.security; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.security.KeyPair; /** * @author zhuquanwen * @version 1.0 * @date 2019/9/27 15:51 * @since jdk1.8 */ public class RsaUtilsTests { @Test public void testRSA() { try { //===============生成公钥和私钥,公钥传给客户端,私钥服务端保留================== //生成RSA公钥和私钥,并Base64编码 KeyPair keyPair = RsaUtils.getKeyPair(); String publicKeyStr = RsaUtils.getPublicKey(keyPair); String privateKeyStr = RsaUtils.getPrivateKey(keyPair); System.out.println("RSA公钥Base64编码:" + publicKeyStr); System.out.println("RSA私钥Base64编码:" + privateKeyStr); //=========================使用公钥加密 String encrypt = RsaUtils.encrypt("祖国", publicKeyStr, "utf-8"); Assertions.assertNotNull(encrypt); //=========================使用私钥解密 String decrypt = RsaUtils.decrypt(encrypt, privateKeyStr, "utf-8"); Assertions.assertNotNull(decrypt); } catch (Exception e) { e.printStackTrace(); } } }
SHA为单向加密
引入依赖(MAVEN):
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
引入依赖(gradle):
// https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
工具类:
package com.iscas.common.tools.core.security; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import org.jetbrains.annotations.NotNull; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; /** * SHA256加密工具类 * * @author zhuquanwen * @version 1.0 * @date 2021/3/14 13:22 * @since jdk1.8 */ @SuppressWarnings("unused") public class Sha256Utils { private static final String DEFAULT_ENCODING = "UTF-8"; private Sha256Utils() { } /** * 利用java原生的摘要实现SHA256加密 * * @param str 待加密的报文 * @return String */ @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming") public static String encrypt(String str) throws NoSuchAlgorithmException { try { return encrypt(str, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 利用java原生的摘要实现SHA256加密 * * @param str 待加密的报文 * @param charset 编码格式 * @return String */ @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming") public static String encrypt(String str, String charset) throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(str.getBytes(charset)); return toHexString(messageDigest.digest()); } /** * hmac-sha256加密 * * @param key 秘钥 * @param str 待加密的报文 * @param charset 编码格式 * @return String */ @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming") public static String hmacEncrypt(String key, String str, String charset) throws UnsupportedEncodingException { byte[] keyBytes = key.getBytes(charset); byte[] strBytes = str.getBytes(charset); byte[] hmacResult = doHmac(keyBytes, strBytes); return toHexString(hmacResult); } /** * hmac-sha256加密 * * @param key 秘钥 * @param str 待加密的报文 * @return String */ @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming") public static String hmacEncrypt(String key, String str) { try { return hmacEncrypt(key, str, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * hmac-sha256加密 * * @param key 秘钥 * @param str 待校验的报文 * @param sha 加密后的串 * @param charset 编码格式 * @return boolean */ @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming") public static boolean hmacVerify(String key, String str, String sha, String charset) throws UnsupportedEncodingException { assert StringUtils.isNotBlank(sha); assert StringUtils.isNotBlank(str); assert StringUtils.isNotBlank(key); assert StringUtils.isNotBlank(charset); byte[] src = ByteUtils.fromHexString(sha); byte[] keyBytes = key.getBytes(charset); byte[] strBytes = str.getBytes(charset); byte[] target = doHmac(keyBytes, strBytes); return Arrays.equals(src, target); } /** * hmac-sha256加密 * * @param key 秘钥 * @param str 待校验的报文 * @param sha 加密后的串 * @return boolean */ @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming") public static boolean hmacVerify(String key, String str, String sha) { try { return hmacVerify(key, str, sha, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } @NotNull private static byte[] doHmac(byte[] keyBytes, byte[] strBytes) { Digest mg = new SHA256Digest(); HMac hMac = new HMac(mg); hMac.init(new KeyParameter(keyBytes)); hMac.update(strBytes, 0, strBytes.length); byte[] hmacResult = new byte[hMac.getMacSize()]; hMac.doFinal(hmacResult, 0); return hmacResult; } private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static String toHexString(byte[] input) { String result = ""; for (int i = 0; i < input.length; i++) { result += HEX_CHARS[(input[i] >>> 4) & 0x0f]; result += HEX_CHARS[(input[i]) & 0x0f]; } return result; } // /** // * 将byte转为16进制 // * // * @param bytes bytes // * @return String // */ // private static String byte2Hex(byte[] bytes) { // StringBuilder stringBuffer = new StringBuilder(); // String temp; // for (byte aByte : bytes) { // temp = Integer.toHexString(aByte & 0xFF); // if (temp.length() == 1) { // //1得到一位的进行补0操作 // stringBuffer.append("0"); // } // stringBuffer.append(temp); // } // return stringBuffer.toString(); // } }
单元测试:
package com.iscas.common.tools.core.security; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; /** * * * @author zhuquanwen * @version 1.0 * @date 2021/3/14 13:36 * @since jdk1.8 */ public class Sha256UtilsTests { @Test public void test() throws UnsupportedEncodingException, NoSuchAlgorithmException { String sha256Str = Sha256Utils.encrypt("123456"); System.out.println(sha256Str); } @Test public void test2() { String iscas = Sha256Utils.hmacEncrypt("iscas", "123456"); System.out.println(iscas); Assertions.assertNotNull(iscas); } @Test public void test3() { String sha = Sha256Utils.hmacEncrypt("iscas", "123456"); System.out.println(sha); boolean res = Sha256Utils.hmacVerify("iscas", "123456", sha); Assertions.assertTrue(res); } }
sm3为单向加密
引入依赖(MAVEN):
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
引入依赖(gradle):
// https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
工具类:
package com.iscas.common.tools.core.security; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import java.io.UnsupportedEncodingException; import java.security.Security; import java.util.Arrays; /** * @author zhuquanwen * @version 1.0 * @date 2023/2/23 9:13 */ public class Sm3Utils { private static final String DEFAULT_ENCODING = "UTF-8"; private Sm3Utils() { } static { Security.addProvider(new BouncyCastleProvider()); } /** * 不使用密钥 * sm3算法加密 * * @param str 待加密字符串 * @return 返回加密后,固定长度=64的16进制字符串 */ public static String encrypt(String str) { try { return encrypt(str, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 不使用密钥 * sm3算法加密 * * @param str 待加密字符串 * @param charset 编码格式 * @return 返回加密后,固定长度=64的16进制字符串 */ public static String encrypt(String str, String charset) throws UnsupportedEncodingException { // 将返回的hash值转换成16进制字符串 String resultHexString = ""; // 将字符串转换成byte数组 byte[] srcData = str.getBytes(charset); // 调用hash byte[] resultHash = hash(srcData); // 将返回的hash值转换成16进制字符串 return ByteUtils.toHexString(resultHash); } /** * 使用密钥 * sm3算法加密 * * @param key 密钥key * @param str 待加密字符串 * @return 返回加密后,固定长度=64的16进制字符串 */ public static String hmacEncrypt(String key, String str) { try { return hmacEncrypt(key, str, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 使用密钥 * sm3算法加密 * * @param key 密钥key * @param str 待加密字符串 * @param charset 编码格式 * @return 返回加密后,固定长度=64的16进制字符串 */ public static String hmacEncrypt(String key, String str, String charset) throws UnsupportedEncodingException { byte[] keyBytes = key.getBytes(charset); byte[] strBytes = str.getBytes(charset); byte[] hmac = hmac(keyBytes, strBytes); return ByteUtils.toHexString(hmac); } /** * 判断源数据与加密数据是否一致 * 通过验证原数组和生成的hash数组是否为同一数组,验证两者是否为同意数据 * * @param srcStr 源字符串 * @param sm3Str 加密后的数据 * @return 校验结果 */ public static boolean verify(String srcStr, String sm3Str) { try { return verifyUseCharset(srcStr, sm3Str, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 判断源数据与加密数据是否一致 * 通过验证原数组和生成的hash数组是否为同一数组,验证两者是否为同意数据 * * @param srcStr 源字符串 * @param sm3Str 加密后的数据 * @param charset 编码格式 * @return 校验结果 */ public static boolean verifyUseCharset(String srcStr, String sm3Str, String charset) throws UnsupportedEncodingException { byte[] srcData = srcStr.getBytes(charset); byte[] sm3Hash = ByteUtils.fromHexString(sm3Str); //通过摘要加密生成新的hash数组 byte[] newHash = hash(srcData); return Arrays.equals(newHash, sm3Hash); } /** * 判断源数据与加密数据是否一致,使用HMAC * 通过验证原数组和生成的hash数组是否为同一数组,验证两者是否为同意数据 * * @param srcStr 源字符串 * @param sm3Str 加密后的数据 * @return 校验结果 */ public static boolean hmacVerify(String key, String srcStr, String sm3Str) { try { return hmacVerifyUseCharset(key, srcStr, sm3Str, DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 判断源数据与加密数据是否一致,使用HMAC * 通过验证原数组和生成的hash数组是否为同一数组,验证两者是否为同意数据 * * @param srcStr 源字符串 * @param sm3Str 加密后的数据 * @param charset 编码格式 * @return 校验结果 */ public static boolean hmacVerifyUseCharset(String key, String srcStr, String sm3Str, String charset) throws UnsupportedEncodingException { byte[] keyData = key.getBytes(charset); byte[] srcData = srcStr.getBytes(charset); byte[] sm3Hash = ByteUtils.fromHexString(sm3Str); //通过摘要加密生成新的hash数组 byte[] newHash = hmac(keyData, srcData); return Arrays.equals(newHash, sm3Hash); } /** * 返回长度=32的byte数组 * 生成对应的hash值 * * @param srcData * @return byte[] */ public static byte[] hash(byte[] srcData) { //摘要加密 SM3Digest digest = new SM3Digest(); //使用指定的数组更新摘要 digest.update(srcData, 0, srcData.length); //获取摘要的长度 byte[] hash = new byte[digest.getDigestSize()]; digest.doFinal(hash, 0); return hash; } /** * 通过密钥进行加密 * 指定密钥进行加密 * * @param key 密钥 * @param srcData 被加密的byte数组 * @return byte[] */ public static byte[] hmac(byte[] key, byte[] srcData) { KeyParameter keyParameter = new KeyParameter(key); SM3Digest digest = new SM3Digest(); HMac mac = new HMac(digest); mac.init(keyParameter); mac.update(srcData, 0, srcData.length); byte[] result = new byte[mac.getMacSize()]; mac.doFinal(result, 0); return result; } ========================================下面的方式为使用JDK原生实现,但无法使用Hmac,暂时注释掉================================= // // private static char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; // public static final byte[] IV = {0x73, (byte) 0x80, 0x16, 0x6f, 0x49, 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42, // (byte) 0xd7, (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30, (byte) 0xbc, (byte) 0x16, 0x31, // 0x38, (byte) 0xaa, (byte) 0xe3, (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e, 0x4e}; // private static final Integer TJ_15 = Integer.valueOf("79cc4519", 16); // private static final Integer TJ_63 = Integer.valueOf("7a879d8a", 16); // private static final byte[] FirstPadding = {(byte) 0x80}; // private static final byte[] ZeroPadding = {(byte) 0x00}; // // // public static String encrypt(String source) throws IOException { // byte[] bytes = encrypt(source.getBytes(StandardCharsets.UTF_8)); // return bytesToHexString(bytes); // } // // public static byte[] encrypt(byte[] source) throws IOException { // byte[] m1 = padding(source); // int n = m1.length / (512 / 8); // // byte[] b; // byte[] vi = IV.clone(); // byte[] vi1 = null; // for (int i = 0; i < n; i++) { // b = Arrays.copyOfRange(m1, i * 64, (i + 1) * 64); // vi1 = CF(vi, b); // vi = vi1; // } // return vi1; // } // // // private static int T(int j) { // if (j >= 0 && j <= 15) { // return TJ_15.intValue(); // } else if (j >= 16 && j <= 63) { // return TJ_63.intValue(); // } else { // throw new RuntimeException("data invalid"); // } // } // // private static Integer FF(Integer x, Integer y, Integer z, int j) { // if (j >= 0 && j <= 15) { // return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue()); // } else if (j >= 16 && j <= 63) { // return Integer.valueOf( // (x.intValue() & y.intValue()) | (x.intValue() & z.intValue()) | (y.intValue() & z.intValue())); // } else { // throw new RuntimeException("data invalid"); // } // } // // private static Integer GG(Integer x, Integer y, Integer z, int j) { // if (j >= 0 && j <= 15) { // return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue()); // } else if (j >= 16 && j <= 63) { // return Integer.valueOf((x.intValue() & y.intValue()) | (~x.intValue() & z.intValue())); // } else { // throw new RuntimeException("data invalid"); // } // } // // private static Integer P0(Integer x) { // return Integer // .valueOf(x.intValue() ^ Integer.rotateLeft(x.intValue(), 9) ^ Integer.rotateLeft(x.intValue(), 17)); // } // // private static Integer P1(Integer x) { // return Integer.valueOf(x.intValue() ^ Integer.rotateLeft(x.intValue(), 15) ^ Integer.rotateLeft(x.intValue(), 23)); // } // // private static byte[] padding(byte[] source) throws IOException { // if (source.length >= 0x2000000000000000L) { // throw new RuntimeException("src data invalid."); // } // long l = source.length * 8; // long k = 448 - (l + 1) % 512; // if (k < 0) { // k = k + 512; // } // // try (ByteArrayOutputStream baos = new ByteArrayOutputStream();) { // baos.write(source); // baos.write(FirstPadding); // long i = k - 7; // while (i > 0) { // baos.write(ZeroPadding); // i -= 8; // } // baos.write(long2bytes(l)); // return baos.toByteArray(); // } // } // // private static byte[] long2bytes(long l) { // byte[] bytes = new byte[8]; // for (int i = 0; i < 8; i++) { // bytes[i] = (byte) (l >>> ((7 - i) * 8)); // } // return bytes; // } // // private static byte[] CF(byte[] vi, byte[] bi) throws IOException { // int a, b, c, d, e, f, g, h; // a = toInteger(vi, 0); // b = toInteger(vi, 1); // c = toInteger(vi, 2); // d = toInteger(vi, 3); // e = toInteger(vi, 4); // f = toInteger(vi, 5); // g = toInteger(vi, 6); // h = toInteger(vi, 7); // // int[] w = new int[68]; // int[] w1 = new int[64]; // for (int i = 0; i < 16; i++) { // w[i] = toInteger(bi, i); // } // for (int j = 16; j < 68; j++) { // w[j] = P1(w[j - 16] ^ w[j - 9] ^ Integer.rotateLeft(w[j - 3], 15)) ^ Integer.rotateLeft(w[j - 13], 7) // ^ w[j - 6]; // } // for (int j = 0; j < 64; j++) { // w1[j] = w[j] ^ w[j + 4]; // } // int ss1, ss2, tt1, tt2; // for (int j = 0; j < 64; j++) { // ss1 = Integer.rotateLeft(Integer.rotateLeft(a, 12) + e + Integer.rotateLeft(T(j), j), 7); // ss2 = ss1 ^ Integer.rotateLeft(a, 12); // tt1 = FF(a, b, c, j) + d + ss2 + w1[j]; // tt2 = GG(e, f, g, j) + h + ss1 + w[j]; // d = c; // c = Integer.rotateLeft(b, 9); // b = a; // a = tt1; // h = g; // g = Integer.rotateLeft(f, 19); // f = e; // e = P0(tt2); // } // byte[] v = toByteArray(a, b, c, d, e, f, g, h); // for (int i = 0; i < v.length; i++) { // v[i] = (byte) (v[i] ^ vi[i]); // } // return v; // } // // private static int toInteger(byte[] source, int index) { // StringBuilder valueStr = new StringBuilder(""); // for (int i = 0; i < 4; i++) { // valueStr.append(chars[(byte) ((source[index * 4 + i] & 0xF0) >> 4)]); // valueStr.append(chars[(byte) (source[index * 4 + i] & 0x0F)]); // } // return Long.valueOf(valueStr.toString(), 16).intValue(); // // } // // private static byte[] toByteArray(int a, int b, int c, int d, int e, int f, int g, int h) throws IOException { // try (ByteArrayOutputStream baos = new ByteArrayOutputStream(32);) { // baos.write(toByteArray(a)); // baos.write(toByteArray(b)); // baos.write(toByteArray(c)); // baos.write(toByteArray(d)); // baos.write(toByteArray(e)); // baos.write(toByteArray(f)); // baos.write(toByteArray(g)); // baos.write(toByteArray(h)); // return baos.toByteArray(); // } // } // // private static byte[] toByteArray(int i) { // byte[] byteArray = new byte[4]; // byteArray[0] = (byte) (i >>> 24); // byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16); // byteArray[2] = (byte) ((i & 0xFFFF) >>> 8); // byteArray[3] = (byte) (i & 0xFF); // return byteArray; // } // // private static String byteToHexString(byte ib) { // char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; // char[] ob = new char[2]; // ob[0] = Digit[(ib >>> 4) & 0X0f]; // ob[1] = Digit[ib & 0X0F]; // return new String(ob); // } // // private static String bytesToHexString(byte[] bytes) { // StringBuilder sb = new StringBuilder(); // for (byte aByte : bytes) { // sb.append(byteToHexString(aByte)); // } // return sb.toString(); // } }
单元测试:
package com.iscas.common.tools.core.security; import org.junit.jupiter.api.Test; import java.io.IOException; /** * @author zhuquanwen * @version 1.0 * @date 2023/2/23 13:34 */ class Sm3UtilsTest { /** * 测试普通sm3加密 */ @Test public void testEncode() { System.out.println(Sm3Utils.encrypt("123456")); } /** * 测试使用密钥hmac加密 */ @Test public void testEncode2() { System.out.println(Sm3Utils.hmacEncrypt("iscas", "123456")); System.out.println(Sm3Utils.hmacEncrypt("ISCAS", "123456")); } /** * 测试普通加密校验 */ @Test public void testVerify() { System.out.println(Sm3Utils.verify("123456", "207cf410532f92a47dee245ce9b11ff71f578ebd763eb3bbea44ebd043d018fb")); } /** * 测试HMAC加密校验 */ @Test public void testHmacVerify() { System.out.println(Sm3Utils.hmacVerify("iscas", "123456", "a0bc6c6ac39712c81f805c8a80407892b1006ee27a04c2b0a15fec84b5065225")); } }
sm4为对称加密
工具类:
package com.iscas.common.tools.core.security; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.SecureRandom; import java.security.Security; import java.util.Arrays; import java.util.Base64; /** * @author zhuquanwen * @version 1.0 * @date 2023/2/23 17:03 */ public class Sm4Utils { private Sm4Utils() { } // 初始化算法提供者信息. static { Security.addProvider(new BouncyCastleProvider()); } /** * 数据编码. */ private static final String CHARSET_UTF8 = "UTF-8"; /** * 秘钥空间大小. */ public static final int SM4_KEY_SIZE = 128; /** * 默认秘钥空间为128,Key的长度是16. */ public static final int SM4_KEY_LENGTH = 16; /** * 算法编号. */ public static final String SM4_NAME = "SM4"; /** * CBC模式串. */ public static final String SM4_NAME_ECB = "SM4/CBC/PKCS5Padding"; // /** // * 首次加密初始向量. // */ // public static final byte[] SM4_KEY_IV = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}; /** * 密钥 */ public static final String CRYPT_KEY = "10f5dd7c2d45d247"; private static final String CKEY = "encryptionIntVec"; /** * 对文本内容进行加密. * * @param plainText 待加密明文内容. * @return 加密的密文. */ public static String encrypt(String plainText) throws Exception { return encodeByCbc(plainText, CRYPT_KEY); } /** * 对文本内容进行加密. * * @param plainText 待加密明文内容. * @param sm4Key SM4秘钥. * @return 加密的密文. */ public static String encrypt(String plainText, String sm4Key) throws Exception { return encodeByCbc(plainText, sm4Key); } /** * 对文本密文进行解密. * * @param cipherText 待解密密文. * @return 解密的明文. * @throws Exception . */ public static String decrypt(String cipherText) throws Exception { return decodeByCbc(cipherText, CRYPT_KEY); } /** * 对文本密文进行解密. * * @param cipherText 待解密密文. * @param sm4Key SM4秘钥. * @return 解密的明文. * @throws Exception . */ public static String decrypt(String cipherText, String sm4Key) throws Exception { return decodeByCbc(cipherText, sm4Key); } /** * 生成SM4算法的KEY. * * @return 生成的SM4秘钥. * @throws Exception . */ public static String generateSm4Key() throws Exception { return Base64.getEncoder().encodeToString(generateSm4Key(SM4_KEY_SIZE)); } /** * 生成SM4算法的KEY. * * @param sm4Key 指定秘钥空间大小. * @return 生成的SM4秘钥. * @throws Exception . */ private static byte[] generateSm4Key(int sm4Key) throws Exception { KeyGenerator keyGenerator = KeyGenerator.getInstance(SM4_NAME, BouncyCastleProvider.PROVIDER_NAME); keyGenerator.init(sm4Key, new SecureRandom()); return keyGenerator.generateKey().getEncoded(); } /** * 对字节数组内容进行加密. * * @param plainBytes 待加密明文内容. * @param sm4Key SM4秘钥. * @return 加密的密文. */ public static byte[] encodeBytes(byte[] plainBytes, String sm4Key) throws Exception { // 秘钥位数处理转换. sm4Key = sm4KeyPadding(sm4Key); // base64格式秘钥转换:sm4Key to byte[]. byte[] sm4KeyBytes = Base64.getDecoder().decode(sm4Key); // 使用转换后的原文和秘钥进行加密操作. return encodeCbcPadding(plainBytes, sm4KeyBytes); } /** * 对字节数组密文进行解密. * * @param cipherBytes 待解密密文. * @param sm4Key SM4秘钥. * @return 解密的明文. */ public static byte[] decodeBytes(byte[] cipherBytes, String sm4Key) throws Exception { // 秘钥位数处理转换. sm4Key = sm4KeyPadding(sm4Key); // base64格式秘钥转换:sm4Key to byte[]. byte[] keyBts = Base64.getDecoder().decode(sm4Key); // 使用转换后的密文和秘钥进行解密操作 return decryptCbcPadding(cipherBytes, keyBts); } /** * 基于CBC模式进行SM4加密. * * @param plainText 待加密明文. * @param sm4Key Base64格式秘钥. * @return 加密后Base64格式密文. * @throws Exception 可能异常. */ private static String encodeByCbc(String plainText, String sm4Key) throws Exception { // 秘钥位数处理转换. sm4Key = sm4KeyPadding(sm4Key); // base64格式秘钥转换:sm4Key to byte[]. byte[] sm4KeyBytes = Base64.getDecoder().decode(sm4Key); // String格式原文转换:plainText to byte[]. byte[] plainBytes = plainText.getBytes(CHARSET_UTF8); // 使用转换后的原文和秘钥进行加密操作. byte[] cipherBytes = encodeCbcPadding(plainBytes, sm4KeyBytes); // 对加密结果使用base64进行编码:cipherBytes to Base64格式. return Base64.getEncoder().encodeToString(cipherBytes); } /** * SM4算法的CBC模式加密. * * @param plainBytes 待加密明文. * @param sm4Key Base64格式秘钥. * @return 加密后byte[]格式密文. * @throws Exception 可能异常. */ private static byte[] encodeCbcPadding(byte[] plainBytes, byte[] sm4Key) throws Exception { Cipher cipher = generateSm4EcbCipher(SM4_NAME_ECB, Cipher.ENCRYPT_MODE, sm4Key); return cipher.doFinal(plainBytes); } /** * 基于CBC模式进行SM4解密. * * @param cipherText 待解密密文. * @param sm4Key Base64格式秘钥. * @return 解密后原文. * @throws Exception 可能异常. */ private static String decodeByCbc(String cipherText, String sm4Key) throws Exception { // 秘钥位数处理转换. sm4Key = sm4KeyPadding(sm4Key); // base64格式秘钥转换:sm4Key to byte[]. byte[] keyBts = Base64.getDecoder().decode(sm4Key); // base64格式密文转换:cipherText to byte[]. byte[] cipherBts = Base64.getDecoder().decode(cipherText); // 使用转换后的密文和秘钥进行解密操作 byte[] plainBytes = decryptCbcPadding(cipherBts, keyBts); // 将解密结果转换为字符串:srcData to String. return new String(plainBytes, CHARSET_UTF8); } /** * SM4算法的CBC模式解密. * * @param cipherBytes 待加密密文. * @param sm4Key Base64格式秘钥. * @return 解密后byte[]格式密文. * @throws Exception 可能异常. */ private static byte[] decryptCbcPadding(byte[] cipherBytes, byte[] sm4Key) throws Exception { Cipher cipher = generateSm4EcbCipher(SM4_NAME_ECB, Cipher.DECRYPT_MODE, sm4Key); return cipher.doFinal(cipherBytes); } /** * 针对错误的秘钥进行补齐或除余操作. * * @param sm4Key Base64格式秘钥. * @return 补齐或除余后的结果. */ private static String sm4KeyPadding(String sm4Key) { String targetSm4Key; byte[] targetSm4KeyBts; if (null == sm4Key) { targetSm4Key = ""; return targetSm4Key; } byte[] sm4KeyBytes = Base64.getDecoder().decode(sm4Key); // 若Key超长,则除去多余的内容. if (sm4KeyBytes.length > SM4_KEY_LENGTH) { targetSm4KeyBts = new byte[SM4_KEY_LENGTH]; System.arraycopy(sm4KeyBytes, 0, targetSm4KeyBts, 0, SM4_KEY_LENGTH); } // 若Key较短,则补齐多余的内容. else if (sm4KeyBytes.length < SM4_KEY_LENGTH) { targetSm4KeyBts = new byte[SM4_KEY_LENGTH]; System.arraycopy(sm4KeyBytes, 0, targetSm4KeyBts, 0, sm4KeyBytes.length); Arrays.fill(targetSm4KeyBts, sm4KeyBytes.length, SM4_KEY_LENGTH, (byte) 1); } else { targetSm4KeyBts = sm4KeyBytes; } // 以Base64格式返回Key. return Base64.getEncoder().encodeToString(targetSm4KeyBts); } /** * 生成SM4算法实例. * * @param sm4Name 算法名称. * @param sm4Mode 加密模式. * @param sm4Key 秘钥内容. * @return SM4算法实例. * @throws Exception 可能异常. */ private static Cipher generateSm4EcbCipher(String sm4Name, int sm4Mode, byte[] sm4Key) throws Exception { Cipher cipher = Cipher.getInstance(sm4Name, BouncyCastleProvider.PROVIDER_NAME); Key secretKey = new SecretKeySpec(sm4Key, SM4_NAME); IvParameterSpec ivParameterSpec = new IvParameterSpec(CKEY.getBytes(StandardCharsets.UTF_8)); cipher.init(sm4Mode, secretKey, ivParameterSpec); return cipher; } }
单元测试
package com.iscas.common.tools.core.security; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * @author zhuquanwen * @version 1.0 * @date 2023/2/23 17:16 */ class Sm4UtilsTest { /** * 默认秘钥加密 * */ @Test void encrypt() throws Exception { String encrypt = Sm4Utils.encrypt("123456"); System.out.println(encrypt); Assertions.assertNotNull(encrypt); } /** * 默认秘钥解密 * */ @Test void decrypt() throws Exception { String encrypt = Sm4Utils.encrypt("123456"); String decrypt = Sm4Utils.decrypt(encrypt); Assertions.assertEquals(decrypt, "123456"); } /** * 自定义秘钥加解密 * */ @Test void test3() throws Exception { String enKey = Sm4Utils.generateSm4Key(); String encrypt = Sm4Utils.encrypt("123456", enKey); System.out.println(encrypt); String decrypt = Sm4Utils.decrypt(encrypt, enKey); Assertions.assertEquals(decrypt, "123456"); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。