美文网首页
RSA 工具包

RSA 工具包

作者: mrathena | 来源:发表于2020-08-13 11:45 被阅读0次

    CreatedAt: 20200813
    JDK Version: Oracle JDK 1.8.0_202

    package com.mrathena.toolkit;
    
    import com.mrathena.exception.ServiceException;
    
    import javax.crypto.Cipher;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.security.*;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.RSAPrivateKeySpec;
    import java.security.spec.RSAPublicKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Base64;
    
    /**
     * RSA 加密工具
     * <p>
     * 公钥加密/私钥加密/公钥解密/私钥解密
     * 私钥签名/公钥验签
     * 密钥生成/密钥转换
     * <p>
     * *******************************************
     * 字符串格式的密钥在没有特殊说明时都为BASE64编码格式 *
     * 字符串格式的签名在没有特殊说明时都为BASE64编码格式 *
     * 字符串格式的密文在没有特殊说明时都为BASE64编码格式 *
     * *******************************************
     * <p>
     * Java Cryptography Architecture
     * Standard Algorithm Name Documentation for JDK 8
     * Java密码体系结构 JDK 8的标准算法名称文档
     * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
     * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator
     * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory
     * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#algspec
     * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
     * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature
     * <p>
     * 参考
     * java RSA加密解密实现(含分段加密)
     * https://www.cnblogs.com/jiafuwei/p/7054500.html
     * JDK安全模块JCE核心Cipher使用详解
     * https://blog.csdn.net/zcmain/article/details/90640797
     * 加密与安全:非对称加密算法 RSA 1024 公钥、秘钥、明文和密文长度
     * https://blog.csdn.net/liwei16611/article/details/83751851
     */
    public final class RSAKit {
    
        private RSAKit() {}
    
        /**
         * 加密算法
         */
        private static final String ALGORITHM = "RSA";
        /**
         * 默认密钥长度(位),RSA密钥长度必须是8的倍数且必须大于等于512,建议1024起步(1024以下已有破解先例)
         */
        private static final int DEFAULT_KEY_SIZE = 1024;
        /**
         * 单例 KeyFactory, 用于转换字符串公私钥到标准公私钥
         */
        private static volatile KeyFactory instance;
        /**
         * UTF-8编码
         */
        private static final Charset UTF8 = StandardCharsets.UTF_8;
    
        // 以下是密钥相关方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 生成公私钥对
         *
         * @param keySize 密钥长度, RSA密钥长度必须是8的倍数且必须大于等于512,建议1024起步(1024以下已有破解先例)
         */
        public static KeyPair generateKeyPair(int keySize) {
            try {
                KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
                keyPairGenerator.initialize(keySize);
                return keyPairGenerator.generateKeyPair();
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 生成公私钥对(1024位)
         */
        public static KeyPair generateKeyPair() {
            return generateKeyPair(DEFAULT_KEY_SIZE);
        }
    
        /**
         * 生成公私钥对(2048位)
         */
        public static KeyPair generateKeyPairWithKeySize2048() {
            return generateKeyPair(2048);
        }
    
        /**
         * 生成公私钥对(4096位)
         */
        public static KeyPair generateKeyPairWithKeySize4096() {
            return generateKeyPair(4096);
        }
    
        /**
         * 单例获取 KeyFactory
         */
        private static KeyFactory getKeyFactory() {
            try {
                if (null == instance) {
                    synchronized (RSAKit.class) {
                        if (null == instance) {
                            instance = KeyFactory.getInstance(ALGORITHM);
                        }
                    }
                }
                return instance;
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 字符串公钥转标准公钥
         */
        public static PublicKey toPublicKey(String publicKeyStr) {
            try {
                return getKeyFactory().generatePublic(new X509EncodedKeySpec(decode(publicKeyStr)));
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 字符串私钥转标准私钥
         */
        public static PrivateKey toPrivateKey(String privateKeyStr) {
            try {
                return getKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(decode(privateKeyStr)));
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 标准密钥转字符串密钥
         */
        public static String toKeyStr(Key key) {
            return encode(key.getEncoded());
        }
    
        // 以下是公钥加密相关方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 公钥加密
         */
        public static byte[] encryptByPublicKey(PublicKey publicKey, byte[] data) {
            return encryptByKey(publicKey, data);
        }
    
        /**
         * 公钥加密
         */
        public static byte[] encryptByPublicKey(PublicKey publicKey, String data) {
            return encryptByKey(publicKey, data.getBytes(UTF8));
        }
    
        /**
         * 公钥加密
         */
        public static byte[] encryptByPublicKey(String publicKeyStr, byte[] data) {
            return encryptByKey(toPublicKey(publicKeyStr), data);
        }
    
        /**
         * 公钥加密
         */
        public static byte[] encryptByPublicKey(String publicKeyStr, String data) {
            return encryptByKey(toPublicKey(publicKeyStr), data.getBytes(UTF8));
        }
    
        /**
         * 公钥加密
         */
        public static String encryptToStringByPublicKey(PublicKey publicKey, byte[] data) {
            return encode(encryptByKey(publicKey, data));
        }
    
        /**
         * 公钥加密
         */
        public static String encryptToStringByPublicKey(PublicKey publicKey, String data) {
            return encode(encryptByKey(publicKey, data.getBytes(UTF8)));
        }
    
        /**
         * 公钥加密
         */
        public static String encryptToStringByPublicKey(String publicKeyStr, byte[] data) {
            return encode(encryptByKey(toPublicKey(publicKeyStr), data));
        }
    
        /**
         * 公钥加密
         */
        public static String encryptToStringByPublicKey(String publicKeyStr, String data) {
            return encode(encryptByKey(toPublicKey(publicKeyStr), data.getBytes(UTF8)));
        }
    
        // 以下是公钥解密相关方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 公钥解密
         */
        public static byte[] decryptByPublicKey(PublicKey publicKey, byte[] data) {
            return decryptByKey(publicKey, data);
        }
    
        /**
         * 公钥解密
         */
        public static byte[] decryptByPublicKey(PublicKey publicKey, String data) {
            return decryptByKey(publicKey, decode(data));
        }
    
        /**
         * 公钥解密
         */
        public static byte[] decryptByPublicKey(String publicKeyStr, byte[] data) {
            return decryptByKey(toPublicKey(publicKeyStr), data);
        }
    
        /**
         * 公钥解密
         */
        public static byte[] decryptByPublicKey(String publicKeyStr, String data) {
            return decryptByKey(toPublicKey(publicKeyStr), decode(data));
        }
    
        /**
         * 公钥解密
         */
        public static String decryptToStringByPublicKey(PublicKey publicKey, byte[] data) {
            return new String(decryptByKey(publicKey, data), UTF8);
        }
    
        /**
         * 公钥解密
         */
        public static String decryptToStringByPublicKey(PublicKey publicKey, String data) {
            return new String(decryptByKey(publicKey, decode(data)), UTF8);
        }
    
        /**
         * 公钥解密
         */
        public static String decryptToStringByPublicKey(String publicKeyStr, byte[] data) {
            return new String(decryptByKey(toPublicKey(publicKeyStr), data), UTF8);
        }
    
        /**
         * 公钥解密
         */
        public static String decryptToStringByPublicKey(String publicKeyStr, String data) {
            return new String(decryptByKey(toPublicKey(publicKeyStr), decode(data)), UTF8);
        }
    
        // 以下是私钥加密相关方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 私钥加密
         */
        public static byte[] encryptByPrivateKey(PrivateKey privateKey, byte[] data) {
            return encryptByKey(privateKey, data);
        }
    
        /**
         * 私钥加密
         */
        public static byte[] encryptByPrivateKey(PrivateKey privateKey, String data) {
            return encryptByKey(privateKey, data.getBytes(UTF8));
        }
    
        /**
         * 私钥加密
         */
        public static byte[] encryptByPrivateKey(String privateKeyStr, byte[] data) {
            return encryptByKey(toPrivateKey(privateKeyStr), data);
        }
    
        /**
         * 私钥加密
         */
        public static byte[] encryptByPrivateKey(String privateKeyStr, String data) {
            return encryptByKey(toPrivateKey(privateKeyStr), data.getBytes(UTF8));
        }
    
        /**
         * 私钥加密
         */
        public static String encryptToStringByPrivateKey(PrivateKey privateKey, byte[] data) {
            return encode(encryptByKey(privateKey, data));
        }
    
        /**
         * 私钥加密
         */
        public static String encryptToStringByPrivateKey(PrivateKey privateKey, String data) {
            return encode(encryptByKey(privateKey, data.getBytes(UTF8)));
        }
    
        /**
         * 私钥加密
         */
        public static String encryptToStringByPrivateKey(String privateKeyStr, byte[] data) {
            return encode(encryptByKey(toPrivateKey(privateKeyStr), data));
        }
    
        /**
         * 私钥加密
         */
        public static String encryptToStringByPrivateKey(String privateKeyStr, String data) {
            return encode(encryptByKey(toPrivateKey(privateKeyStr), data.getBytes(UTF8)));
        }
    
        // 以下是私钥解密相关方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 私钥解密
         */
        public static byte[] decryptByPrivateKey(PrivateKey privateKey, byte[] data) {
            return decryptByKey(privateKey, data);
        }
    
        /**
         * 私钥解密
         */
        public static byte[] decryptByPrivateKey(PrivateKey privateKey, String data) {
            return decryptByKey(privateKey, decode(data));
        }
    
        /**
         * 私钥解密
         */
        public static byte[] decryptByPrivateKey(String privateKeyStr, byte[] data) {
            return decryptByKey(toPrivateKey(privateKeyStr), data);
        }
    
        /**
         * 私钥解密
         */
        public static byte[] decryptByPrivateKey(String privateKeyStr, String data) {
            return decryptByKey(toPrivateKey(privateKeyStr), decode(data));
        }
    
        /**
         * 私钥解密
         */
        public static String decryptToStringByPrivateKey(PrivateKey privateKey, byte[] data) {
            return new String(decryptByKey(privateKey, data), UTF8);
        }
    
        /**
         * 私钥解密
         */
        public static String decryptToStringByPrivateKey(PrivateKey privateKey, String data) {
            return new String(decryptByKey(privateKey, decode(data)), UTF8);
        }
    
        /**
         * 私钥解密
         */
        public static String decryptToStringByPrivateKey(String privateKeyStr, byte[] data) {
            return new String(decryptByKey(toPrivateKey(privateKeyStr), data), UTF8);
        }
    
        /**
         * 私钥解密
         */
        public static String decryptToStringByPrivateKey(String privateKeyStr, String data) {
            return new String(decryptByKey(toPrivateKey(privateKeyStr), decode(data)), UTF8);
        }
    
        // 以下是密钥加密解密通用依赖方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 密钥加密
         * <p>
         * RSA加密时最大分段长度
         * 512  - 512/8-11=64-11=53
         * 1024 - 1024/8-11=128-11=117
         * 2048 - 2048/8-11=256-11=245
         * 4096 - 4096/8-11=512-11=501
         * 加密时,最大分段长度,是密钥长度的八分之一减十一,加密分段你长度有一个可选范围
         * 1024可以使用117,2048也可以使用117,但最大可使用245,4096也可以使用117,但最大可使用501
         */
        private static byte[] encryptByKey(Key key, byte[] data) {
            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.ENCRYPT_MODE, key);
                return encryptByCipher(cipher, getBlockLength(key) / 8 - 11, data);
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 密钥解密
         * <p>
         * RSA解密时分段长度
         * 512  - 512/8=64
         * 1024 - 1024/8=128
         * 2048 - 2048/8=256
         * 4096 - 4096/8=512
         * 这个长度是固定的,正好是密钥长度的八分之一,不像加密时是一个范围
         */
        private static byte[] decryptByKey(Key key, byte[] data) {
            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.DECRYPT_MODE, key);
                return encryptByCipher(cipher, getBlockLength(key) / 8, data);
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 获取加解密基础分段长度
         */
        private static int getBlockLength(Key key) {
            try {
                if (key instanceof PublicKey) {
                    // 获取公钥长度(位)
                    return getKeyFactory().getKeySpec(key, RSAPublicKeySpec.class).getModulus().toString(2).length();
                } else {
                    // 获取私钥长度(位)
                    return getKeyFactory().getKeySpec(key, RSAPrivateKeySpec.class).getModulus().toString(2).length();
                }
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 加密解密流程
         * 加密过程使用二维数组存储分块加密结果,最终转换为一维数组存储完整加密结果
         * 也可使用ByteArrayOutputStream的write()方法把分块解密结果写到流,toByteArray()方法转换称为完成加密结果
         *
         * @param data           数据
         * @param cipher         密码器
         * @param maxBlockLength 加密/解密块大小, 加密117, 解密128
         * @return 加密/解密结果
         */
        private static byte[] encryptByCipher(Cipher cipher, int maxBlockLength, byte[] data) {
            try {
                int lines = data.length % maxBlockLength == 0 ? data.length / maxBlockLength : data.length / maxBlockLength + 1;
                byte[][] tempArray = new byte[lines][];
                for (int i = 0; i < lines; i++) {
                    int offset = i * maxBlockLength;
                    tempArray[i] = cipher.doFinal(data, offset, i == lines - 1 ? data.length - offset : maxBlockLength);
                }
                int tempArrayTotalLength = 0;
                for (byte[] bytes : tempArray) {
                    tempArrayTotalLength = tempArrayTotalLength + bytes.length;
                }
                byte[] targetArray = new byte[tempArrayTotalLength];
                int index = 0;
                for (byte[] byteArray : tempArray) {
                    for (byte aByte : byteArray) {
                        targetArray[index] = aByte;
                        index++;
                    }
                }
                return targetArray;
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        // 以下是私钥签名相关方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 私钥签名
         *
         * @param privateKey 私钥
         * @param algorithm  签名算法,NONEwithRSA,MD2withRSA,MD5withRSA,SHA1withRSA,SHA224withRSA,SHA256withRSA,SHA384withRSA,SHA512withRSA,SHA512/224withRSA,SHA512/256withRSA
         * @param data       待签名数据
         */
        private static byte[] sign(PrivateKey privateKey, String algorithm, byte[] data) {
            try {
                Signature signature = Signature.getInstance(algorithm);
                signature.initSign(privateKey);
                signature.update(data);
                return signature.sign();
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 私钥签名
         */
        public static String signToString(PrivateKey privateKey, String algorithm, byte[] data) {
            return encode(sign(privateKey, algorithm, data));
        }
    
        /**
         * 私钥签名
         */
        public static String signToString(String privateKeyStr, String algorithm, byte[] data) {
            return encode(sign(toPrivateKey(privateKeyStr), algorithm, data));
        }
    
        /**
         * 私钥签名
         */
        public static String signToString(PrivateKey privateKey, String algorithm, String data) {
            return encode(sign(privateKey, algorithm, data.getBytes(UTF8)));
        }
    
        /**
         * 私钥签名
         */
        public static String signToString(String privateKeyStr, String algorithm, String data) {
            return encode(sign(toPrivateKey(privateKeyStr), algorithm, data.getBytes(UTF8)));
        }
    
        // 以下是公钥验签相关方法 ---------- ---------- ---------- ---------- ----------
    
        /**
         * 公钥验签
         *
         * @param publicKey 公钥
         * @param algorithm 签名算法,NONEwithRSA,MD2withRSA,MD5withRSA,SHA1withRSA,SHA224withRSA,SHA256withRSA,SHA384withRSA,SHA512withRSA,SHA512/224withRSA,SHA512/256withRSA
         * @param data      待验签数据
         * @param sign      签名字符串(BASE64编码)
         */
        public static boolean verify(PublicKey publicKey, String algorithm, byte[] data, String sign) {
            try {
                Signature signature = Signature.getInstance(algorithm);
                signature.initVerify(publicKey);
                signature.update(data);
                return signature.verify(decode(sign));
            } catch (Throwable cause) {
                throw new ServiceException(cause);
            }
        }
    
        /**
         * 公钥验签
         */
        public static boolean verify(String publicKeyStr, String algorithm, byte[] data, String sign) {
            return verify(toPublicKey(publicKeyStr), algorithm, data, sign);
        }
    
        /**
         * 公钥验签
         */
        public static boolean verify(PublicKey publicKey, String algorithm, String data, String sign) {
            return verify(publicKey, algorithm, data.getBytes(UTF8), sign);
        }
    
        /**
         * 公钥验签
         */
        public static boolean verify(String publicKeyStr, String algorithm, String data, String sign) {
            return verify(toPublicKey(publicKeyStr), algorithm, data.getBytes(UTF8), sign);
        }
    
        /**
         * 编码
         */
        private static String encode(byte[] data) {
            return Base64.getEncoder().encodeToString(data);
        }
    
        /**
         * 解码
         */
        private static byte[] decode(String data) {
            return Base64.getDecoder().decode(data);
        }
    
    }
    

    相关文章

      网友评论

          本文标题:RSA 工具包

          本文链接:https://www.haomeiwen.com/subject/yxebdktx.html