使用Java實現RSA加密解密

1. 介紹

RSA加密算法是一種非對稱加密算法。在公開密鑰加密和電子商業中RSA被廣泛使用。RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。

這裏提供一個在線進行RSA加密解密:http://tool.chacuo.net/cryptrsapubkey

2. 具體實現

密鑰可以是字節數組,也可以是Base64編碼過的。加密後對數據的輸出採用2種方式:Base64、Hex,其中Base64使用的是Android SDK裏面的API,具體代碼如下:

package com.fantasy.blogdemo.crypto.utils;

import android.util.Base64;

import com.fantasy.blogdemo.utils.ConvertUtils;

import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * RSA加解密工具類
 * <pre>
 *     author  : Fantasy
 *     version : 1.0, 2019-08-25
 *     since   : 1.0, 2019-08-25
 * </pre>
 */
public class RSAUtils {
    private static final String CHARSET = "UTF-8";

    /**
     * 加密,輸出Base64字符串密文
     *
     * @param data           明文
     * @param publicKey      公鑰
     * @param keySize        公鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 密文
     */
    public static String encryptBase64(String data, byte[] publicKey, int keySize, String transformation) {
        try {
            return Base64.encodeToString(handle(data.getBytes(CHARSET), publicKey, keySize, transformation,
                    true), Base64.NO_WRAP);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密,輸出Base64字符串密文
     *
     * @param data           明文
     * @param publicKey      公鑰(Base64字符串)
     * @param keySize        公鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 密文
     */
    public static String encryptBase64(String data, String publicKey, int keySize, String transformation) {
        try {
            return Base64.encodeToString(handle(data.getBytes(CHARSET), Base64.decode(publicKey, Base64.NO_WRAP),
                    keySize, transformation, true), Base64.NO_WRAP);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密,輸出十六進制字符串密文
     *
     * @param data           明文
     * @param publicKey      公鑰
     * @param keySize        公鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 密文
     */
    public static String encryptHex(String data, byte[] publicKey, int keySize, String transformation) {
        try {
            return ConvertUtils.bytesToHexString(handle(data.getBytes(CHARSET), publicKey, keySize,
                    transformation, true));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 加密,輸出十六進制字符串密文
     *
     * @param data           明文
     * @param publicKey      公鑰(Base64字符串)
     * @param keySize        公鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 密文
     */
    public static String encryptHex(String data, String publicKey, int keySize, String transformation) {
        try {
            return ConvertUtils.bytesToHexString(handle(data.getBytes(CHARSET),
                    Base64.decode(publicKey, Base64.NO_WRAP), keySize, transformation, true));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密,密文爲Base64字符串
     *
     * @param data           密文
     * @param privateKey     私鑰
     * @param keySize        私鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 明文
     */
    public static String decryptBase64(String data, byte[] privateKey, int keySize, String transformation) {
        try {
            return new String(handle(Base64.decode(data, Base64.NO_WRAP), privateKey, keySize,
                    transformation, false), CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密,密文爲Base64字符串
     *
     * @param data           密文
     * @param privateKey     私鑰(Base64字符串)
     * @param keySize        私鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 明文
     */
    public static String decryptBase64(String data, String privateKey, int keySize, String transformation) {
        try {
            return new String(handle(Base64.decode(data, Base64.NO_WRAP), Base64.decode(privateKey, Base64.NO_WRAP),
                    keySize, transformation, false), CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密,密文爲十六進制字符串
     *
     * @param data           密文
     * @param privateKey     私鑰
     * @param keySize        私鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 明文
     */
    public static String decryptHex(String data, byte[] privateKey, int keySize, String transformation) {
        try {
            return new String(handle(ConvertUtils.hexStringToBytes(data), privateKey, keySize, transformation, false), CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密,密文爲十六進制字符串
     *
     * @param data           密文
     * @param privateKey     私鑰(Base64字符串)
     * @param keySize        私鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @return 明文
     */
    public static String decryptHex(String data, String privateKey, int keySize, String transformation) {
        try {
            return new String(handle(ConvertUtils.hexStringToBytes(data), Base64.decode(privateKey, Base64.NO_WRAP),
                    keySize, transformation, false), CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 處理數據,加密或解密
     *
     * @param data           數據
     * @param key            密鑰
     * @param keySize        密鑰大小,舉例:1024, 2048...
     * @param transformation 類型,格式爲:加密算法/加密模式/填充方式,舉例:<i>RSA/None/PKCS1Padding</i>。<br/>
     *                       相關取值可以查看下列兩個文檔:
     *                       <ul>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/api">JavaSE 8 API</a>
     *                       中的 javax.crypto.Cipher</li>
     *                       <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
     *                       Standard Algorithm Name Documentation</a></li>
     *                       </ul>
     * @param isEncrypt      如果是加密,則爲true;如果爲解密,則爲false
     * @return 加密後或解密後的字節數組
     * @throws Exception 異常
     */
    private static byte[] handle(byte[] data, byte[] key, int keySize, String transformation,
                                 boolean isEncrypt) throws Exception {
        Key rsaKey;
        if (isEncrypt) {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
            rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
        } else {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);
            rsaKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
        }
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, rsaKey);
        int len = data.length;
        int maxLen = keySize / 8;
        if (isEncrypt) {
            String lowerTrans = transformation.toLowerCase();
            if (lowerTrans.endsWith("pkcs1padding")) {
                maxLen -= 11;
            }
        }
        int count = len / maxLen;
        if (count > 0) {
            byte[] ret = new byte[0];
            byte[] buff = new byte[maxLen];
            int index = 0;
            for (int i = 0; i < count; i++) {
                System.arraycopy(data, index, buff, 0, maxLen);
                ret = joins(ret, cipher.doFinal(buff));
                index += maxLen;
            }
            if (index != len) {
                int restLen = len - index;
                buff = new byte[restLen];
                System.arraycopy(data, index, buff, 0, restLen);
                ret = joins(ret, cipher.doFinal(buff));
            }
            return ret;
        } else {
            return cipher.doFinal(data);
        }
    }

    /**
     * 合併兩個字節數組
     *
     * @param prefix 前一個字節數組
     * @param suffix 後一個字節數組
     * @return 字節數組
     */
    private static byte[] joins(byte[] prefix, byte[] suffix) {
        byte[] ret = new byte[prefix.length + suffix.length];
        System.arraycopy(prefix, 0, ret, 0, prefix.length);
        System.arraycopy(suffix, 0, ret, prefix.length, suffix.length);
        return ret;
    }

}

3. 使用方式

舉例:使用"RSA/None/PKCS1Padding"模式,公鑰和密鑰都是Base64字符串,密鑰長度爲1024,輸出的密文爲Base64字符串。 對於Android,RSA/ECB/PKCS1padding 和 RSA/None/PKCS1Padding 等效。

// 對於Android,RSA/ECB/PKCS1padding 和 RSA/None/PKCS1Padding 等效
//String transformation = "RSA/ECB/PKCS1padding";
String transformation = "RSA/None/PKCS1Padding";
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhSzPPnFn41iaz+t4tI4kbaXNuNFOsI8hFeCYtlwPFKRbETHbBS10bMvUbOWLFtRgZV3L924GQ9orbomEmJ1nWyaSO8iBbZAyiWUP5PJJh/b9kHj1MMwG712bGfYYPdjkRprNpzU9w4UBzUMKKUoHU4c/Gbb4XeBK9LNTPWQL4YwIDAQAB";
String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOFLM8+cWfjWJrP63i0jiRtpc240U6wjyEV4Ji2XA8UpFsRMdsFLXRsy9Rs5YsW1GBlXcv3bgZD2ituiYSYnWdbJpI7yIFtkDKJZQ/k8kmH9v2QePUwzAbvXZsZ9hg92ORGms2nNT3DhQHNQwopSgdThz8Ztvhd4Er0s1M9ZAvhjAgMBAAECgYEAxwNLTUXsJGfn4Gzm/jC52MEZ+mu2zgT90IAGGZeg+PUG63gwHyeXo4MsCVRz7/m8xAX/ykew+IEQwFt8Pdvc+rrs5yml4gOBPfhpau5QaI75xNjnyH7UA3mbRCZeyZrvuKqtY/f8pCgzy3EBWnRpkcsqeE6bsOQrD45mltr+0QECQQDynvhKEh+hD5xBpF/DIP8Fp6fizexHdA6+aZT/gLaFA4XgZ9HEDDBhvNdadyYUNOLWhkxRHv6CkT5azfLXsJEhAkEA7begtbBCDXDf1+DRh3j2S8zcv6+utYgcpjvxZqjbPi6UIWXLxI80PIwQ0uouHCUMjikBA6VX9vTbw9TZ/IelAwJBAKI3W7baiz86mrTg3A4w/5GeWQexuurDVCBHo5F5U493nYk+oOe9ZpPSmQIpa9JS0d+xB1GtsWlHBzPbQySnL0ECQACjqvT1QTl6EbPXwp92eqQtQmQMbNW4RiaUjlpyrVs5zkAho1T9EyMqJPNI71n6VVa/8k8WxyAdkZ7ZlBikCQEkNe1+sAKnh+AFGCJ+6WAq1J2RuIgcA6bVL3ip7F2NHdE+N+tR9JqWw3JNCweWmAlzKIGs6eKSVD5egzKaLXss=";
String data = "blogDemo123"; // 待加密的數據

// 公鑰加密,輸出Base64字符串
String result1 = RSAUtils.encryptBase64(data, publicKey, 1024, transformation);;

// 私鑰解密
String result2 = RSAUtils.decryptBase64(result1, privateKey, 1024, transformation);

想看更多例子可以到我的GitHub上面看看BlogDemo

 

如果想進一步交流和學習的同學,可以加一下QQ羣哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章