From 84deef4ca40cba39f27ac0bbaaf9d2c67e5d63ee Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 26 Apr 2023 22:27:21 +0800 Subject: [PATCH] fix code --- .../org/dromara/hutool/crypto/CertUtil.java | 138 +++++ .../dromara/hutool/crypto/KeyStoreUtil.java | 154 ++++++ .../org/dromara/hutool/crypto/KeyUtil.java | 483 ++++-------------- .../org/dromara/hutool/crypto/SpecUtil.java | 90 ++++ .../crypto/openssl/OpenSSLPBECommon.java | 4 +- .../hutool/crypto/asymmetric/SM2Test.java | 4 +- 6 files changed, 494 insertions(+), 379 deletions(-) create mode 100644 hutool-crypto/src/main/java/org/dromara/hutool/crypto/CertUtil.java create mode 100644 hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyStoreUtil.java create mode 100644 hutool-crypto/src/main/java/org/dromara/hutool/crypto/SpecUtil.java diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CertUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CertUtil.java new file mode 100644 index 000000000..e93fdb8ea --- /dev/null +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CertUtil.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.crypto; + +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.crypto.provider.GlobalProviderFactory; + +import java.io.File; +import java.io.InputStream; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Provider; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; + +/** + * 数字证书{@link Certificate}相关工具类 + * + * @author looly + * @since 6.0.0 + */ +public class CertUtil { + + /** + * Certification类型:X.509 + */ + public static final String TYPE_X509 = "X.509"; + + /** + * 读取X.509 Certification文件
+ * Certification为证书文件
+ * see: ... + * + * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 + * @return {@link KeyStore} + */ + public static Certificate readX509Certificate(final InputStream in) { + return readCertificate(TYPE_X509, in); + } + + /** + * 读取X.509 Certification文件
+ * Certification为证书文件
+ * see: ... + * + * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 + * @param password 密码 + * @param alias 别名 + * @return {@link KeyStore} + */ + public static Certificate readX509Certificate(final InputStream in, final char[] password, final String alias) { + return readCertificate(TYPE_X509, in, password, alias); + } + + /** + * 读取Certification文件
+ * Certification为证书文件
+ * see: ... + * + * @param type 类型,例如X.509 + * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 + * @param password 密码 + * @param alias 别名 + * @return {@link KeyStore} + * @since 4.4.1 + */ + public static Certificate readCertificate(final String type, final InputStream in, final char[] password, final String alias) { + final KeyStore keyStore = KeyStoreUtil.readKeyStore(type, in, password); + try { + return keyStore.getCertificate(alias); + } catch (final KeyStoreException e) { + throw new CryptoException(e); + } + } + + /** + * 读取Certification文件
+ * Certification为证书文件
+ * see: ... + * + * @param type 类型,例如X.509 + * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 + * @return {@link Certificate} + */ + public static Certificate readCertificate(final String type, final InputStream in) { + try { + return getCertificateFactory(type).generateCertificate(in); + } catch (final CertificateException e) { + throw new CryptoException(e); + } + } + + /** + * 获得 Certification + * + * @param keyStore {@link KeyStore} + * @param alias 别名 + * @return {@link Certificate} + */ + public static Certificate getCertificate(final KeyStore keyStore, final String alias) { + try { + return keyStore.getCertificate(alias); + } catch (final Exception e) { + throw new CryptoException(e); + } + } + + /** + * 获取{@link CertificateFactory} + * + * @param type 类型,例如X.509 + * @return {@link KeyPairGenerator} + * @since 4.5.0 + */ + public static CertificateFactory getCertificateFactory(final String type) { + final Provider provider = GlobalProviderFactory.getProvider(); + + final CertificateFactory factory; + try { + factory = (null == provider) ? CertificateFactory.getInstance(type) : CertificateFactory.getInstance(type, provider); + } catch (final CertificateException e) { + throw new CryptoException(e); + } + return factory; + } +} diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyStoreUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyStoreUtil.java new file mode 100644 index 000000000..b02408faf --- /dev/null +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyStoreUtil.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.crypto; + +import org.dromara.hutool.core.io.IoUtil; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.crypto.provider.GlobalProviderFactory; + +import java.io.File; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Provider; + +/** + * {@link KeyStore} 相关工具类 + * + * @author looly + * @since 6.0.0 + */ +public class KeyStoreUtil { + + /** + * Java密钥库(Java Key Store,JKS)KEY_STORE + */ + public static final String TYPE_JKS = "JKS"; + /** + * jceks + */ + public static final String TYPE_JCEKS = "jceks"; + /** + * PKCS12是公钥加密标准,它规定了可包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件 + */ + public static final String TYPE_PKCS12 = "pkcs12"; + + /** + * 读取密钥库(Java Key Store,JKS) KeyStore文件
+ * KeyStore文件用于数字证书的密钥对保存
+ * see: ... + * + * @param keyFile 证书文件 + * @param password 密码 + * @return {@link KeyStore} + * @since 5.0.0 + */ + public static KeyStore readJKSKeyStore(final File keyFile, final char[] password) { + return readKeyStore(TYPE_JKS, keyFile, password); + } + + /** + * 读取密钥库(Java Key Store,JKS) KeyStore文件
+ * KeyStore文件用于数字证书的密钥对保存
+ * see: ... + * + * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(File)} 读取 + * @param password 密码 + * @return {@link KeyStore} + */ + public static KeyStore readJKSKeyStore(final InputStream in, final char[] password) { + return readKeyStore(TYPE_JKS, in, password); + } + + /** + * 读取PKCS12 KeyStore文件
+ * KeyStore文件用于数字证书的密钥对保存 + * + * @param keyFile 证书文件 + * @param password 密码 + * @return {@link KeyStore} + * @since 5.0.0 + */ + public static KeyStore readPKCS12KeyStore(final File keyFile, final char[] password) { + return readKeyStore(TYPE_PKCS12, keyFile, password); + } + + /** + * 读取PKCS12 KeyStore文件
+ * KeyStore文件用于数字证书的密钥对保存 + * + * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(java.io.File)} 读取 + * @param password 密码 + * @return {@link KeyStore} + * @since 5.0.0 + */ + public static KeyStore readPKCS12KeyStore(final InputStream in, final char[] password) { + return readKeyStore(TYPE_PKCS12, in, password); + } + + /** + * 读取KeyStore文件
+ * KeyStore文件用于数字证书的密钥对保存
+ * see: ... + * + * @param type 类型 + * @param keyFile 证书文件 + * @param password 密码,null表示无密码 + * @return {@link KeyStore} + * @since 5.0.0 + */ + public static KeyStore readKeyStore(final String type, final File keyFile, final char[] password) { + InputStream in = null; + try { + in = FileUtil.getInputStream(keyFile); + return readKeyStore(type, in, password); + } finally { + IoUtil.closeQuietly(in); + } + } + + /** + * 读取KeyStore文件
+ * KeyStore文件用于数字证书的密钥对保存
+ * see: ... + * + * @param type 类型 + * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(File)} 读取 + * @param password 密码,null表示无密码 + * @return {@link KeyStore} + */ + public static KeyStore readKeyStore(final String type, final InputStream in, final char[] password) { + final KeyStore keyStore = getKeyStore(type); + try { + keyStore.load(in, password); + } catch (final Exception e) { + throw new CryptoException(e); + } + return keyStore; + } + + /** + * 获取{@link KeyStore}对象 + * + * @param type 类型 + * @return {@link KeyStore} + */ + public static KeyStore getKeyStore(final String type) { + final Provider provider = GlobalProviderFactory.getProvider(); + try { + return null == provider ? KeyStore.getInstance(type) : KeyStore.getInstance(type, provider); + } catch (final KeyStoreException e) { + throw new CryptoException(e); + } + } +} diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java index 10225050c..7f49f91a1 100644 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java @@ -12,15 +12,13 @@ package org.dromara.hutool.crypto; +import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.codec.binary.Base64; import org.dromara.hutool.core.io.file.FileUtil; -import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.text.CharUtil; -import org.dromara.hutool.core.util.RandomUtil; import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.RandomUtil; import org.dromara.hutool.crypto.asymmetric.AsymmetricAlgorithm; import org.dromara.hutool.crypto.bc.BCUtil; import org.dromara.hutool.crypto.bc.SmUtil; @@ -30,37 +28,14 @@ import org.dromara.hutool.crypto.symmetric.SymmetricAlgorithm; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.DESKeySpec; -import javax.crypto.spec.DESedeKeySpec; -import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.io.File; import java.io.InputStream; import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.PublicKey; -import java.security.SecureRandom; +import java.security.*; import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; import java.security.interfaces.RSAPrivateCrtKey; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.RSAPublicKeySpec; -import java.security.spec.X509EncodedKeySpec; +import java.security.spec.*; /** * 密钥工具类 @@ -77,23 +52,6 @@ import java.security.spec.X509EncodedKeySpec; */ public class KeyUtil { - /** - * Java密钥库(Java Key Store,JKS)KEY_STORE - */ - public static final String KEY_TYPE_JKS = "JKS"; - /** - * jceks - */ - public static final String KEY_TYPE_JCEKS = "jceks"; - /** - * PKCS12是公钥加密标准,它规定了可包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件 - */ - public static final String KEY_TYPE_PKCS12 = "pkcs12"; - /** - * Certification类型:X.509 - */ - public static final String CERT_TYPE_X509 = "X.509"; - /** * 默认密钥字节数 * @@ -105,15 +63,7 @@ public class KeyUtil { */ public static final int DEFAULT_KEY_SIZE = 1024; - /** - * SM2默认曲线 - * - *
-	 * Default SM2 curve
-	 * 
- */ - public static final String SM2_DEFAULT_CURVE = SmUtil.SM2_CURVE_NAME; - + // region ----- generateKey /** * 生成 {@link SecretKey},仅用于对称加密和摘要算法密钥生成 * @@ -205,18 +155,8 @@ public class KeyUtil { if (null == key) { secretKey = generateKey(algorithm); } else { - final KeySpec keySpec; - try { - if (algorithm.startsWith("DESede")) { - // DESede兼容 - keySpec = new DESedeKeySpec(key); - } else { - keySpec = new DESKeySpec(key); - } - } catch (final InvalidKeyException e) { - throw new CryptoException(e); - } - secretKey = generateKey(algorithm, keySpec); + secretKey = generateKey(algorithm, + SpecUtil.createKeySpec(algorithm, key)); } return secretKey; } @@ -225,19 +165,18 @@ public class KeyUtil { * 生成PBE {@link SecretKey} * * @param algorithm PBE算法,包括:PBEWithMD5AndDES、PBEWithSHA1AndDESede、PBEWithSHA1AndRC2_40等 - * @param key 密钥 + * @param password 口令 * @return {@link SecretKey} */ - public static SecretKey generatePBEKey(final String algorithm, char[] key) { + public static SecretKey generatePBEKey(final String algorithm, char[] password) { if (StrUtil.isBlank(algorithm) || !algorithm.startsWith("PBE")) { throw new CryptoException("Algorithm [{}] is not a PBE algorithm!", algorithm); } - if (null == key) { - key = RandomUtil.randomString(32).toCharArray(); + if (null == password) { + password = RandomUtil.randomString(32).toCharArray(); } - final PBEKeySpec keySpec = new PBEKeySpec(key); - return generateKey(algorithm, keySpec); + return generateKey(algorithm, SpecUtil.createPBEKeySpec(password)); } /** @@ -255,7 +194,9 @@ public class KeyUtil { throw new CryptoException(e); } } + // endregion + // region ----- keyPair /** * 生成RSA私钥,仅用于非对称加密
* 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法
@@ -372,6 +313,51 @@ public class KeyUtil { } } + /** + * 通过RSA私钥生成RSA公钥 + * + * @param privateKey RSA私钥 + * @return RSA公钥,null表示私钥不被支持 + * @since 5.3.6 + */ + public static PublicKey getRSAPublicKey(final PrivateKey privateKey) { + if (privateKey instanceof RSAPrivateCrtKey) { + final RSAPrivateCrtKey privk = (RSAPrivateCrtKey) privateKey; + return getRSAPublicKey(privk.getModulus(), privk.getPublicExponent()); + } + return null; + } + + /** + * 获得RSA公钥对象 + * + * @param modulus Modulus + * @param publicExponent Public Exponent + * @return 公钥 + * @since 5.3.6 + */ + public static PublicKey getRSAPublicKey(final String modulus, final String publicExponent) { + return getRSAPublicKey( + new BigInteger(modulus, 16), new BigInteger(publicExponent, 16)); + } + + /** + * 获得RSA公钥对象 + * + * @param modulus Modulus + * @param publicExponent Public Exponent + * @return 公钥 + * @since 5.3.6 + */ + public static PublicKey getRSAPublicKey(final BigInteger modulus, final BigInteger publicExponent) { + final RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent); + try { + return getKeyFactory(AsymmetricAlgorithm.RSA.getValue()).generatePublic(publicKeySpec); + } catch (final InvalidKeySpecException e) { + throw new CryptoException(e); + } + } + /** * 生成用于非对称加密的公钥和私钥,仅用于非对称加密
* 密钥对生成算法见:... @@ -413,7 +399,7 @@ public class KeyUtil { public static KeyPair generateKeyPair(final String algorithm, final int keySize, final byte[] seed) { // SM2算法需要单独定义其曲线生成 if ("SM2".equalsIgnoreCase(algorithm)) { - final ECGenParameterSpec sm2p256v1 = new ECGenParameterSpec(SM2_DEFAULT_CURVE); + final ECGenParameterSpec sm2p256v1 = new ECGenParameterSpec(SmUtil.SM2_CURVE_NAME); return generateKeyPair(algorithm, keySize, seed, sm2p256v1); } @@ -549,6 +535,42 @@ public class KeyUtil { return keyPairGen.generateKeyPair(); } + /** + * 从KeyStore中获取私钥公钥 + * + * @param type 类型 + * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(java.io.File)} 读取 + * @param password 密码 + * @param alias 别名 + * @return {@link KeyPair} + * @since 4.4.1 + */ + public static KeyPair getKeyPair(final String type, final InputStream in, final char[] password, final String alias) { + final KeyStore keyStore = KeyStoreUtil.readKeyStore(type, in, password); + return getKeyPair(keyStore, password, alias); + } + + /** + * 从KeyStore中获取私钥公钥 + * + * @param keyStore {@link KeyStore} + * @param password 密码 + * @param alias 别名 + * @return {@link KeyPair} + * @since 4.4.1 + */ + public static KeyPair getKeyPair(final KeyStore keyStore, final char[] password, final String alias) { + final PublicKey publicKey; + final PrivateKey privateKey; + try { + publicKey = keyStore.getCertificate(alias).getPublicKey(); + privateKey = (PrivateKey) keyStore.getKey(alias, password); + } catch (final Exception e) { + throw new CryptoException(e); + } + return new KeyPair(publicKey, privateKey); + } + /** * 获取{@link KeyPairGenerator} * @@ -562,13 +584,14 @@ public class KeyUtil { final KeyPairGenerator keyPairGen; try { keyPairGen = (null == provider) // - ? KeyPairGenerator.getInstance(getMainAlgorithm(algorithm)) // - : KeyPairGenerator.getInstance(getMainAlgorithm(algorithm), provider);// + ? KeyPairGenerator.getInstance(getMainAlgorithm(algorithm)) // + : KeyPairGenerator.getInstance(getMainAlgorithm(algorithm), provider);// } catch (final NoSuchAlgorithmException e) { throw new CryptoException(e); } return keyPairGen; } + // endregion /** * 获取{@link KeyFactory} @@ -583,8 +606,8 @@ public class KeyUtil { final KeyFactory keyFactory; try { keyFactory = (null == provider) // - ? KeyFactory.getInstance(getMainAlgorithm(algorithm)) // - : KeyFactory.getInstance(getMainAlgorithm(algorithm), provider); + ? KeyFactory.getInstance(getMainAlgorithm(algorithm)) // + : KeyFactory.getInstance(getMainAlgorithm(algorithm), provider); } catch (final NoSuchAlgorithmException e) { throw new CryptoException(e); } @@ -604,8 +627,8 @@ public class KeyUtil { final SecretKeyFactory keyFactory; try { keyFactory = (null == provider) // - ? SecretKeyFactory.getInstance(getMainAlgorithm(algorithm)) // - : SecretKeyFactory.getInstance(getMainAlgorithm(algorithm), provider); + ? SecretKeyFactory.getInstance(getMainAlgorithm(algorithm)) // + : SecretKeyFactory.getInstance(getMainAlgorithm(algorithm), provider); } catch (final NoSuchAlgorithmException e) { throw new CryptoException(e); } @@ -624,8 +647,8 @@ public class KeyUtil { final KeyGenerator generator; try { generator = (null == provider) // - ? KeyGenerator.getInstance(getMainAlgorithm(algorithm)) // - : KeyGenerator.getInstance(getMainAlgorithm(algorithm), provider); + ? KeyGenerator.getInstance(getMainAlgorithm(algorithm)) // + : KeyGenerator.getInstance(getMainAlgorithm(algorithm), provider); } catch (final NoSuchAlgorithmException e) { throw new CryptoException(e); } @@ -667,174 +690,14 @@ public class KeyUtil { algorithm = StrUtil.subSuf(algorithm, indexOfWith + "with".length()); } if ("ECDSA".equalsIgnoreCase(algorithm) - || "SM2".equalsIgnoreCase(algorithm) - || "ECIES".equalsIgnoreCase(algorithm) + || "SM2".equalsIgnoreCase(algorithm) + || "ECIES".equalsIgnoreCase(algorithm) ) { algorithm = "EC"; } return algorithm; } - /** - * 读取密钥库(Java Key Store,JKS) KeyStore文件
- * KeyStore文件用于数字证书的密钥对保存
- * see: ... - * - * @param keyFile 证书文件 - * @param password 密码 - * @return {@link KeyStore} - * @since 5.0.0 - */ - public static KeyStore readJKSKeyStore(final File keyFile, final char[] password) { - return readKeyStore(KEY_TYPE_JKS, keyFile, password); - } - - /** - * 读取密钥库(Java Key Store,JKS) KeyStore文件
- * KeyStore文件用于数字证书的密钥对保存
- * see: ... - * - * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(File)} 读取 - * @param password 密码 - * @return {@link KeyStore} - */ - public static KeyStore readJKSKeyStore(final InputStream in, final char[] password) { - return readKeyStore(KEY_TYPE_JKS, in, password); - } - - /** - * 读取PKCS12 KeyStore文件
- * KeyStore文件用于数字证书的密钥对保存 - * - * @param keyFile 证书文件 - * @param password 密码 - * @return {@link KeyStore} - * @since 5.0.0 - */ - public static KeyStore readPKCS12KeyStore(final File keyFile, final char[] password) { - return readKeyStore(KEY_TYPE_PKCS12, keyFile, password); - } - - /** - * 读取PKCS12 KeyStore文件
- * KeyStore文件用于数字证书的密钥对保存 - * - * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(java.io.File)} 读取 - * @param password 密码 - * @return {@link KeyStore} - * @since 5.0.0 - */ - public static KeyStore readPKCS12KeyStore(final InputStream in, final char[] password) { - return readKeyStore(KEY_TYPE_PKCS12, in, password); - } - - /** - * 读取KeyStore文件
- * KeyStore文件用于数字证书的密钥对保存
- * see: ... - * - * @param type 类型 - * @param keyFile 证书文件 - * @param password 密码,null表示无密码 - * @return {@link KeyStore} - * @since 5.0.0 - */ - public static KeyStore readKeyStore(final String type, final File keyFile, final char[] password) { - InputStream in = null; - try { - in = FileUtil.getInputStream(keyFile); - return readKeyStore(type, in, password); - } finally { - IoUtil.closeQuietly(in); - } - } - - /** - * 读取KeyStore文件
- * KeyStore文件用于数字证书的密钥对保存
- * see: ... - * - * @param type 类型 - * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(File)} 读取 - * @param password 密码,null表示无密码 - * @return {@link KeyStore} - */ - public static KeyStore readKeyStore(final String type, final InputStream in, final char[] password) { - final KeyStore keyStore = getKeyStore(type); - try { - keyStore.load(in, password); - } catch (final Exception e) { - throw new CryptoException(e); - } - return keyStore; - } - - /** - * 获取{@link KeyStore}对象 - * - * @param type 类型 - * @return {@link KeyStore} - */ - public static KeyStore getKeyStore(final String type) { - final Provider provider = GlobalProviderFactory.getProvider(); - try { - return null == provider ? KeyStore.getInstance(type) : KeyStore.getInstance(type, provider); - } catch (final KeyStoreException e) { - throw new CryptoException(e); - } - } - - /** - * 从KeyStore中获取私钥公钥 - * - * @param type 类型 - * @param in {@link InputStream} 如果想从文件读取.keystore文件,使用 {@link FileUtil#getInputStream(java.io.File)} 读取 - * @param password 密码 - * @param alias 别名 - * @return {@link KeyPair} - * @since 4.4.1 - */ - public static KeyPair getKeyPair(final String type, final InputStream in, final char[] password, final String alias) { - final KeyStore keyStore = readKeyStore(type, in, password); - return getKeyPair(keyStore, password, alias); - } - - /** - * 从KeyStore中获取私钥公钥 - * - * @param keyStore {@link KeyStore} - * @param password 密码 - * @param alias 别名 - * @return {@link KeyPair} - * @since 4.4.1 - */ - public static KeyPair getKeyPair(final KeyStore keyStore, final char[] password, final String alias) { - final PublicKey publicKey; - final PrivateKey privateKey; - try { - publicKey = keyStore.getCertificate(alias).getPublicKey(); - privateKey = (PrivateKey) keyStore.getKey(alias, password); - } catch (final Exception e) { - throw new CryptoException(e); - } - return new KeyPair(publicKey, privateKey); - } - - /** - * 读取X.509 Certification文件
- * Certification为证书文件
- * see: ... - * - * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 - * @param password 密码 - * @param alias 别名 - * @return {@link KeyStore} - * @since 4.4.1 - */ - public static Certificate readX509Certificate(final InputStream in, final char[] password, final String alias) { - return readCertificate(CERT_TYPE_X509, in, password, alias); - } - /** * 读取X.509 Certification文件中的公钥
* Certification为证书文件
@@ -845,98 +708,13 @@ public class KeyUtil { * @since 4.5.2 */ public static PublicKey readPublicKeyFromCert(final InputStream in) { - final Certificate certificate = readX509Certificate(in); + final Certificate certificate = CertUtil.readX509Certificate(in); if (null != certificate) { return certificate.getPublicKey(); } return null; } - /** - * 读取X.509 Certification文件
- * Certification为证书文件
- * see: ... - * - * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 - * @return {@link KeyStore} - * @since 4.4.1 - */ - public static Certificate readX509Certificate(final InputStream in) { - return readCertificate(CERT_TYPE_X509, in); - } - - /** - * 读取Certification文件
- * Certification为证书文件
- * see: ... - * - * @param type 类型,例如X.509 - * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 - * @param password 密码 - * @param alias 别名 - * @return {@link KeyStore} - * @since 4.4.1 - */ - public static Certificate readCertificate(final String type, final InputStream in, final char[] password, final String alias) { - final KeyStore keyStore = readKeyStore(type, in, password); - try { - return keyStore.getCertificate(alias); - } catch (final KeyStoreException e) { - throw new CryptoException(e); - } - } - - /** - * 读取Certification文件
- * Certification为证书文件
- * see: ... - * - * @param type 类型,例如X.509 - * @param in {@link InputStream} 如果想从文件读取.cer文件,使用 {@link FileUtil#getInputStream(File)} 读取 - * @return {@link Certificate} - */ - public static Certificate readCertificate(final String type, final InputStream in) { - try { - return getCertificateFactory(type).generateCertificate(in); - } catch (final CertificateException e) { - throw new CryptoException(e); - } - } - - /** - * 获得 Certification - * - * @param keyStore {@link KeyStore} - * @param alias 别名 - * @return {@link Certificate} - */ - public static Certificate getCertificate(final KeyStore keyStore, final String alias) { - try { - return keyStore.getCertificate(alias); - } catch (final Exception e) { - throw new CryptoException(e); - } - } - - /** - * 获取{@link CertificateFactory} - * - * @param type 类型,例如X.509 - * @return {@link KeyPairGenerator} - * @since 4.5.0 - */ - public static CertificateFactory getCertificateFactory(final String type) { - final Provider provider = GlobalProviderFactory.getProvider(); - - final CertificateFactory factory; - try { - factory = (null == provider) ? CertificateFactory.getInstance(type) : CertificateFactory.getInstance(type, provider); - } catch (final CertificateException e) { - throw new CryptoException(e); - } - return factory; - } - /** * 编码压缩EC公钥(基于BouncyCastle)
* 见:... @@ -975,51 +753,6 @@ public class KeyUtil { return BCUtil.decodeECPoint(encodeByte, curveName); } - /** - * 通过RSA私钥生成RSA公钥 - * - * @param privateKey RSA私钥 - * @return RSA公钥,null表示私钥不被支持 - * @since 5.3.6 - */ - public static PublicKey getRSAPublicKey(final PrivateKey privateKey) { - if (privateKey instanceof RSAPrivateCrtKey) { - final RSAPrivateCrtKey privk = (RSAPrivateCrtKey) privateKey; - return getRSAPublicKey(privk.getModulus(), privk.getPublicExponent()); - } - return null; - } - - /** - * 获得RSA公钥对象 - * - * @param modulus Modulus - * @param publicExponent Public Exponent - * @return 公钥 - * @since 5.3.6 - */ - public static PublicKey getRSAPublicKey(final String modulus, final String publicExponent) { - return getRSAPublicKey( - new BigInteger(modulus, 16), new BigInteger(publicExponent, 16)); - } - - /** - * 获得RSA公钥对象 - * - * @param modulus Modulus - * @param publicExponent Public Exponent - * @return 公钥 - * @since 5.3.6 - */ - public static PublicKey getRSAPublicKey(final BigInteger modulus, final BigInteger publicExponent) { - final RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent); - try { - return getKeyFactory("RSA").generatePublic(publicKeySpec); - } catch (final InvalidKeySpecException e) { - throw new CryptoException(e); - } - } - /** * 将密钥编码为Base64格式 * diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SpecUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SpecUtil.java new file mode 100644 index 000000000..f8fbf8f05 --- /dev/null +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SpecUtil.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.crypto; + +import org.dromara.hutool.core.util.RandomUtil; + +import javax.crypto.spec.*; +import java.security.InvalidKeyException; +import java.security.spec.KeySpec; + +/** + * 规范相关工具类,用于生成密钥规范、参数规范等快捷方法。 + * + * + * @author looly + * @since 6.0.0 + */ +public class SpecUtil { + + /** + * 根据算法创建{@link KeySpec} + * + * + * @param algorithm 算法 + * @param key 密钥 + * @return {@link KeySpec} + */ + public static KeySpec createKeySpec(final String algorithm, byte[] key) { + try { + if (algorithm.startsWith("DESede")) { + if (null == key) { + key = RandomUtil.randomBytes(24); + } + // DESede兼容 + return new DESedeKeySpec(key); + } else if (algorithm.startsWith("DES")) { + if (null == key) { + key = RandomUtil.randomBytes(8); + } + return new DESKeySpec(key); + } + } catch (final InvalidKeyException e) { + throw new CryptoException(e); + } + + return new SecretKeySpec(key, algorithm); + } + + /** + * 创建{@link PBEKeySpec}
+ * PBE算法没有密钥的概念,密钥在其它对称加密算法中是经过算法计算得出来的,PBE算法则是使用口令替代了密钥。 + * + * @param password 口令 + * @return {@link PBEKeySpec} + */ + public static PBEKeySpec createPBEKeySpec(char[] password) { + if (null == password) { + password = RandomUtil.randomString(32).toCharArray(); + } + return new PBEKeySpec(password); + } + + /** + * 创建{@link PBEParameterSpec} + * + * @param salt 加盐值 + * @param iterationCount 摘要次数 + * @return {@link PBEParameterSpec} + */ + public static PBEParameterSpec createPBEParameterSpec(final byte[] salt, final int iterationCount) { + return new PBEParameterSpec(salt, iterationCount); + } +} diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLPBECommon.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLPBECommon.java index 204968f48..aab8966c5 100755 --- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLPBECommon.java +++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/openssl/OpenSSLPBECommon.java @@ -33,9 +33,9 @@ public class OpenSSLPBECommon { final String algorithm, int iterationCount) throws Exception { - final SecretKey key = KeyUtil.generateKey(algorithm, new PBEKeySpec(password)); - final Cipher cipher = SecureUtil.createCipher(algorithm); + + final SecretKey key = KeyUtil.generateKey(algorithm, new PBEKeySpec(password)); cipher.init(cipherMode, key, new PBEParameterSpec(salt, iterationCount)); return cipher; diff --git a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java index 0da49c15f..b0e46d84b 100644 --- a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java +++ b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/asymmetric/SM2Test.java @@ -190,8 +190,8 @@ public class SM2Test { final byte[] data = KeyUtil.encodeECPublicKey(publicKey); final String encodeHex = HexUtil.encodeHexStr(data); final String encodeB64 = Base64.encode(data); - final PublicKey Hexdecode = KeyUtil.decodeECPoint(encodeHex, KeyUtil.SM2_DEFAULT_CURVE); - final PublicKey B64decode = KeyUtil.decodeECPoint(encodeB64, KeyUtil.SM2_DEFAULT_CURVE); + final PublicKey Hexdecode = KeyUtil.decodeECPoint(encodeHex, SmUtil.SM2_CURVE_NAME); + final PublicKey B64decode = KeyUtil.decodeECPoint(encodeB64, SmUtil.SM2_CURVE_NAME); Assertions.assertEquals(HexUtil.encodeHexStr(publicKey.getEncoded()), HexUtil.encodeHexStr(Hexdecode.getEncoded())); Assertions.assertEquals(HexUtil.encodeHexStr(publicKey.getEncoded()), HexUtil.encodeHexStr(B64decode.getEncoded())); }