RSA 密钥对生成 加密解密 签名验签


import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class RSATool {
    // *********************************************************************************************

    /**
     * @param key 用于加密的密钥,可以是公钥也可以是私钥,用公钥加密那叫做加密,用私钥加密那叫做签名
     * @param beforeEncryptData 加密前的数据,该数据的字节最多为117
     *                          注意:此处被加密的数据的数据类型是字节数组类型,所以我们需要记住该字节数组类型的数据是通过哪种解码(utf-8、utf-16等)方式获取的,
     *                          如此我们才能在解密数据得到字节数组类型的数据后,根据对应的编码(utf-8、utf-16)方式将字节数组类型的数据还原为字符串
     * @return 返回加密后的数据
     *          注意:注意:加密后的数据我们一般将其进行Base64编码转为字符串,而不是使用utf-8、utf-16等编码方式,如此可以避免字符串中出现具有特殊用途的字符
     */
    public static byte[] encrypt(Key key, byte[] beforeEncryptData){

        try {
            // 创建加密解密工具
            Cipher cipher = Cipher.getInstance("RSA");

            // 初始化加密解密工具
            cipher.init(Cipher.ENCRYPT_MODE, key);

            // 正式开始加密解密
            return cipher.doFinal(beforeEncryptData);
            // 注意:加密完后的数据不要直接转为字符串,因为直接转为字符串后,字符串中有可能会有特殊字符,从而导致再次转为字节数组时,会数据错乱
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace(); // 创建加密解密工具失败,因为没有这个加密解密算法
        } catch (NoSuchPaddingException e) {
            e.printStackTrace(); // 创建加密解密工具失败
        } catch (InvalidKeyException e) {
            e.printStackTrace(); // 初始化加密解密工具时失败,因为传递的参数密钥为非法密钥
        } catch (BadPaddingException e) {
            e.printStackTrace(); // 加密解密失败,因为加密解密的数据已经被损坏了
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace(); // 加密解密失败,因为加密解密的数据长度非法,被加密的数据的字节数不可超过117,被解密的数据的字节数不可超过128
        }

        return null;
    }

    /**
     * @param key 用于解密的密钥,可以是公钥也可以是私钥,但是在解密时必须确定了被解密的数据是由公钥和私钥中的哪一个密钥进行加密的
     *            用私钥解密叫解密,用公钥解密叫验签
     * @param beforeDecryptData 解密前的数据,该数据的字节最多为128
     *                          注意:此处被解密的数据的数据类型是字节数组类型,所以如果需要进行解密的数据不是该类型的话,就需要先转为该类型
     *                          而当初字节数组类型是如何转为其他类型的,那么这里就需要按照对应的反转换将其重新转为字节数组类型,
     *                          例如:加密结果的字节数组类型的数据通过Base64编码转为字符串,那么此处就需要将字符串通过Base64解码转为字节数组类型的数据
     * @return 返回解密后的数据
     *          注意:返回的数据是字节数组类型,需要记住当初加密前被加密的字节数组类型数据是如何得来的
     *              例如:当初加密前的字节数组类型数据是通过某种解码(utf-8、utf-16等)方式得来的,所以解密结果的字节数组类型数据可以根据对应的编码方式还原为字符串
     */
    public static byte[] decrypt(Key key, byte[] beforeDecryptData){

        try {
            // 创建加密解密工具
            Cipher cipher = Cipher.getInstance("RSA");

            // 初始化加密解密工具
            cipher.init(Cipher.DECRYPT_MODE, key);

            // 正式开始加密解密
            return cipher.doFinal(beforeDecryptData);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace(); // 创建加密解密工具失败,因为没有这个加密解密算法
        } catch (NoSuchPaddingException e) {
            e.printStackTrace(); // 创建加密解密工具失败
        } catch (InvalidKeyException e) {
            e.printStackTrace(); // 初始化加密解密工具时失败,因为传递的参数密钥为非法密钥
        } catch (BadPaddingException e) {
            e.printStackTrace(); // 加密解密失败,因为加密解密的数据已经被损坏了
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace(); // 加密解密失败,因为加密解密的数据长度非法,被加密的数据的字节数不可超过117,被解密的数据的字节数不可超过128
        }

        return null;
    }

    // *********************************************************************************************

    /**
     * RSA专门使用私钥进行数据签名的方法
     * @param rsaPrivateKey 用于签名的RSA密钥对中的私钥
     * @param beforeSignData 签名前的数据
     * @return 返回签名后的数据
     */
    public static byte[] formalSign(RSAPrivateKey rsaPrivateKey, byte[] beforeSignData){

        try {
            // 创建签名/验签工具对象
            Signature signature = Signature.getInstance("SHA1WithRSA");

            // 使用私钥进行 签名/验签工具 的 签名初始化
            signature.initSign(rsaPrivateKey);

            // 设置要进行签名/验签的数据
            signature.update(beforeSignData);

            // 开始签名/验签
            return signature.sign();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace(); // 创建签名/验签工具失败,因为没有这个签名/验签算法
        } catch (InvalidKeyException e) {
            e.printStackTrace(); // 初始化签名/验签工具时失败,因为初始化时传递的参数私钥为非法私钥
        } catch (SignatureException e) {
            e.printStackTrace(); // 设置被签名/验签的数据时失败 / 正式签名/验签时出现异常
        }

        return null;
    }

    /**
     * RSA使用私钥进行加密的签名方式
     * @param rsaPrivateKey 用于签名的RSA密钥对中的私钥
     * @param beforeSignData 签名前的数据
     * @return 返回签名后的数据
     */
    public static byte[] encryptSign(RSAPrivateKey rsaPrivateKey, byte[] beforeSignData){
        return encrypt(rsaPrivateKey, beforeSignData);
    }

    /**
     * RSA专门使用公钥进行数据验签的方法,判断 afterSignData 是不是 beforeSignData 签名后的数据
     * @param rsaPublicKey 用于验签的RSA密钥对中的公钥
     * @param beforeSignData 被验签的数据
     * @param afterSignData 数据被签名后的数据
     * @return 返回验签结果
     */
    public static boolean formalCheckSign(RSAPublicKey rsaPublicKey, byte[] beforeSignData, byte[] afterSignData){

        try {
            // 创建签名/验签工具对象
            Signature signature = Signature.getInstance("SHA1WithRSA");

            // 使用公钥进行 签名/验签工具 的 验签初始化
            signature.initVerify(rsaPublicKey);

            // 设置要进行签名/验签的数据
            signature.update(beforeSignData);

            // 开始签名/验签
            return signature.verify(afterSignData);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace(); // 创建签名/验签工具失败,因为没有这个签名/验签算法
        } catch (InvalidKeyException e) {
            e.printStackTrace(); // 初始化签名/验签工具时失败,因为初始化时传递的参数公钥为非法私钥
        } catch (SignatureException e) {
            e.printStackTrace(); // 设置被签名/验签的数据时失败 / 正式签名/验签时出现异常
        }

        return false;
    }

    /**
     * RSA使用公钥进行解密的验签方法
     * @param rsaPublicKey 用于验签的RSA密钥对中的公钥
     * @param beforeSignData 被验签的数据
     * @param afterSignData 数据被签名后的数据
     * @return 返回验签结果
     */
    public static boolean decryptCheckSign(RSAPublicKey rsaPublicKey, byte[] beforeSignData, byte[] afterSignData){
        return Arrays.equals(beforeSignData, decrypt(rsaPublicKey, afterSignData));
    }

    // *********************************************************************************************

    /**
     * 将 字符串类型的私钥 转为JAVA所需要的 私钥类型RSAPrivateKey
     */
    public static RSAPrivateKey getPrivateKeyByString(String stringPrivateKey){

        // 对私钥数据进行一定的类型转变处理
        // 将字符串形式的私钥 解码 为 字节数组类型
        // 因为私钥的字节数组类型的数据是通过Base64编码的方法转为字符串的
        // 所以使用Base64解码的方法还原回私钥的字节数组类型的数据
        byte[] bytePrivateKey = Base64Tool.decode(stringPrivateKey.toCharArray());
        // 对字节数组类型的私钥数据进行一定的包装
        PKCS8EncodedKeySpec pKCS8PrivateKey = new PKCS8EncodedKeySpec(bytePrivateKey);

        try {
            // 创建RSA密钥创建工厂
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            // 拿着包装后的私钥数据生成出私钥对象,RSAPrivateKey 才是在JAVA中进行解密或签名时用的私钥数据类型
            return (RSAPrivateKey) keyFactory.generatePrivate(pKCS8PrivateKey);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace(); // 密钥生成工厂创建失败
        } catch (InvalidKeySpecException e) {
            e.printStackTrace(); // 密钥生成失败
        }

        return null;
    }

    /**
     * 将 字符串类型的公钥 转为JAVA所需要的 公钥类型RSAPublicKey
     */
    public static RSAPublicKey getPublicKeyByString(String stringPublicKey){

        // 对公钥数据进行一定的类型转变处理
        // 将字符串形式的公钥 解码 为 字节数组类型
        // 因为公钥的字节数组类型的数据是通过Base64编码的方法转为字符串的
        // 所以使用Base64解码的方法还原回公钥的字节数组类型的数据
        byte[] bytePublicKey = Base64Tool.decode(stringPublicKey.toCharArray());
        // 对字节数组类型的公钥数据进行一定的包装
        X509EncodedKeySpec x509PublicKey = new X509EncodedKeySpec(bytePublicKey);

        try {
            // 创建RSA密钥创建工厂
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            // 拿着包装后的公钥数据生成出公钥对象,RSAPublicKey 才是在JAVA中进行加密或验签时用的公钥数据类型
            return (RSAPublicKey) keyFactory.generatePublic(x509PublicKey);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace(); // 密钥生成工厂创建失败
        } catch (InvalidKeySpecException e) {
            e.printStackTrace(); // 密钥生成失败
        }

        return null;
    }

    // *********************************************************************************************

    /**
     * 生成字符串类型的RSA密钥对
     * @return  字符串数组中的第一个元素是:经过Base64编码后的RSA公钥
     *          字符串数组中的第二个元素是:经过Base64编码后的RSA私钥
     */
    public static String[] generatorRSAKey(){

        try {

            // 创建 基于RSA算法 的 公钥和私钥对的生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");

            // 初始化 公钥和私钥对的生成器
            keyPairGenerator.initialize(1024, new SecureRandom());

            // 执行代码生成 密钥对对象
            KeyPair keyPair = keyPairGenerator.generateKeyPair();

            // 注意:字节数字类型的密钥才是可以在所有系统/应用/软件之间通用的
            // 获取字节数组类型的 公钥
            byte[] publicKeyByte = keyPair.getPublic().getEncoded();
            // 获取字节数组类型的 私钥
            byte[] privateKeyByte = keyPair.getPrivate().getEncoded();

            // 因为密钥在之后使用时,有可能会被存储、传输等,而我们又不能直接使用字节数组类型,所以需要转为字符串类型
            // 因为字节数组类型直接转为字符串时,使用的是系统默认的编码方式(通常是utf-8编码),
            // 同时我们又无法确认密钥的字节数组类型数据中的字节在经过编码后,其编码结果字符串是否包含一些特殊用途的字符,
            // 所以为了编码结果字符串能在不同系统/应用/软件之间进行传输,所以在将字节数组类型的数据转为字符串时,使用Bas64的编码方式
            String publicKey = new String(Base64Tool.encode(publicKeyByte));
            String privateKey = new String(Base64Tool.encode(privateKeyByte));

            return new String[]{publicKey, privateKey};
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return null;
    }
}

你可能感兴趣的:(安全)