RSA in Java

本文使用 Java Security API 演示 RSA 算法的使用过程。

1 RSA 加密算法属于非对称加密算法,第一步需要生成密钥对

public static KeyPair initKeyPair()
    throws NoSuchAlgorithmException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
    keyPairGenerator.initialize(KEY_SIZE);
    return keyPairGenerator.generateKeyPair();
}

生成的 java.security.KeyPair 对象虽然已经实现了 java.io.Serializable 接口,但为了更方便存储和传输,常需要使用 Base64 编码成字符串。

public static Map initKeys()
    throws NoSuchAlgorithmException {
    KeyPair keyPair = initKeyPair();
    PublicKey publicKey = keyPair.getPublic();
    PrivateKey privateKey = keyPair.getPrivate();
    return new HashMap() {{
        put(RSA_PUBLIC_KEY, Base64.getEncoder().encodeToString(publicKey.getEncoded()));
        put(RSA_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey.getEncoded()));
    }};
}

2 有了密钥对后就可以使用其中一个加密,使用另一个解密,首先给出公钥加密的代码片段

public static byte[] encryptByPublicKey(byte[] data, String key)
    throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {
    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
    KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
    PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    return cipher.doFinal(data);
}

使用公钥加密后的密文只能使用私钥解密

public static byte[] decryptByPrivateKey(byte[] data, String key)
    throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
    KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
    PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    return cipher.doFinal(data);
}

3 下面是使用私钥加密的代码片段

public static byte[] encryptByPrivateKey(byte[] data, String key)
    throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
    KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
    PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.ENCRYPT_MODE, privateKey);
    return cipher.doFinal(data);
}

同样,使用私钥加密的密文只能使用公钥解密

public static byte[] decryptByPublicKey(byte[] data, String key)
    throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
    BadPaddingException, IllegalBlockSizeException {
    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
    KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
    PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.DECRYPT_MODE, publicKey);
    return cipher.doFinal(data);
}

以上便是使用 Java 原生的 Security API 实现 RSA 加解密的工具代码,可以在此基础上进一步封装,如接收加解密的数据不止是 byte[] 类型,可以直接接收 String 或其他特殊类型的数据,返回的加解密后的结果也可以封装成 String 等其它类型。同一类型密钥的加解密代码中也存在大量重复代码,可以统一提取成样板代码。

以下是完整的 RSA 加解密工具类代码

package learn.spring.security.util;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class RSAUtils {
    
    private static final String ALGORITHM = "RSA";
    
    private static final int KEY_SIZE = 1024;
    
    public static final String RSA_PUBLIC_KEY = "RSA_PUBLIC_KEY";
    
    public static final String RSA_PRIVATE_KEY = "RSA_PRIVATE_KEY";
    
    /**
     * 生成密钥对
     */
    public static KeyPair initKeyPair()
        throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        return keyPairGenerator.generateKeyPair();
    }
    
    /**
     * 生成密钥对,并保存为 Map 对象
     */
    public static Map initKeys()
        throws NoSuchAlgorithmException {
        KeyPair keyPair = initKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        return new HashMap() {{
            put(RSA_PUBLIC_KEY, Base64.getEncoder().encodeToString(publicKey.getEncoded()));
            put(RSA_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey.getEncoded()));
        }};
    }
    
    /**
     * 公钥加密
     */
    public static byte[] encryptByPublicKey(byte[] data, String key)
        throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
        BadPaddingException, IllegalBlockSizeException {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }
    
    /**
     * 私钥解密
     */
    public static byte[] decryptByPrivateKey(byte[] data, String key)
        throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
        BadPaddingException, IllegalBlockSizeException {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }
    
    /**
     * 私钥加密
     */
    public static byte[] encryptByPrivateKey(byte[] data, String key)
        throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
        BadPaddingException, IllegalBlockSizeException {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }
    
    /**
     * 公钥解密
     */
    public static byte[] decryptByPublicKey(byte[] data, String key)
        throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
        BadPaddingException, IllegalBlockSizeException {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }
}

附单元测试代码

package learn.spring.security.util;

import org.junit.Assert;
import org.junit.Test;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Map;
import java.util.Objects;

public class RSAUtilsTests {
    
    @Test
    public void testInitKeyPair()
        throws NoSuchAlgorithmException {
        KeyPair keyPair = RSAUtils.initKeyPair();
        Assert.assertNotNull(keyPair);
        Assert.assertNotNull(keyPair.getPublic());
        Assert.assertNotNull(keyPair.getPrivate());
        Assert.assertNotEquals(keyPair.getPublic(), keyPair.getPrivate());
    }
    
    @Test
    public void testInitKeys()
        throws NoSuchAlgorithmException {
        Map keys = RSAUtils.initKeys();
        for (Map.Entry entry : keys.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            Assert.assertTrue(key.equals(RSAUtils.RSA_PUBLIC_KEY) || key.equals(RSAUtils.RSA_PRIVATE_KEY));
            Assert.assertTrue(Objects.nonNull(value) && !value.trim().equals(""));
        }
    }
    
    @Test
    public void testEncryptByPublicAndDecryptByPrivate()
        throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException,
        InvalidKeySpecException, NoSuchPaddingException {
        String msg = "Hello, RSA!";
        Map keys = RSAUtils.initKeys();
        byte[] encryptedMsg = RSAUtils.encryptByPublicKey(msg.getBytes(), keys.get(RSAUtils.RSA_PUBLIC_KEY));
        byte[] decryptedMsg = RSAUtils.decryptByPrivateKey(encryptedMsg, keys.get(RSAUtils.RSA_PRIVATE_KEY));
        String restoredMsg = new String(decryptedMsg);
        System.out.println(restoredMsg);
        Assert.assertEquals(msg, restoredMsg);
    }
    
    @Test
    public void testEncryptByPrivateAndDecryptByPublic()
        throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException,
        InvalidKeySpecException, NoSuchPaddingException {
        String msg = "Introduce RSA";
        Map keys = RSAUtils.initKeys();
        byte[] encryptedMsg = RSAUtils.encryptByPrivateKey(msg.getBytes(), keys.get(RSAUtils.RSA_PRIVATE_KEY));
        byte[] decryptedMsg = RSAUtils.decryptByPublicKey(encryptedMsg, keys.get(RSAUtils.RSA_PUBLIC_KEY));
        String restoredMsg = new String(decryptedMsg);
        System.out.println(restoredMsg);
        Assert.assertEquals(msg, restoredMsg);
    }
}

你可能感兴趣的:(RSA in Java)