RSA 数据加密解密

RSA 是一种非对称的数据加密算法,他有两对钥匙,分别为公钥和私钥,公钥加密只能用私钥解密,那相应的私钥加密,也只能由公钥解密。这样保证了一定的安全性。

RSA 的用处有两种:
– 是对数据的加密和解密,比如银行卡,身份证,手机号,等等这些比较敏感的信息,进行加密。
– 加签和验签,多用在需要公网传输数据的时候,包括https的核心原理也是这样的。

https的工作原理可以参考 https加密通信过程图解

show me the code

package com.netfinworks.mag.util.sign;

import org.apache.commons.codec.binary.Base64;

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.xml.bind.DatatypeConverter;

/** * Created by mazhenhua on 2017/05/06. */
public class RSATest {



    /** * 签名算法 */
    public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";


    private static byte[] h2b(String hex){
        return DatatypeConverter.parseHexBinary(hex);
    }
    private static String b2h(byte[] bytes){
        return DatatypeConverter.printHexBinary(bytes);
    }

    private static SecureRandom sr = new SecureRandom();

    public static KeyPair newKeyPair(int rsabits) throws NoSuchAlgorithmException {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(rsabits, sr);
        return generator.generateKeyPair();
    }

    public static byte[] pubKeyToBytes(PublicKey key){
        return key.getEncoded(); // X509 for a public key
    }
    public static byte[] privKeyToBytes(PrivateKey key){
        return key.getEncoded(); // PKCS8 for a private key
    }

    public static PublicKey bytesToPubKey(byte[] bytes) throws InvalidKeySpecException, NoSuchAlgorithmException{
        return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(bytes));
    }
    public static PrivateKey bytesToPrivKey(byte[] bytes) throws InvalidKeySpecException, NoSuchAlgorithmException{
        return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(bytes));
    }

    public static byte[] encryptWithPubKey(byte[] input, PublicKey key) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(input);
    }
    public static byte[] decryptWithPrivKey(byte[] input, PrivateKey key) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(input);
    }

    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    /** * * @param text 待验签数据 * @param sign 验签值 * @param publicKey 公钥 * @param charset 编码字符 * @return * @throws Exception */
    public static boolean verify(String text, String sign, PublicKey publicKey, String charset) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(getContentBytes(text, charset));
        return signature.verify(Base64.decodeBase64(sign));
    }

    /** * * @param text 待加签数据 * @param privateK 私钥 * @param charset 编码字符 * @return * @throws Exception */
    public static String sign(String text, PrivateKey privateK, String charset) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(getContentBytes(text, charset));
        byte[] result = signature.sign();
        return Base64.encodeBase64String(result);
    }

    public static void main(String[] args) throws Exception {
        KeyPair kp = newKeyPair(1<<11); // 2048 bit RSA; might take a second to generate keys
        PublicKey pubKey = kp.getPublic();
        PrivateKey privKey = kp.getPrivate();
        String plainText = "Dear Bob,\nWish you were here.\n\t--Alice";
        byte[] cipherText = encryptWithPubKey(plainText.getBytes("UTF-8"),pubKey);
        System.out.println("cipherText: "+b2h(cipherText));
        System.out.println("plainText:");
        System.out.println(new String(decryptWithPrivKey(cipherText,privKey),"UTF-8"));
    }



}

main方法中,只有加解密的,加签验签的方法我已经加上了。

这里用的祕钥对都是自己生成的2048位长度,一般情况下都是使用的都是被base64后的字符串,或者是购买的密钥对证书。这个密钥也可以自己用JDK生成,网上很多方法。

很多网站还在用1024为长度的祕钥,我听过很多同事1024长度的祕钥已经有人能够破解了。现在建议用2048为长度的祕钥。

祕钥长度越长加密的复杂度就越高,相应的破解起来就越困难,理论上当祕钥达到一定长度是不可能被破解的。

参考资料:stackoverflow

点赞