diff --git a/CHANGELOG.md b/CHANGELOG.md index 84418d6f5..b666f7308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * 【core 】 增加DefaultCloneable(pr#459@Gitee) * 【core 】 CollStreamUtil增加是否并行的重载(pr#467@Gitee) * 【core 】 ResourceClassLoader增加缓存(pr#1959@Github) +* 【crypto 】 增加CipherWrapper,增加setRandom(pr#1959@Github) * ### 🐞Bug修复 * 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github) diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java b/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java new file mode 100644 index 000000000..bb9a2578a --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java @@ -0,0 +1,121 @@ +package cn.hutool.crypto; + +import javax.crypto.Cipher; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * {@link Cipher}包装类,提供初始化模式等额外方法
+ * 包装之后可提供自定义或默认的: + * + * + * @author looly + * @since 5.7.17 + */ +public class CipherWrapper { + + private final Cipher cipher; + /** + * 算法参数 + */ + private AlgorithmParameterSpec params; + /** + * 随机数生成器,可自定义随机数种子 + */ + private SecureRandom random; + + /** + * 构造 + * + * @param algorithm 算法名称 + */ + public CipherWrapper(String algorithm) { + this(SecureUtil.createCipher(algorithm)); + } + + /** + * 构造 + * + * @param cipher {@link Cipher} + */ + public CipherWrapper(Cipher cipher) { + this.cipher = cipher; + } + + /** + * 获取{@link AlgorithmParameterSpec}
+ * 在某些算法中,需要特别的参数,例如在ECIES中,此处为IESParameterSpec + * + * @return {@link AlgorithmParameterSpec} + */ + public AlgorithmParameterSpec getParams() { + return this.params; + } + + /** + * 设置 {@link AlgorithmParameterSpec},通常用于加盐或偏移向量 + * + * @param params {@link AlgorithmParameterSpec} + * @return this + */ + public CipherWrapper setParams(AlgorithmParameterSpec params) { + this.params = params; + return this; + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + */ + public CipherWrapper setRandom(SecureRandom random) { + this.random = random; + return this; + } + + /** + * 获取被包装的{@link Cipher} + * + * @return {@link Cipher} + */ + public Cipher getCipher() { + return this.cipher; + } + + /** + * 初始化{@link Cipher}为加密或者解密模式 + * + * @param mode 模式,见{@link Cipher#ENCRYPT_MODE} 或 {@link Cipher#DECRYPT_MODE} + * @param key 密钥 + * @return this + * @throws InvalidKeyException 无效key + * @throws InvalidAlgorithmParameterException 无效算法 + */ + public CipherWrapper initMode(int mode, Key key) + throws InvalidKeyException, InvalidAlgorithmParameterException { + final Cipher cipher = this.cipher; + final AlgorithmParameterSpec params = this.params; + final SecureRandom random = this.random; + if (null != params) { + if (null != random) { + cipher.init(mode, key, params, random); + } else { + cipher.init(mode, key, params); + } + } else { + if (null != random) { + cipher.init(mode, key, random); + } else { + cipher.init(mode, key); + } + } + return this; + } +} diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java index 22198e58a..25f5661c4 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java @@ -2,6 +2,7 @@ package cn.hutool.crypto.asymmetric; import cn.hutool.core.codec.Base64; import cn.hutool.core.io.FastByteArrayOutputStream; +import cn.hutool.crypto.CipherWrapper; import cn.hutool.crypto.CryptoException; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.SecureUtil; @@ -16,6 +17,7 @@ import java.security.InvalidKeyException; import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; /** @@ -37,7 +39,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto /** * Cipher负责完成加密或解密工作 */ - protected Cipher cipher; + protected CipherWrapper cipherWrapper; /** * 加密的块大小 @@ -47,12 +49,6 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * 解密的块大小 */ protected int decryptBlockSize = -1; - - /** - * 算法参数 - */ - private AlgorithmParameterSpec algorithmParameterSpec; - // ------------------------------------------------------------------ Constructor start /** @@ -201,7 +197,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public AlgorithmParameterSpec getAlgorithmParameterSpec() { - return algorithmParameterSpec; + return this.cipherWrapper.getParams(); } /** @@ -212,7 +208,19 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public void setAlgorithmParameterSpec(AlgorithmParameterSpec algorithmParameterSpec) { - this.algorithmParameterSpec = algorithmParameterSpec; + this.cipherWrapper.setParams(algorithmParameterSpec); + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + * @since 5.7.17 + */ + public AsymmetricCrypto setRandom(SecureRandom random) { + this.cipherWrapper.setRandom(random); + return this; } @Override @@ -229,11 +237,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto final Key key = getKeyByType(keyType); lock.lock(); try { - initMode(Cipher.ENCRYPT_MODE, key); + final Cipher cipher = initMode(Cipher.ENCRYPT_MODE, key); if (this.encryptBlockSize < 0) { // 在引入BC库情况下,自动获取块大小 - final int blockSize = this.cipher.getBlockSize(); + final int blockSize = cipher.getBlockSize(); if (blockSize > 0) { this.encryptBlockSize = blockSize; } @@ -254,11 +262,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto final Key key = getKeyByType(keyType); lock.lock(); try { - initMode(Cipher.DECRYPT_MODE, key); + final Cipher cipher = initMode(Cipher.DECRYPT_MODE, key); if (this.decryptBlockSize < 0) { // 在引入BC库情况下,自动获取块大小 - final int blockSize = this.cipher.getBlockSize(); + final int blockSize = cipher.getBlockSize(); if (blockSize > 0) { this.decryptBlockSize = blockSize; } @@ -281,7 +289,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public Cipher getCipher() { - return cipher; + return this.cipherWrapper.getCipher(); } /** @@ -290,7 +298,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 4.5.2 */ protected void initCipher() { - this.cipher = SecureUtil.createCipher(algorithm); + this.cipherWrapper = new CipherWrapper(this.algorithm); } /** @@ -309,7 +317,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto // 不足分段 if (dataLength <= maxBlockSize) { - return this.cipher.doFinal(data, 0, dataLength); + return getCipher().doFinal(data, 0, dataLength); } // 分段解密 @@ -337,7 +345,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto // 对数据分段处理 while (remainLength > 0) { blockSize = Math.min(remainLength, maxBlockSize); - out.write(cipher.doFinal(data, offSet, blockSize)); + out.write(getCipher().doFinal(data, offSet, blockSize)); offSet += blockSize; remainLength = dataLength - offSet; @@ -351,14 +359,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * * @param mode 模式,可选{@link Cipher#ENCRYPT_MODE}或者{@link Cipher#DECRYPT_MODE} * @param key 密钥 + * @return {@link Cipher} * @throws InvalidAlgorithmParameterException 异常算法错误 * @throws InvalidKeyException 异常KEY错误 */ - private void initMode(int mode, Key key) throws InvalidAlgorithmParameterException, InvalidKeyException { - if (null != this.algorithmParameterSpec) { - cipher.init(mode, key, this.algorithmParameterSpec); - } else { - cipher.init(mode, key); - } + private Cipher initMode(int mode, Key key) throws InvalidAlgorithmParameterException, InvalidKeyException { + return this.cipherWrapper.initMode(mode, key).getCipher(); } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java index ba92ab553..ea4a7756d 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java @@ -3,15 +3,16 @@ package cn.hutool.crypto.symmetric; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.CipherMode; +import cn.hutool.crypto.CipherWrapper; import cn.hutool.crypto.CryptoException; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.Padding; -import cn.hutool.crypto.SecureUtil; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -25,6 +26,7 @@ import java.io.OutputStream; import java.io.Serializable; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -40,18 +42,11 @@ import java.util.concurrent.locks.ReentrantLock; public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, Serializable { private static final long serialVersionUID = 1L; + private CipherWrapper cipherWrapper; /** * SecretKey 负责保存对称密钥 */ private SecretKey secretKey; - /** - * Cipher负责完成加密或解密工作 - */ - private Cipher cipher; - /** - * 加密解密参数 - */ - private AlgorithmParameterSpec params; /** * 是否0填充 */ @@ -152,7 +147,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, this.isZeroPadding = true; } - this.cipher = SecureUtil.createCipher(algorithm); + this.cipherWrapper = new CipherWrapper(algorithm); return this; } @@ -171,7 +166,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 加密或解密 */ public Cipher getCipher() { - return cipher; + return cipherWrapper.getCipher(); } /** @@ -181,7 +176,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setParams(AlgorithmParameterSpec params) { - this.params = params; + this.cipherWrapper.setParams(params); return this; } @@ -192,8 +187,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setIv(IvParameterSpec iv) { - setParams(iv); - return this; + return setParams(iv); } /** @@ -203,7 +197,18 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setIv(byte[] iv) { - setIv(new IvParameterSpec(iv)); + return setIv(new IvParameterSpec(iv)); + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + * @since 5.7.17 + */ + public SymmetricCrypto setRandom(SecureRandom random){ + this.cipherWrapper.setRandom(random); return this; } @@ -237,6 +242,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @since 5.6.8 */ public byte[] update(byte[] data) { + final Cipher cipher = cipherWrapper.getCipher(); lock.lock(); try { return cipher.update(paddingDataWithZero(data, cipher.getBlockSize())); @@ -376,11 +382,8 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, */ private SymmetricCrypto initParams(String algorithm, AlgorithmParameterSpec paramsSpec) { if (null == paramsSpec) { - byte[] iv = null; - final Cipher cipher = this.cipher; - if (null != cipher) { - iv = cipher.getIV(); - } + byte[] iv = Opt.ofNullable(cipherWrapper) + .map(CipherWrapper::getCipher).map(Cipher::getIV).get(); // 随机IV if (StrUtil.startWithIgnoreCase(algorithm, "PBE")) { @@ -409,13 +412,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @throws InvalidAlgorithmParameterException 无效算法 */ private Cipher initMode(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException { - final Cipher cipher = this.cipher; - if (null == this.params) { - cipher.init(mode, secretKey); - } else { - cipher.init(mode, secretKey, params); - } - return cipher; + return this.cipherWrapper.initMode(mode, this.secretKey).getCipher(); } /**