JDK中JCA的简单使用(三)---RSA加密解密

版权声明:本博客为学习、笔记之用,以笔记形式记录学习的知识与感悟。学习过程中可能参考各种资料,如觉文中表述过分引用,请务必告知,以便迅速处理。如有错漏,不吝赐教。 https://blog.csdn.net/Cappadocia_/article/details/83153266

Cipher 类

Cipher类提供用于加密和解密的加密密码功能。加密是获取数据(称为明文)和 密钥,并且生成数据(密文)对于不知道密钥的第三方无意义的过程。解密是一个相反的过程:采用密文和密钥并生成明文。

对称与非对称加密
有两种主要的加密类型:对称(也称为密钥)和非对称(或公钥加密))。在对称加密中,加密和解密数据的密钥相同。保持密钥的私密性对于保持数据机密至关重要。另一方面,非对称密码术使用公钥/私钥对来加密数据。用一个密钥加密的数据用另一个密钥解密。用户首先生成公钥/私钥对,然后在任何人都可以访问的可信数据库中发布公钥。希望与该用户安全通信的用户使用检索到的公钥加密数据。只有私钥的持有者才能解密。保密私钥对于此计划至关重要。
非对称算法(例如RSA)通常比对称算法慢得多。这些算法不是为有效保护大量数据而设计的。实际上,非对称算法用于交换较小的密钥,用于初始化对称算法。

流与分组密码
密码有两种主要类型:块和 流。分组密码一次处理整个块,通常长度为多个字节。如果没有足够的数据来构成完整的输入块,则必须填充数据:即,在加密之前,必须添加虚拟字节以使密码的块大小为倍。然后在解密阶段剥离这些字节。填充可以由应用程序完成,也可以通过初始化密码来使用填充类型,例如“PKCS5PADDING”。相反,流密码一次处理一个小单元(通常是一个字节或甚至一个比特)的输入数据。这允许密码处理任意数量的数据而无需填充。

运作模式
当使用简单的分组密码进行加密时,两个相同的明文块将始终产生相同的密文块。试图打破密文的密码分析者如果注意到重复文本的块,将会有一个更容易的工作。为了增加文本的复杂性,反馈模式使用前一个输出块来改变输入块,然后再应用加密算法。第一个块需要一个初始值,这个值称为初始化向量(IV)。由于IV只是在加密之前改变数据,因此IV应该是随机的,但不一定需要保密。存在多种模式,诸如CBC(密码块链接),CFB(密码反馈模式)和OFB(输出反馈模式)。ECB(电子密码本模式)是不受块位置或其他密文块影响的模式。由于ECB密文使用相同的明文/密钥是相同的,因此该模式通常不适用于加密应用程序。

主要应用场景

需要将传输的数据进行加密,例如支付信息等,这里需要注意的是,加密解密字符串有长度限制,需要分块加解密

使用流程:
1.使用私钥加密

/**
     * 私钥加密
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return encrypt(data, cipher);
    }

2.使用公钥解密

  public static byte[] decryptByPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return decrypt(encryptedData, cipher);
    }

完整代码:

  /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    
    /**
     * 私钥解密
     *
     * @param encryptedData 已加密数据
     * @param privateKey    私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        return decrypt(encryptedData, cipher);
    }

    /**
     * 公钥解密
     *
     * @param encryptedData 已加密数据
     * @param publicKeyStr     公钥
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKeyStr)
            throws Exception {
        PublicKey publicKey = getPublickey(publicKeyStr);
        return decryptByPublicKey(encryptedData,publicKey);
    }

    public static byte[] decryptByPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return decrypt(encryptedData, cipher);
    }



    /**
     * 公钥加密
     *
     * @param data      源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        return encrypt(data, cipher);
    }

    /**
     *
     * 私钥字符串加密
     *
     * @param data       源数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
            throws Exception {
        PrivateKey privateK = getPrivateKey(privateKey);
        return encryptByPrivateKey(data,privateK);
    }

    /**
     * 私钥加密
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return encrypt(data, cipher);
    }

    /**
     * 分块加密
     *
     * @param data
     * @param cipher
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, Cipher cipher) throws Exception {
        return dealWithData(data, cipher, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 分块解密
     *
     * @param encryptedData
     * @param cipher
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] encryptedData, Cipher cipher) throws Exception {
        return dealWithData(encryptedData, cipher, MAX_DECRYPT_BLOCK);
    }

    /**
     * 分块处理数据
     *
     * @param data
     * @param cipher
     * @param block
     * @return
     * @throws Exception
     */
    public static byte[] dealWithData(byte[] data, Cipher cipher, int block) throws Exception {
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > block) {
                cache = cipher.doFinal(data, offSet, block);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * block;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    public static void main(String[] args) throws Exception {
        String data = "Signature类是一个引擎类,提供加密的数字签名算法,例如DSA或RSAwithMD5。加密安全签名算法采用任意大小的输入和私钥,并生成一个相对较短(通常是固定大小)的字节串——签名。RSA最大解密密文大小为128个字节,RSA最大加密明文大小为117个字节,测试加密解密字符串";
        Map keyPair = getKeyPair();
        String privateKeyStr = getPrivateKeyStr(keyPair);
        String publicKeyStr = getPublicKeyStr(keyPair);

        byte[] encryptData = encryptByPrivateKey(data.getBytes(),privateKeyStr);
        String encryptStr = Base64.getEncoder().encodeToString(encryptData);
        log.info("加密后的字符串为:{}",encryptStr);

        byte[] decryptData = Base64.getDecoder().decode(encryptStr);
        byte[] decrypt = decryptByPublicKey(decryptData,publicKeyStr);
        log.info("解密后的字符串为:{}",new String(decrypt));

    }

获得的Cipher对象getInstance必须初始化为四种模式之一,这四种模式被定义为类中的最终整数常量Cipher。这些模式可以通过它们的符号名称引用,如下所示,以及每种模式的用途说明:
ENCRYPT_MODE
加密数据。
DECRYPT_MODE
解密数据。
WRAP_MODE
将a包装java.security.Key成字节,以便可以安全地传输密钥。
UNWRAP_MODE
将先前包装的密钥展开到 java.security.Key对象中。

完整工具类下载:https://download.csdn.net/download/cappadocia_/10730247

猜你喜欢

转载自blog.csdn.net/Cappadocia_/article/details/83153266