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;
/**
* AES加解密工具类
*
* @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;
/**
* BCrypt加解密工具类
* @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):
<dependency>
<groupId>org.bouncycastlegroupId>
<artifactId>bcprov-jdk15onartifactId>
<version>1.70version>
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):
<dependency>
<groupId>org.bouncycastlegroupId>
<artifactId>bcprov-jdk15onartifactId>
<version>1.70version>
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):
<dependency>
<groupId>org.bouncycastlegroupId>
<artifactId>bcprov-jdk15onartifactId>
<version>1.70version>
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");
}
}