This commit is contained in:
Looly 2020-09-14 19:06:06 +08:00
parent 7d81b20e2b
commit 999c1d80d4
7 changed files with 257 additions and 64 deletions

View File

@ -0,0 +1,49 @@
package cn.hutool.crypto;
import javax.crypto.Cipher;
/**
* Cipher模式的枚举封装
*
* @author looly
* @since 5.4.3
*/
public enum CipherMode {
/**
* 加密模式
*/
encrypt(Cipher.ENCRYPT_MODE),
/**
* 解密模式
*/
decrypt(Cipher.DECRYPT_MODE),
/**
* 包装模式
*/
wrap(Cipher.WRAP_MODE),
/**
* 拆包模式
*/
unwrap(Cipher.UNWRAP_MODE);
/**
* 构造
*
* @param value {@link Cipher}
*/
CipherMode(int value) {
this.value = value;
}
private final int value;
/**
* 获取枚举值对应的int表示
*
* @return 枚举值对应的int表示
*/
public int getValue() {
return this.value;
}
}

View File

@ -618,11 +618,19 @@ public class KeyUtil {
*/ */
public static String getAlgorithmAfterWith(String algorithm) { public static String getAlgorithmAfterWith(String algorithm) {
Assert.notNull(algorithm, "algorithm must be not null !"); Assert.notNull(algorithm, "algorithm must be not null !");
if(StrUtil.startWithIgnoreCase(algorithm, "ECIESWith")){
return "EC";
}
int indexOfWith = StrUtil.lastIndexOfIgnoreCase(algorithm, "with"); int indexOfWith = StrUtil.lastIndexOfIgnoreCase(algorithm, "with");
if (indexOfWith > 0) { if (indexOfWith > 0) {
algorithm = StrUtil.subSuf(algorithm, indexOfWith + "with".length()); algorithm = StrUtil.subSuf(algorithm, indexOfWith + "with".length());
} }
if ("ECDSA".equalsIgnoreCase(algorithm) || "SM2".equalsIgnoreCase(algorithm)) { if ("ECDSA".equalsIgnoreCase(algorithm)
|| "SM2".equalsIgnoreCase(algorithm)
|| "ECIES".equalsIgnoreCase(algorithm)
) {
algorithm = "EC"; algorithm = "EC";
} }
return algorithm; return algorithm;

View File

@ -3,6 +3,7 @@ package cn.hutool.crypto.asymmetric;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FastByteArrayOutputStream; import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.crypto.CryptoException; import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm; import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
@ -10,9 +11,12 @@ import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import java.io.IOException; import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key; import java.security.Key;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
/** /**
* 非对称加密算法 * 非对称加密算法
@ -26,19 +30,30 @@ import java.security.PublicKey;
* </pre> * </pre>
* *
* @author Looly * @author Looly
*
*/ */
public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto> { public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto> {
/** Cipher负责完成加密或解密工作 */ /**
* Cipher负责完成加密或解密工作
*/
protected Cipher cipher; protected Cipher cipher;
/** 加密的块大小 */ /**
* 加密的块大小
*/
protected int encryptBlockSize = -1; protected int encryptBlockSize = -1;
/** 解密的块大小 */ /**
* 解密的块大小
*/
protected int decryptBlockSize = -1; protected int decryptBlockSize = -1;
/**
* 算法参数
*/
private AlgorithmParameterSpec algorithmParameterSpec;
// ------------------------------------------------------------------ Constructor start // ------------------------------------------------------------------ Constructor start
/** /**
* 构造创建新的私钥公钥对 * 构造创建新的私钥公钥对
* *
@ -63,9 +78,9 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
* 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br> * 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
* 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密 * 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密
* *
* @param algorithm {@link SymmetricAlgorithm} * @param algorithm {@link SymmetricAlgorithm}
* @param privateKeyStr 私钥Hex或Base64表示 * @param privateKeyStr 私钥Hex或Base64表示
* @param publicKeyStr 公钥Hex或Base64表示 * @param publicKeyStr 公钥Hex或Base64表示
*/ */
public AsymmetricCrypto(AsymmetricAlgorithm algorithm, String privateKeyStr, String publicKeyStr) { public AsymmetricCrypto(AsymmetricAlgorithm algorithm, String privateKeyStr, String publicKeyStr) {
this(algorithm.getValue(), SecureUtil.decode(privateKeyStr), SecureUtil.decode(publicKeyStr)); this(algorithm.getValue(), SecureUtil.decode(privateKeyStr), SecureUtil.decode(publicKeyStr));
@ -75,9 +90,9 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
* 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br> * 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
* 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密 * 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密
* *
* @param algorithm {@link SymmetricAlgorithm} * @param algorithm {@link SymmetricAlgorithm}
* @param privateKey 私钥 * @param privateKey 私钥
* @param publicKey 公钥 * @param publicKey 公钥
*/ */
public AsymmetricCrypto(AsymmetricAlgorithm algorithm, byte[] privateKey, byte[] publicKey) { public AsymmetricCrypto(AsymmetricAlgorithm algorithm, byte[] privateKey, byte[] publicKey) {
this(algorithm.getValue(), privateKey, publicKey); this(algorithm.getValue(), privateKey, publicKey);
@ -87,9 +102,9 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
* 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br> * 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
* 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密 * 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密
* *
* @param algorithm {@link SymmetricAlgorithm} * @param algorithm {@link SymmetricAlgorithm}
* @param privateKey 私钥 * @param privateKey 私钥
* @param publicKey 公钥 * @param publicKey 公钥
* @since 3.1.1 * @since 3.1.1
*/ */
public AsymmetricCrypto(AsymmetricAlgorithm algorithm, PrivateKey privateKey, PublicKey publicKey) { public AsymmetricCrypto(AsymmetricAlgorithm algorithm, PrivateKey privateKey, PublicKey publicKey) {
@ -100,9 +115,9 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
* 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br> * 构造 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
* 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密 * 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密
* *
* @param algorithm 非对称加密算法 * @param algorithm 非对称加密算法
* @param privateKeyBase64 私钥Base64 * @param privateKeyBase64 私钥Base64
* @param publicKeyBase64 公钥Base64 * @param publicKeyBase64 公钥Base64
*/ */
public AsymmetricCrypto(String algorithm, String privateKeyBase64, String publicKeyBase64) { public AsymmetricCrypto(String algorithm, String privateKeyBase64, String publicKeyBase64) {
this(algorithm, Base64.decode(privateKeyBase64), Base64.decode(publicKeyBase64)); this(algorithm, Base64.decode(privateKeyBase64), Base64.decode(publicKeyBase64));
@ -110,30 +125,30 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
/** /**
* 构造 * 构造
* * <p>
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br> * 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
* 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密 * 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密
* *
* @param algorithm 算法 * @param algorithm 算法
* @param privateKey 私钥 * @param privateKey 私钥
* @param publicKey 公钥 * @param publicKey 公钥
*/ */
public AsymmetricCrypto(String algorithm, byte[] privateKey, byte[] publicKey) { public AsymmetricCrypto(String algorithm, byte[] privateKey, byte[] publicKey) {
this(algorithm, // this(algorithm, //
SecureUtil.generatePrivateKey(algorithm, privateKey), // KeyUtil.generatePrivateKey(algorithm, privateKey), //
SecureUtil.generatePublicKey(algorithm, publicKey)// KeyUtil.generatePublicKey(algorithm, publicKey)//
); );
} }
/** /**
* 构造 * 构造
* * <p>
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br> * 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
* 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密 * 私钥和公钥可以单独传入一个如此则只能使用此钥匙来做加密或者解密
* *
* @param algorithm 算法 * @param algorithm 算法
* @param privateKey 私钥 * @param privateKey 私钥
* @param publicKey 公钥 * @param publicKey 公钥
* @since 3.1.1 * @since 3.1.1
*/ */
public AsymmetricCrypto(String algorithm, PrivateKey privateKey, PublicKey publicKey) { public AsymmetricCrypto(String algorithm, PrivateKey privateKey, PublicKey publicKey) {
@ -177,6 +192,28 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
this.decryptBlockSize = decryptBlockSize; this.decryptBlockSize = decryptBlockSize;
} }
/**
* 获取{@link AlgorithmParameterSpec}<br>
* 在某些算法中需要特别的参数例如在ECIES中此处为IESParameterSpec
*
* @return {@link AlgorithmParameterSpec}
* @since 5.4.3
*/
public AlgorithmParameterSpec getAlgorithmParameterSpec() {
return algorithmParameterSpec;
}
/**
* 设置{@link AlgorithmParameterSpec}<br>
* 在某些算法中需要特别的参数例如在ECIES中此处为IESParameterSpec
*
* @param algorithmParameterSpec {@link AlgorithmParameterSpec}
* @since 5.4.3
*/
public void setAlgorithmParameterSpec(AlgorithmParameterSpec algorithmParameterSpec) {
this.algorithmParameterSpec = algorithmParameterSpec;
}
@Override @Override
public AsymmetricCrypto init(String algorithm, PrivateKey privateKey, PublicKey publicKey) { public AsymmetricCrypto init(String algorithm, PrivateKey privateKey, PublicKey publicKey) {
super.init(algorithm, privateKey, publicKey); super.init(algorithm, privateKey, publicKey);
@ -185,10 +222,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
} }
// --------------------------------------------------------------------------------- Encrypt // --------------------------------------------------------------------------------- Encrypt
/** /**
* 加密 * 加密
* *
* @param data 被加密的bytes * @param data 被加密的bytes
* @param keyType 私钥或公钥 {@link KeyType} * @param keyType 私钥或公钥 {@link KeyType}
* @return 加密后的bytes * @return 加密后的bytes
*/ */
@ -197,12 +235,12 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
final Key key = getKeyByType(keyType); final Key key = getKeyByType(keyType);
lock.lock(); lock.lock();
try { try {
cipher.init(Cipher.ENCRYPT_MODE, key); initCipher(Cipher.ENCRYPT_MODE, key);
if(this.encryptBlockSize < 0){ if (this.encryptBlockSize < 0) {
// 在引入BC库情况下自动获取块大小 // 在引入BC库情况下自动获取块大小
final int blockSize = this.cipher.getBlockSize(); final int blockSize = this.cipher.getBlockSize();
if(blockSize > 0){ if (blockSize > 0) {
this.encryptBlockSize = blockSize; this.encryptBlockSize = blockSize;
} }
} }
@ -216,10 +254,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
} }
// --------------------------------------------------------------------------------- Decrypt // --------------------------------------------------------------------------------- Decrypt
/** /**
* 解密 * 解密
* *
* @param data 被解密的bytes * @param data 被解密的bytes
* @param keyType 私钥或公钥 {@link KeyType} * @param keyType 私钥或公钥 {@link KeyType}
* @return 解密后的bytes * @return 解密后的bytes
*/ */
@ -228,12 +267,12 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
final Key key = getKeyByType(keyType); final Key key = getKeyByType(keyType);
lock.lock(); lock.lock();
try { try {
cipher.init(Cipher.DECRYPT_MODE, key); initCipher(Cipher.DECRYPT_MODE, key);
if(this.decryptBlockSize < 0){ if (this.decryptBlockSize < 0) {
// 在引入BC库情况下自动获取块大小 // 在引入BC库情况下自动获取块大小
final int blockSize = this.cipher.getBlockSize(); final int blockSize = this.cipher.getBlockSize();
if(blockSize > 0){ if (blockSize > 0) {
this.decryptBlockSize = blockSize; this.decryptBlockSize = blockSize;
} }
} }
@ -252,11 +291,23 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
* 获得加密或解密器 * 获得加密或解密器
* *
* @return 加密或解密 * @return 加密或解密
* @deprecated 拼写错误请使用{@link #getCipher()}
*/ */
@Deprecated
public Cipher getClipher() { public Cipher getClipher() {
return cipher; return cipher;
} }
/**
* 获得加密或解密器
*
* @return 加密或解密
* @since 5.4.3
*/
public Cipher getCipher() {
return cipher;
}
/** /**
* 初始化{@link Cipher}默认尝试加载BC库 * 初始化{@link Cipher}默认尝试加载BC库
* *
@ -269,12 +320,12 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
/** /**
* 加密或解密 * 加密或解密
* *
* @param data 被加密或解密的内容数据 * @param data 被加密或解密的内容数据
* @param maxBlockSize 最大块分段大小 * @param maxBlockSize 最大块分段大小
* @return 加密或解密后的数据 * @return 加密或解密后的数据
* @throws IllegalBlockSizeException 分段异常 * @throws IllegalBlockSizeException 分段异常
* @throws BadPaddingException padding错误异常 * @throws BadPaddingException padding错误异常
* @throws IOException IO异常不会被触发 * @throws IOException IO异常不会被触发
*/ */
private byte[] doFinal(byte[] data, int maxBlockSize) throws IllegalBlockSizeException, BadPaddingException, IOException { private byte[] doFinal(byte[] data, int maxBlockSize) throws IllegalBlockSizeException, BadPaddingException, IOException {
// 模长 // 模长
@ -292,17 +343,16 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
/** /**
* 分段加密或解密 * 分段加密或解密
* *
* @param data 数据 * @param data 数据
* @param maxBlockSize 最大分段的段大小不能为小于1 * @param maxBlockSize 最大分段的段大小不能为小于1
* @return 加密或解密后的数据 * @return 加密或解密后的数据
* @throws IllegalBlockSizeException 分段异常 * @throws IllegalBlockSizeException 分段异常
* @throws BadPaddingException padding错误异常 * @throws BadPaddingException padding错误异常
* @throws IOException IO异常不会被触发 * @throws IOException IO异常不会被触发
*/ */
private byte[] doFinalWithBlock(byte[] data, int maxBlockSize) throws IllegalBlockSizeException, BadPaddingException, IOException { private byte[] doFinalWithBlock(byte[] data, int maxBlockSize) throws IllegalBlockSizeException, BadPaddingException, IOException {
final int dataLength = data.length; final int dataLength = data.length;
@SuppressWarnings("resource") @SuppressWarnings("resource") final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
int offSet = 0; int offSet = 0;
// 剩余长度 // 剩余长度
@ -319,4 +369,20 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto<AsymmetricCrypto>
return out.toByteArray(); return out.toByteArray();
} }
/**
* 初始化{@link Cipher}
*
* @param mode 模式可选{@link Cipher#ENCRYPT_MODE}或者{@link Cipher#DECRYPT_MODE}
* @param key 密钥
* @throws InvalidAlgorithmParameterException 异常算法错误
* @throws InvalidKeyException 异常KEY错误
*/
private void initCipher(int mode, Key key) throws InvalidAlgorithmParameterException, InvalidKeyException {
if (null != this.algorithmParameterSpec) {
cipher.init(mode, key, this.algorithmParameterSpec);
} else {
cipher.init(mode, key);
}
}
} }

View File

@ -196,6 +196,6 @@ public class BaseAsymmetric<T extends BaseAsymmetric<T>> {
} }
return this.publicKey; return this.publicKey;
} }
throw new CryptoException("Uknown key type: " + type); throw new CryptoException("Unsupported key type: " + type);
} }
} }

View File

@ -1,5 +1,7 @@
package cn.hutool.crypto.asymmetric; package cn.hutool.crypto.asymmetric;
import javax.crypto.Cipher;
/** /**
* 密钥类型 * 密钥类型
* *
@ -7,5 +9,37 @@ package cn.hutool.crypto.asymmetric;
* *
*/ */
public enum KeyType { public enum KeyType {
PrivateKey, PublicKey /**
* 公钥
*/
PublicKey(Cipher.PUBLIC_KEY),
/**
* 私钥
*/
PrivateKey(Cipher.PRIVATE_KEY),
/**
* 密钥
*/
SecretKey(Cipher.SECRET_KEY);
/**
* 构造
*
* @param value {@link Cipher}
*/
KeyType(int value) {
this.value = value;
}
private final int value;
/**
* 获取枚举值对应的int表示
*
* @return 枚举值对应的int表示
*/
public int getValue() {
return this.value;
}
} }

View File

@ -31,4 +31,19 @@ public class KeyUtilTest {
final PublicKey rsaPublicKey = KeyUtil.getRSAPublicKey(aPrivate); final PublicKey rsaPublicKey = KeyUtil.getRSAPublicKey(aPrivate);
Assert.assertEquals(rsaPublicKey, keyPair.getPublic()); Assert.assertEquals(rsaPublicKey, keyPair.getPublic());
} }
/**
* 测试EC和ECIES算法生成的KEY是一致的
*/
@Test
public void generateECIESKeyTest(){
final KeyPair ecies = KeyUtil.generateKeyPair("ECIES");
Assert.assertNotNull(ecies.getPrivate());
Assert.assertNotNull(ecies.getPublic());
byte[] privateKeyBytes = ecies.getPrivate().getEncoded();
final PrivateKey privateKey = KeyUtil.generatePrivateKey("EC", privateKeyBytes);
Assert.assertEquals(ecies.getPrivate(), privateKey);
}
} }

View File

@ -1,6 +1,7 @@
package cn.hutool.crypto.test.asymmetric; package cn.hutool.crypto.test.asymmetric;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.AsymmetricCrypto;
import cn.hutool.crypto.asymmetric.ECIES; import cn.hutool.crypto.asymmetric.ECIES;
import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.KeyType;
import org.junit.Assert; import org.junit.Assert;
@ -12,6 +13,25 @@ public class ECIESTest {
public void eciesTest(){ public void eciesTest(){
final ECIES ecies = new ECIES(); final ECIES ecies = new ECIES();
doTest(ecies, ecies);
}
@Test
public void eciesTest2(){
final ECIES ecies = new ECIES();
final byte[] privateKeyBytes = ecies.getPrivateKey().getEncoded();
final ECIES ecies2 = new ECIES(privateKeyBytes, null);
doTest(ecies, ecies2);
}
/**
* 测试用例
*
* @param cryptoForEncrypt 加密的Crypto
* @param cryptoForDecrypt 解密的Crypto
*/
private void doTest(AsymmetricCrypto cryptoForEncrypt, AsymmetricCrypto cryptoForDecrypt){
String textBase = "我是一段特别长的测试"; String textBase = "我是一段特别长的测试";
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
@ -19,8 +39,9 @@ public class ECIESTest {
} }
// 公钥加密私钥解密 // 公钥加密私钥解密
String encryptStr = ecies.encryptBase64(text.toString(), KeyType.PublicKey); String encryptStr = cryptoForEncrypt.encryptBase64(text.toString(), KeyType.PublicKey);
String decryptStr = StrUtil.utf8Str(ecies.decrypt(encryptStr, KeyType.PrivateKey));
String decryptStr = StrUtil.utf8Str(cryptoForDecrypt.decrypt(encryptStr, KeyType.PrivateKey));
Assert.assertEquals(text.toString(), decryptStr); Assert.assertEquals(text.toString(), decryptStr);
} }
} }