Java非对称加密RSA

1.RSA工具类

package com.river.blog.utils;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;

/**
 * RSA工具类
 * @author River
 * @date 2018/6/14
 */
public class RSAUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);
    private static final String ALGORITHM = "RSA";
    private static final String CHARSET = "UTF-8";
    private static final int DEFAULT_KEY_SIZE = 1024;

    /**
     * 获取公钥和私钥
     * @return
     */
    public static KeyPair getKeys() {
        KeyPair keyPair = null;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
            keyPairGenerator.initialize(DEFAULT_KEY_SIZE);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("Get keys error! Exception:{}", e.getMessage());
        }
        return keyPair;
    }

    /**
     * 公钥加密
     * @param data
     * @param publicKey
     * @return
     */
    public static String publicEncrypt(String data, Key publicKey) {
        String result = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] bytes = cipher.doFinal(Base64.encodeBase64(data.getBytes(CHARSET)));
            result = Base64.encodeBase64URLSafeString(bytes);
        } catch (Exception e) {
            LOGGER.error("Encrypted with a public key error! Exception:{}", e.getMessage());
        }
        return result;
    }

    /**
     * 私钥解密
     * @param data
     * @param privateKey
     * @return
     */
    public static String privateDecrypt(String data, Key privateKey) {
        String result = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] bytes = cipher.doFinal(Base64.decodeBase64(data));
            result = new String(Base64.decodeBase64(bytes));
        } catch (Exception e) {
            LOGGER.error("Decrypted with a private key error! Exception:{}", e.getMessage());
        }

        return result;
    }

    public static void main(String[] args) throws Exception {
        KeyPair keys = getKeys();
        String data = "Hello World!(你好 世界!)";
        System.out.println("准备加密的数据是:" + data);
        System.out.println("数据的长度是:" + data.getBytes(CHARSET).length);
        String encryptData = publicEncrypt(data, keys.getPublic());
        System.out.println("通过公钥加密后的数据是:" + encryptData);
        String decryptData = privateDecrypt(encryptData, keys.getPrivate());
        System.out.println("通过私钥解密后的数据是:" + decryptData);
    }

    public RSAUtils() {

    }
}

测试结果:

经过测试,加密的时候,如果数据的长度超过117byte,会报错:Data must not be longer than 117 bytes
跟踪一下源码看看这个117是怎么来的,
查看源码可以发现有一个方法:

public static int getByteLength(BigInteger var0) {
    int var1 = var0.bitLength();
    return var1 + 7 >> 3;
}

这个是IDEA反编译出来的结果
var1的值就是之前设置过得keySize的值
写一个main方法测试一下:

    public static void main(String[] args) {
        System.out.println(1024+7>>3);
        System.out.println(2048+7>>3);
    }

结果:(小发现:+号的优先级大于>>

128
256

长度是128和256,keySize是1024的情况下长度限制是128,2048是256,但是事实上keySize=1024的时候长度限制并不是128而是117。
还有一个方法:RSAPadding.getInstance(var10, var7, var3);IDEA反编译的代码有点乱就不贴上来了。
里面写了maxDataSize=byteLength-11;所以才会有117这么个限制,如果把keySize设置成2048,
那么会提示超过了245(256-11)bytes的限制。
 

解密的时候限制是128和256,也就是keySize>>3的结果。

好,找到了问题,那么改善一下代码:

package com.river.blog.utils;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;

/**
 * RSA工具类
 * @author River
 * @date 2018/6/14
 */
public class RSAUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);
    private static final String ALGORITHM = "RSA";
    private static final String CHARSET = "UTF-8";
    private static final int DEFAULT_KEY_SIZE = 1024;

    /**
     * 获取公钥和私钥
     * @return
     */
    public static KeyPair getKeys() {
        KeyPair keyPair = null;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
            keyPairGenerator.initialize(DEFAULT_KEY_SIZE);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("Get keys error! Exception:{}", e.getMessage());
        }
        return keyPair;
    }

    /**
     * 公钥加密
     * @param data
     * @param publicKey
     * @return
     */
    public static String publicEncrypt(String data, Key publicKey) {
        String result = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] bytes = doFinal(cipher, Cipher.ENCRYPT_MODE, Base64.encodeBase64(data.getBytes(CHARSET)));
            result = Base64.encodeBase64URLSafeString(bytes);
        } catch (Exception e) {
            LOGGER.error("Encrypted with a public key error! Exception:{}", e.getMessage());
        }
        return result;
    }

    /**
     * 私钥解密
     * @param data
     * @param privateKey
     * @return
     */
    public static String privateDecrypt(String data, Key privateKey) {
        String result = null;
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] bytes = doFinal(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data.getBytes(CHARSET)));
            result = new String(Base64.decodeBase64(bytes));
        } catch (Exception e) {
            LOGGER.error("Decrypted with a private key error! Exception:{}", e.getMessage());
        }

        return result;
    }

    /**
     * 真正要加密/解密执行的方法,包含了分段加密/解密
     * @param cipher
     * @param mode
     * @param data
     * @return
     * @throws Exception
     */
    private static byte[] doFinal(Cipher cipher, int mode, byte[] data) throws Exception {
        int maxByteLength = DEFAULT_KEY_SIZE >> 3;
        if (mode == Cipher.ENCRYPT_MODE) {
            maxByteLength -= 11;
        }
        int dataLength = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream(dataLength);
        int offSet;
        int index = 0;
        byte[] tmp;
        while (dataLength > (offSet=index*maxByteLength)) {
            if (dataLength-offSet>maxByteLength) {
                tmp = cipher.doFinal(data, offSet, maxByteLength);
            } else {
                tmp = cipher.doFinal(data, offSet, dataLength-offSet);
            }
            index++;
            out.write(tmp, 0, tmp.length);
        }
        byte[] result = out.toByteArray();
        out.close();
        return result;
    }

    public static void main(String[] args) throws Exception {
        KeyPair keys = getKeys();
        String data = "今日头条是个挺好的软件,每天在上面看一些新闻,看一些笑话,看一些视频。\n" +
                "确实是挺有意思,但是也浪费了很多时间。不过最近关注了一个名字是‘Excel精选技巧’\n" +
                "的头条号,他会经常发一些Excel的使用技巧,还有一些Word的使用技巧。我想不定期的看\n" +
                "他的视频,同时自己实验把这些技巧学会,用写博客的方式记录下来(也搜一下别的关于\n" +
                "Office技巧的视频)。";
        System.out.println("准备加密的数据是:\n" + data);
        System.out.println("数据的长度是:" + data.getBytes(CHARSET).length);
        String encryptData = publicEncrypt(data, keys.getPublic());
        System.out.println("通过公钥加密后的数据是:\n" + encryptData);
        String decryptData = privateDecrypt(encryptData, keys.getPrivate());
        System.out.println("通过私钥解密后的数据是:\n" + decryptData);
    }

    public RSAUtils() {

    }
}

测试结果:


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值