欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

前后端RSA互相加解密、加签验签、密钥对生成(Java)

最编程 2024-08-14 07:19:41
...
import com.universe.crypto.CryptoUtils.Algorithm.Encryption; import com.universe.crypto.CryptoUtils.Algorithm.Signing; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.Base64.Decoder; import java.util.Base64.Encoder; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 支持AES、DES、RSA加密、数字签名以及生成对称密钥和非对称密钥对 */ public class CryptoUtils { private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; private static final Encoder BASE64_ENCODER = Base64.getEncoder(); private static final Decoder BASE64_DECODER = Base64.getDecoder(); private static final Map<Algorithm, KeyFactory> KEY_FACTORY_CACHE = new ConcurrentHashMap<>(); private static final Map<Algorithm, Cipher> CIPHER_CACHE = new HashMap<>(); /** * 生成对称密钥,目前支持的算法有AES、DES * @param algorithm * @return * @throws NoSuchAlgorithmException */ public static String generateSymmetricKey(Algorithm algorithm) throws NoSuchAlgorithmException { KeyGenerator generator = KeyGenerator.getInstance(algorithm.getName()); generator.init(algorithm.getKeySize()); SecretKey secretKey = generator.generateKey(); return BASE64_ENCODER.encodeToString(secretKey.getEncoded()); } /** * 生成非对称密钥对,目前支持的算法有RSA、DSA。备注:默认生成的密钥格式为PKCS8 * @param algorithm * @return * @throws NoSuchAlgorithmException */ public static AsymmetricKeyPair generateAsymmetricKeyPair(Algorithm algorithm) throws NoSuchAlgorithmException { KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm.getName()); generator.initialize(algorithm.getKeySize()); KeyPair keyPair = generator.generateKeyPair(); String publicKey = BASE64_ENCODER.encodeToString(keyPair.getPublic().getEncoded()); String privateKey = BASE64_ENCODER.encodeToString(keyPair.getPrivate().getEncoded()); return new AsymmetricKeyPair(publicKey, privateKey); } public static String encryptByRSA(String publicKeyText, String plainText) throws Exception { return encryptAsymmetrically(publicKeyText, plainText, Encryption.RSA_ECB_PKCS1); } public static String decryptByRSA(String privateKeyText, String ciphertext) throws Exception { return decryptAsymmetrically(privateKeyText, ciphertext, Encryption.RSA_ECB_PKCS1); } /** * SHA1签名算法和DSA加密算法结合使用生成数字签名 * @param privateKeyText * @param msg * @return 数字签名 * @throws Exception */ public static String signBySHA1WithDSA(String privateKeyText, String msg) throws Exception { return doSign(privateKeyText, msg, Encryption.DSA, Signing.SHA1WithDSA); } /** * SHA1签名算法和RSA加密算法结合使用生成数字签名 * @param privateKeyText 私钥 * @param msg 待加签内容 * @return 数字签名 * @throws Exception */ public static String signBySHA1WithRSA(String privateKeyText, String msg) throws Exception { return doSign(privateKeyText, msg, Encryption.RSA_ECB_PKCS1, Signing.SHA1WithRSA); } /** * SHA256签名算法和RSA加密算法结合使用生成数字签名 * @param privateKeyText 私钥 * @param msg 待加签内容 * @return 数字签名 * @throws Exception */ public static String signBySHA256WithRSA(String privateKeyText, String msg) throws Exception { return doSign(privateKeyText, msg, Encryption.RSA_ECB_PKCS1, Signing.SHA256WithRSA); } /** * SHA1签名算法和DSA加密算法检验数字签名 * @param publicKeyText 公钥 * @param msg 待验签内容 * @param signatureText 数字 * @return 检验是否成功 * @throws Exception */ public static boolean verifyBySHA1WithDSA(String publicKeyText, String msg, String signatureText) throws Exception { return doVerify(publicKeyText, msg, signatureText, Encryption.DSA, Signing.SHA1WithDSA); } /** * SHA1签名算法和RSA加密算法检验数字签名 * @param publicKeyText 公钥 * @param msg 待验签内容 * @param signatureText 签名 * @return 校验是否成功 * @throws Exception */ public static boolean verifyBySHA1WithRSA(String publicKeyText, String msg, String signatureText) throws Exception { return doVerify(publicKeyText, msg, signatureText, Encryption.RSA_ECB_PKCS1, Signing.SHA1WithRSA); } /** * SHA256签名算法和RSA加密算法检验数字签名 * @param publicKeyText 公钥 * @param msg 待验签内容 * @param signatureText 签名 * @return 校验是否成功 * @throws Exception */ public static boolean verifyBySHA256WithRSA(String publicKeyText, String msg, String signatureText) throws Exception { return doVerify(publicKeyText, msg, signatureText, Encryption.RSA_ECB_PKCS1, Signing.SHA256WithRSA); } /** * 对称加密 * @param secretKey 密钥 * @param iv 加密向量,只有CBC模式才支持,如果是CBC则必传 * @param plainText 明文 * @param algorithm 对称加密算法,如AES、DES * @return * @throws Exception */ public static String encryptSymmetrically(String secretKey, String iv, String plainText, Algorithm algorithm) throws Exception { SecretKey key = decodeSymmetricKey(secretKey, algorithm); IvParameterSpec ivParameterSpec = StringUtils.isBlank(iv) ? null : decodeIv(iv); byte[] plainTextInBytes = plainText.getBytes(DEFAULT_CHARSET); byte[] ciphertextInBytes = transform(algorithm, Cipher.ENCRYPT_MODE, key, ivParameterSpec, plainTextInBytes); return BASE64_ENCODER.encodeToString(ciphertextInBytes); } /** * 对称解密 * @param secretKey 密钥 * @param iv 加密向量,只有CBC模式才支持,如果是CBC则必传 * @param ciphertext 密文 * @param algorithm 对称加密算法,如AES、DES * @return * @throws Exception */ public static String decryptSymmetrically(String secretKey, String iv, String ciphertext, Algorithm algorithm) throws Exception { SecretKey key = decodeSymmetricKey(secretKey, algorithm); IvParameterSpec ivParameterSpec = StringUtils.isBlank(iv) ? null : decodeIv(iv); byte[] ciphertextInBytes = BASE64_DECODER.decode(ciphertext); byte[] plainTextInBytes = transform(algorithm, Cipher.DECRYPT_MODE, key, ivParameterSpec, ciphertextInBytes); return new String(plainTextInBytes, DEFAULT_CHARSET); } /** * 非对称加密 * @param publicKeyText 公钥 * @param plainText 明文 * @param algorithm 非对称加密算法 * @return * @throws Exception */ public static String encryptAsymmetrically(String publicKeyText, String plainText, Algorithm algorithm) throws Exception { PublicKey publicKey = regeneratePublicKey(publicKeyText, algorithm); byte[] plainTextInBytes = plainText.getBytes(DEFAULT_CHARSET); byte[] ciphertextInBytes = transform(algorithm, Cipher.ENCRYPT_MODE, publicKey, plainTextInBytes); return BASE64_ENCODER.encodeToString(ciphertextInBytes); } /** * 非对称解密 * @param privateKeyText 私钥 * @param ciphertext 密文 * @param algorithm 非对称加密算法 * @return * @throws Exception */ public static String decryptAsymmetrically(String privateKeyText, String ciphertext, Algorithm algorithm) throws Exception { PrivateKey privateKey = regeneratePrivateKey(privateKeyText, algorithm); byte[] ciphertextInBytes = BASE64_DECODER.decode(ciphertext); byte[] plainTextInBytes = transform(algorithm, Cipher.DECRYPT_MODE, privateKey, ciphertextInBytes); return new String(plainTextInBytes, DEFAULT_CHARSET); } /** * 生成数字签名 * @param privateKeyText 私钥 * @param msg 传输的数据 * @param encryptionAlgorithm 加密算法,见Algorithm中的加密算法 * @param signatureAlgorithm 签名算法,见Algorithm中的签名算法 * @return 数字签名 * @throws Exception */ public static String doSign(String privateKeyText, String msg, Algorithm encryptionAlgorithm, Algorithm signatureAlgorithm) throws Exception { PrivateKey privateKey = regeneratePrivateKey(privateKeyText, encryptionAlgorithm); // Signature只支持签名算法 Signature signature = Signature.getInstance(signatureAlgorithm.getName()); signature.initSign(privateKey); signature.update(msg.getBytes(DEFAULT_CHARSET)); byte[] signatureInBytes = signature.sign(); return BASE64_ENCODER.encodeToString(signatureInBytes); } /** * 数字签名验证 * @param publicKeyText 公钥 * @param msg 传输的数据 * @param signatureText 数字签名 * @param encryptionAlgorithm 加密算法,见Algorithm中的加密算法 * @param signatureAlgorithm 签名算法,见Algorithm中的签名算法 * @return 校验是否成功 * @throws Exception */ public static boolean doVerify(String publicKeyText, String msg, String signatureText, Algorithm encryptionAlgorithm, Algorithm signatureAlgorithm) throws Exception { PublicKey publicKey = regeneratePublicKey(publicKeyText, encryptionAlgorithm); Signature signature = Signature.getInstance(signatureAlgorithm.getName()); signature.initVerify(publicKey); signature.update(msg.getBytes(DEFAULT_CHARSET)); return signature.verify(BASE64_DECODER.decode(signatureText)); } /** * 将密钥进行Base64位解码,重新生成SecretKey实例 * @param secretKey 密钥 * @param algorithm 算法 * @return */ private static SecretKey decodeSymmetricKey(String secretKey, Algorithm algorithm) { byte[] key = BASE64_DECODER.decode(secretKey); return new SecretKeySpec(key, algorithm.getName()); } private static IvParameterSpec decodeIv(String iv) { byte[] ivInBytes = BASE64_DECODER.decode(iv); return new IvParameterSpec(ivInBytes); } private static PublicKey regeneratePublicKey(String publicKeyText, Algorithm algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] keyInBytes = BASE64_DECODER.decode(publicKeyText); KeyFactory keyFactory = getKeyFactory(algorithm); // 公钥必须使用RSAPublicKeySpec或者X509EncodedKeySpec KeySpec publicKeySpec = new X509EncodedKeySpec(keyInBytes); PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); return publicKey; } private static PrivateKey regeneratePrivateKey(String key, Algorithm algorithm) throws Exception { byte[] keyInBytes = BASE64_DECODER.decode(key); KeyFactory keyFactory = getKeyFactory(algorithm); // 私钥必须使用RSAPrivateCrtKeySpec或者PKCS8EncodedKeySpec KeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyInBytes); PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); return privateKey; } private static KeyFactory getKeyFactory(Algorithm algorithm) throws NoSuchAlgorithmException { KeyFactory keyFactory = KEY_FACTORY_CACHE.get(algorithm); if (keyFactory == null) { keyFactory = KeyFactory.getInstance(algorithm.getName()); KEY_FACTORY_CACHE.put(algorithm, keyFactory); } return keyFactory; } private static byte[] transform(Algorithm algorithm, int mode, Key key, byte[] msg) throws Exception { return transform(algorithm, mode, key, null, msg); } private static byte[] transform(Algorithm algorithm, int mode, Key key, IvParameterSpec iv, byte[] msg) throws Exception { Cipher cipher = CIPHER_CACHE.get(algorithm); // double check,减少上下文切换 if (cipher == null) { synchronized (CryptoUtils.class) { if ((cipher = CIPHER_CACHE.get(algorithm)) == null) { cipher = determineWhichCipherToUse(algorithm); CIPHER_CACHE.put(algorithm, cipher); } cipher.init(mode, key, iv); return cipher.doFinal(msg); } } synchronized (CryptoUtils.class) { cipher.init(mode, key, iv); return cipher.doFinal(msg); } } private static Cipher determineWhichCipherToUse(Algorithm algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher; String transformation = algorithm.getTransformation(); // 官方推荐的transformation使用algorithm/mode/padding组合,SunJCE使用ECB作为默认模式,使用PKCS5Padding作为默认填充 if (StringUtils.isNotEmpty(transformation)) { cipher = Cipher.getInstance(transformation); } else {

上一篇: 加密与压缩VueJS代码的方法

下一篇: Vue和Webpack的代码混淆加密方法被重新改写了