RSA Secure Data Connections for API Design Java Demo

本篇讲解基于 Java 语言,实现 HTTPs 和 RSA 加解密通信的 API 示例。
示例代码已提交到 Github,参考链接 Java 示例代码


import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.log4j.Logger;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Enumeration;


public class RSAUtil {

    private static Logger LOGGER = Logger.getLogger(RSAUtil.class);
    private static String KEY_ALGORITHM = "RSA";

    public static final int KEY_SIZE = 2048; // 密钥长度, 一般2048
    public static final int RESERVE_BYTES = 11;
    public final static String KEY_PKCS12 = "PKCS12";

    public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block需要预留11字节
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";// sign值生成方式
    public static final Charset CHARSET_UTF8 = Charset.forName("UTF-8");

    private String algorithm;// 密钥生成模式
    private String signature;// 签名sign生成模式
    private Charset charset;// 编码格式

    private int keySize;// RSA密钥长度必须是64的倍数,在512~65536之间
    private int decryptBlock; // 默认keySize=2048的情况下, 256 bytes
    private int encryptBlock; // 默认keySize=2048的情况下, 245 bytes

    private static KeyFactory keyFactory;
    static {
        try {
            keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            LOGGER.info("生成RSA密钥对异常," + e);
        }
    }

    public RSAUtil() {
        this(CIPHER_ALGORITHM);
    }

    public RSAUtil(String algorithm) {
        this(algorithm, CHARSET_UTF8);
    }

    public RSAUtil(int keySize) {
        this(CIPHER_ALGORITHM, keySize, CHARSET_UTF8, SIGNATURE_ALGORITHM);
    }

    public RSAUtil(String algorithm, Charset charset) {
        this(algorithm, KEY_SIZE, charset, SIGNATURE_ALGORITHM);
    }

    public RSAUtil(String algorithm, int keySize, Charset charset, String signature) {
        this.algorithm = algorithm;
        this.signature = signature;
        this.charset = charset;
        this.keySize = keySize;

        this.decryptBlock = this.keySize / 8;
        this.encryptBlock = decryptBlock - RESERVE_BYTES;
    }

    /**
     * 公钥加密
     * @param publicKeyStr 公钥字符串
     * @param data 需要加密的数据
     * @return 加密后字符串
     */
    public String encrypt(String publicKeyStr, String data) {
        PublicKey publicKey = restorePublicKey(publicKeyStr);
        return encrypt(publicKey, data);
    }

    /**
     * 公钥加密
     * @param publicKey 公钥
     * @param data 需要加密的数据
     * @return 加密后字符串
     */
    public String encrypt(PublicKey publicKey, String data) {
        byte[] bytes = data.getBytes(charset);
        return encrypt(publicKey, bytes);
    }

    /**
     * 公钥加密
     * @param publicKey 公钥
     * @param data 需要加密的数据
     * @return 加密后字符串
     */
    public String encrypt(PublicKey publicKey, byte[] data) {
        byte[] encrypt = null;

        int dataLen = data.length;
        // 计算分段加密的block数 (向上取整)
        int nBlock = (dataLen / encryptBlock);
        if ((dataLen % encryptBlock) != 0) { // 余数非0,block数再加1
            nBlock += 1;
        }
        // 输出buffer, 大小为nBlock个decryptBlock
        ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlock);

        try {
            Cipher cipher = getCipher();
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            // 分段加密
            for (int offset = 0; offset < dataLen; offset += encryptBlock) {
                // block大小: encryptBlock 或 剩余字节数
                int inputLen = (dataLen - offset);
                if (inputLen > encryptBlock) {
                    inputLen = encryptBlock;
                }

                // 得到分段加密结果
                byte[] encryptedBlock = cipher.doFinal(data, offset, inputLen);
                // 追加结果到输出buffer中
                outbuf.write(encryptedBlock);
            }

            outbuf.flush();
            encrypt = outbuf.toByteArray();
            outbuf.close();
        } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException e) {
            LOGGER.info("使用RSA,加密数据异常", e);
        }

        byte[] enData = Base64.encodeBase64(encrypt);
        return StringUtils.newString(enData, charset.name());
    }

    /**
     * 公钥解密
     * @param publicKeyStr 公钥字符串
     * @param data 需要解密的数据
     * @return 解密后字符串
     * @throws Exception 异常
     */
    public String decryptByPublicKey(String publicKeyStr, String data) throws Exception {
        PublicKey publicKey = restorePublicKey(publicKeyStr);
        return decryptByPublicKey(publicKey, data);
    }

    /**
     * 公钥解密
     * @param publicKey 公钥
     * @param data 需要解密的数据
     * @return 解密后字符串
     * @throws Exception 异常
     */
    public String decryptByPublicKey(PublicKey publicKey, String data) throws Exception {
        byte[] bytes = Base64.decodeBase64(data.getBytes(charset));
        return decryptByPublicKey(publicKey, bytes);
    }

    /**
     * 公钥解密
     * @param publicKey 公钥
     * @param data 需要解密的数据
     * @return 解密后字符串
     * @throws Exception 异常
     */
    public String decryptByPublicKey(PublicKey publicKey, byte[] data) throws Exception {
        byte[] decrypt = null;

        int dataLen = data.length;
        // 计算分段解密的block数 (理论上应该能整除)
        int nBlock = (dataLen / decryptBlock);
        if ((dataLen % decryptBlock) != 0) { // 余数非0,block数再加1
            nBlock += 1;
        }
        // 输出buffer, 大小为nBlock个encryptBlock
        ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * encryptBlock);

        Cipher cipher = getCipher();
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        // 分段解密
        for (int offset = 0; offset < data.length; offset += decryptBlock) {
            // block大小: decryptBlock 或 剩余字节数
            int inputLen = (data.length - offset);
            if (inputLen > decryptBlock) {
                inputLen = decryptBlock;
            }

            // 得到分段解密结果
            byte[] decryptedBlock = cipher.doFinal(data, offset, inputLen);
            // 追加结果到输出buffer中
            outbuf.write(decryptedBlock);
        }
        outbuf.flush();
        decrypt = outbuf.toByteArray();
        outbuf.close();

        return StringUtils.newString(decrypt, charset.name());
    }

    /**
     * 私钥加密
     * @param privateKeyStr 私钥字符串
     * @param data 需要加密的数据
     * @return 加密后字符串
     */
    public String encryptByPrivateKey(String privateKeyStr, String data) {
        PrivateKey privateKey = restorePrivateKey(privateKeyStr);
        return encryptByPrivateKey(privateKey, data);
    }

    /**
     * 私钥加密
     * @param privateKey 私钥
     * @param data 需要加密的数据
     * @return 加密后字符串
     */
    public String encryptByPrivateKey(PrivateKey privateKey, String data) {
        byte[] bytes = data.getBytes(charset);
        return encryptByPrivateKey(privateKey, bytes);
    }

    /**
     * 私钥加密
     * @param privateKey 私钥
     * @param data 需要加密的数据
     * @return 加密后字符串
     */
    public String encryptByPrivateKey(PrivateKey privateKey, byte[] data) {
        byte[] encrypt = null;

        int dataLen = data.length;
        // 计算分段加密的block数 (向上取整)
        int nBlock = (dataLen / encryptBlock);
        if ((dataLen % encryptBlock) != 0) { // 余数非0,block数再加1
            nBlock += 1;
        }
        // 输出buffer, 大小为nBlock个decryptBlock
        ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlock);

        try {
            Cipher cipher = getCipher();
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);

            // 分段加密
            for (int offset = 0; offset < dataLen; offset += encryptBlock) {
                // block大小: encryptBlock 或 剩余字节数
                int inputLen = (dataLen - offset);
                if (inputLen > encryptBlock) {
                    inputLen = encryptBlock;
                }

                // 得到分段加密结果
                byte[] encryptedBlock = cipher.doFinal(data, offset, inputLen);
                // 追加结果到输出buffer中
                outbuf.write(encryptedBlock);
            }

            outbuf.flush();
            encrypt = outbuf.toByteArray();
            outbuf.close();
        } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException e) {
            LOGGER.info("使用RSA,加密数据异常", e);
        }

        byte[] enData = Base64.encodeBase64(encrypt);
        return StringUtils.newString(enData, charset.name());
    }

    /**
     * 私钥加密
     * @param privateKey 私钥
     * @param data 需要加密的数据
     * @return 加密后字符串
     */
    public String encryptByPrivateKeyBase64(PrivateKey privateKey, String data) {
        byte[] bytes = new String(new Base64().encode(data.getBytes(CHARSET_UTF8))).getBytes(CHARSET_UTF8);
        return encryptByPrivateKeyBase64(privateKey, bytes);
    }

    public String encryptByPrivateKeyBase64(PrivateKey privateKey, byte[] data) {
        byte[] encrypt = null;

        int dataLen = data.length;
        // 计算分段加密的block数 (向上取整)
        int nBlock = (dataLen / encryptBlock);
        if ((dataLen % encryptBlock) != 0) { // 余数非0,block数再加1
            nBlock += 1;
        }
        // 输出buffer, 大小为nBlock个decryptBlock
        ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlock);

        try {
            Cipher cipher = getCipher();
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);

            // 分段加密
            for (int offset = 0; offset < dataLen; offset += encryptBlock) {
                // block大小: encryptBlock 或 剩余字节数
                int inputLen = (dataLen - offset);
                if (inputLen > encryptBlock) {
                    inputLen = encryptBlock;
                }

                // 得到分段加密结果
                byte[] encryptedBlock = cipher.doFinal(data, offset, inputLen);
                // 追加结果到输出buffer中
                outbuf.write(encryptedBlock);
            }

            outbuf.flush();
            encrypt = outbuf.toByteArray();
            outbuf.close();
        } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException e) {
            LOGGER.info("使用RSA,加密数据异常", e);
        }

        StringBuilder hexRetSB = new StringBuilder();
        for (byte b : encrypt) {
            String hexString = Integer.toHexString(0x00ff & b);
            hexRetSB.append(hexString.length() == 1 ? 0 : "").append(hexString);
        }

        return hexRetSB.toString();
    }

    /**
     * 私钥解密
     * @param privateKeyStr 私钥字符串
     * @param data 需要解密的数据
     * @return 解密后字符串
     * @throws Exception 异常
     */
    public String decrypt(String privateKeyStr, String data) throws Exception {
        PrivateKey privateKey = restorePrivateKey(privateKeyStr);
        return decrypt(privateKey, data);
    }

    /**
     * 私钥解密
     * @param privateKey 私钥
     * @param data 需要解密的数据
     * @return 解密后字符串
     * @throws Exception 异常
     */
    public String decrypt(PrivateKey privateKey, String data) throws Exception {
        byte[] bytes = Base64.decodeBase64(data.getBytes(charset));
        return decrypt(privateKey, bytes);
    }

    /**
     * 私钥解密
     * @param privateKey 私钥
     * @param data 需要解密的数据
     * @return 解密后字符串
     * @throws Exception 异常
     */
    public String decrypt(PrivateKey privateKey, byte[] data) throws Exception {
        byte[] decrypt = null;

        int dataLen = data.length;
        // 计算分段解密的block数 (理论上应该能整除)
        int nBlock = (dataLen / encryptBlock);
        if ((dataLen % encryptBlock) != 0) { // 余数非0,block数再加1
            nBlock += 1;
        }
        // 输出buffer, 大小为nBlock个encryptBlock
        ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * encryptBlock);

        Cipher cipher = getCipher();
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // 分段解密
        for (int offset = 0; offset < data.length; offset += decryptBlock) {
            // block大小: decryptBlock 或 剩余字节数
            int inputLen = (data.length - offset);
            if (inputLen > decryptBlock) {
                inputLen = decryptBlock;
            }

            // 得到分段解密结果
            byte[] decryptedBlock = cipher.doFinal(data, offset, inputLen);
            // 追加结果到输出buffer中
            outbuf.write(decryptedBlock);
        }
        outbuf.flush();
        decrypt = outbuf.toByteArray();
        outbuf.close();

        return StringUtils.newString(decrypt, charset.name());
    }

    /**
     * 根据keyFactory生成Cipher
     * @return cipher
     */
    private Cipher getCipher() {
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(this.algorithm);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            LOGGER.info("生成RSA Cipher异常", e);
        }
        return cipher;
    }

    /**
     * 公钥还原,将公钥转化为PublicKey对象
     * @param publicKeyStr 公钥字符串
     * @return PublicKey对象
     */
    public PublicKey restorePublicKey(String publicKeyStr) {
        PublicKey publicKey = null;

        byte[] keyBytes = Base64.decodeBase64(publicKeyStr.getBytes(charset));
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        try {
            publicKey = keyFactory.generatePublic(x509KeySpec);
        } catch (InvalidKeySpecException e) {
            LOGGER.info("公钥还原,将公钥转化为PublicKey对象异常", e);
        }

        return publicKey;
    }

    /**
     * 私钥还原,将私钥转化为privateKey对象
     * @param privateKeyStr 私钥字符串
     * @return PrivateKey对象
     */
    public PrivateKey restorePrivateKey(String privateKeyStr) {
        PrivateKey privateKey = null;

        byte[] keyBytes = Base64.decodeBase64(privateKeyStr.getBytes(charset));
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        try {
            privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (InvalidKeySpecException e) {
            LOGGER.info("私钥还原,将私钥转化为privateKey对象异常", e);
        }

        return privateKey;
    }

    /**
     * 生成密钥对
     * @param keySize 密钥长度
     * @return 密钥对
     */
    public static KeyPair generateKeyPair(Integer keySize) {
        KeyPair keyPair = null;
        if (null == keySize) {
            keySize = KEY_SIZE;
        }
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            keyPairGen.initialize(keySize);

            keyPair = keyPairGen.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            LOGGER.info("RSA公私钥对生成异常:", e);
        }
        return keyPair;
    }

    /**
     * RSA生成sign值
     * @param privateKeyStr 私钥字符串
     * @param data 数据
     * @return sign值
     * @throws Exception 异常
     */
    public String generateSign(String privateKeyStr, String data) throws Exception {
        return generateSign(restorePrivateKey(privateKeyStr), data);
    }

    /**
     * RSA生成sign值
     * @param privateKey 私钥
     * @param data 数据
     * @return sign值
     * @throws Exception 异常
     */
    public String generateSign(PrivateKey privateKey, String data) throws Exception {
        Signature signature = Signature.getInstance(this.signature);
        signature.initSign(privateKey);
        signature.update(data.getBytes(charset));
        return Base64.encodeBase64String(signature.sign());
    }

    /**
     * RSA验签
     * @param publicKeyStr 公钥字符串
     * @param data 数据
     * @param sign sign值
     * @return 验签是否成功
     * @throws Exception 异常
     */
    public boolean verifyRSA(String publicKeyStr, String data, String sign) throws Exception {
        return verifyRSA(restorePublicKey(publicKeyStr), data, sign);
    }

    /**
     * RSA验签
     * @param publicKey 公钥
     * @param data 数据
     * @param sign sign值
     * @return 验签是否成功
     * @throws Exception 异常
     */
    public boolean verifyRSA(PublicKey publicKey, String data, String sign) throws Exception {
        Signature signature = Signature.getInstance(this.signature);
        signature.initVerify(publicKey);
        signature.update(data.getBytes(charset));

        return signature.verify(Base64.decodeBase64(sign));
    }

    /**
     * 读取密钥对
     *
     * @param file 密钥文件
     * @return 密钥对
     */
    public static KeyPair generateKeyPair(File file, String password) {
        KeyPair keyPair = null;

        if (null == file) {
            return keyPair;
        }

        // 文件内容
        byte[] reads = null;

        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            reads = new byte[inputStream.available()];
            inputStream.read(reads);
        } catch (FileNotFoundException e) {
            LOGGER.error("公钥文件不存在:", e);
        } catch (IOException e) {
            LOGGER.error("公钥文件读取失败:", e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    LOGGER.error("关闭文件流失败:", e);
                }
            }
        }


        String line = null;
        // 生成公钥
        PublicKey publicKey = null;

        // 生成私钥
        PrivateKey privateKey = null;
        try {
            KeyStore ks = KeyStore.getInstance(KEY_PKCS12);
            char[] charPriKeyPass = password.toCharArray();
            ks.load(new ByteArrayInputStream(reads), charPriKeyPass);
            Enumeration aliasEnum = ks.aliases();
            String keyAlias = null;
            if (aliasEnum.hasMoreElements()) {
                keyAlias = aliasEnum.nextElement();
            }
            privateKey = (PrivateKey) ks.getKey(keyAlias, charPriKeyPass);
        } catch (IOException e) {
            // 加密失败
            LOGGER.error("解析文件,读取私钥失败:", e);
        } catch (KeyStoreException e) {
            LOGGER.error("私钥存储异常:", e);
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("不存在的解密算法:", e);
        } catch (CertificateException e) {
            LOGGER.error("证书异常:", e);
        } catch (UnrecoverableKeyException e) {
            LOGGER.error("不可恢复的秘钥异常", e);
        }

        //        if (null != publicKey && null != privateKey) {
        if (null != privateKey) {
            keyPair = new KeyPair(publicKey, privateKey);
        } else {
            throw new RuntimeException("加载密钥失败");
        }

        return keyPair;
    }

    /**
     * 生成私钥 base64加密
     * @param key 密钥
     * @param charset 编码格式
     * @return 私钥
     */
    public static String convertToString(Key key, Charset charset) {
        byte[] keyBytes = key.getEncoded();
        return StringUtils.newString(Base64.encodeBase64(keyBytes), charset.name());
    }

    public int getKeySize() {
        return keySize;
    }

    public Charset getCharset() {
        return charset;
    }

}

2017-10-22

rocket-wing