import org.apache.commons.codec.binary.Base64
;
import org.junit.
Test;
import javax.crypto.Cipher
;
import java.io.ByteArrayOutputStream
;
import java.security.KeyFactory
;
import java.security.PrivateKey
;
import java.security.PublicKey
;
import java.security.spec.PKCS8EncodedKeySpec
;
import java.security.spec.X509EncodedKeySpec
;
/**
 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式<br/>
 * 由于非对称加密速度极其缓慢,一般文件不使用它来加密而是使用对称加密,<br/>
 * 非对称加密算法可以用来对对称加密的密钥加密,这样保证密钥的安全也就保证了数据的安全
 */
public class RSAEncrypterTest {
    
/**
     * 加密算法RSA
     */
    private static final String 
KEY_ALGORITHM = 
"RSA";
    /**
     * 签名算法
     */
    private static final String 
CIPHER_ALGORITHM = 
"RSA/ECB/PKCS1Padding";
    /**
     * 私钥
     * openssl pkcs8 -topk8 -inform PEM -in p_key.pem -outform PEM -nocrypt
     */
    private static final String 
PRIVATE_KEY = 
"" +
        
"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPMhwl+bv8MlboJf" +
        
"u6W5ec0SoQQxsHn8fHmfIEvWpLc1K6uiycJfOR4nHz2hRPMquw5uuz/yWvDUkrcl" +
        
"dabWKuI9tq2kv8Kxv1M2E+9DE+ySCKqsrAp/XZMlsy8DP+fkPEATQRv4EUkH5eD6" +
        
"x+0BlOpbZVtXdC7vyWeGSPOwldiRAgMBAAECgYBZ5ozB34xbszaWnKF9GQURsCa7" +
        
"r0frA1Jfglhl0ntbhxybvXjPF3i+Tob9uekN5Sn/nzA75/h6fo+1ivNi+mQmRVf2" +
        
"6h5/kfRYy0y3RNPFOTnd6B36etUx6EpzXlpT/q+igRqlu4ts4P1heBGOdqtLlSp9" +
        
"0KqHnjieFEgMpMschQJBAP1HdyYoSJwbYd5J3DpUIEpfjIn1MkJ2OyrEk6MQypqF" +
        
"G8ilOJCMsFFW8oY6pPPYT9z+3NwPqxz+UWU9Kv9WjXcCQQD1vmNcAB4eg99uEExs" +
        
"WOHEcWFNZ654K/iJ915A/c7cIJSBkoe80kqDYg+UXuY/r7ZuUi3zPSECZzs4ge8H" +
        
"+yw3AkEAzPSM1WLZxwFdhAzO2qCC9Mw3JNg3EJMuM+CY++a1nK7EGeyhRk+iyl+K" +
        
"p9ZC71QG5HWU8br4I+EkqsEBz1gR5wJAf8/kEaemAq0H+lDTzs5zrh5iQ0YXwjzs" +
        
"aaWVCix3+JmhY7r4ika9ppPMEplBHAKh2cM4TviOQJe8f2llDzueBQJBAPaYpKS7" +
        
"fTh7xi5WkJrStcNVOVgThxgmIEg3mGTztBMe87WTgUAPMr4N9gewNf+5jx231cFB" +
        
"3gYkkPgWNOYL/2Y=";
    /**
     * 公钥
     */
    private static final String 
PUBLIC_KEY = 
"" +
        
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDzIcJfm7/DJW6CX7uluXnNEqEE" +
        
"MbB5/Hx5nyBL1qS3NSurosnCXzkeJx89oUTzKrsObrs/8lrw1JK3JXWm1iriPbat" +
        
"pL/Csb9TNhPvQxPskgiqrKwKf12TJbMvAz/n5DxAE0Eb+BFJB+Xg+sftAZTqW2Vb" +
        
"V3Qu78lnhkjzsJXYkQIDAQAB";
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 
117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 
128;
    @Test
    public void decodeTest() 
throws Exception {
        String data = 
"g08ZvPcrV+k8Fh1wJ8BRGDGJWOV6pm6aq6G+Q0vgfS4SQXAXx1DI9r489cs9krBu/2Okm1g4tvqzESWHW/" +
            
"HFqFdsgYAHluN/x6mOAx4WTYYw5XlWCYdEUhhfHeUhZi9WR0omKCrFgSfyNWx5OPy/35PSLuvl5df5E30ewejDM9+tV8" +
            
"pAr/lDQD30BE/XwH2Hvc1EdsZ54kncK2v7lOvmMNR3PmjVsryQNnuy4kqUY5yOqi7VJlf8atOheCUe+psrnJauDc32m/" +
            
"R99hE1UhD/R8j7Df9ZptbL/37GOpyeCyRrBpGaSjUDJ2YmiS3AnA2hoeDVV5eT72J8P5EPFYuDGQ==";
        PrivateKey privateKey = 
getPrivateKey(
PRIVATE_KEY)
;
        Cipher vDeCipher = Cipher.
getInstance(
CIPHER_ALGORITHM)
;
        vDeCipher.init(Cipher.
DECRYPT_MODE, privateKey)
;
        //私钥解密
        byte[] decrypted = 
autoFinal(Base64.
decodeBase64(data.getBytes())
, vDeCipher
, MAX_DECRYPT_BLOCK)
;
        System.
out.println(
"解密后:" + 
new String(decrypted
, "utf-8"))
;
    }
    
@Test
    public void baseTest() 
throws Exception {
        PrivateKey privateKey = 
getPrivateKey(
PRIVATE_KEY)
;
        PublicKey publicKey = 
getPublicKey(
PUBLIC_KEY)
;
        String data = 
"RSA加密明文最大长度117字节,解密要求密文最大长度为128字节,所以在加密和解密的过程中需要分块进行。";
        //--
        Cipher pEnCipher = Cipher.
getInstance(
CIPHER_ALGORITHM)
;//java默认"RSA"="RSA/ECB/PKCS1Padding"
        pEnCipher.init(Cipher.
ENCRYPT_MODE, publicKey)
;
        Cipher vDeCipher = Cipher.
getInstance(
CIPHER_ALGORITHM)
;
        vDeCipher.init(Cipher.
DECRYPT_MODE, privateKey)
;
        //--
        Cipher vEnCipher = Cipher.
getInstance(
CIPHER_ALGORITHM)
;
        vEnCipher.init(Cipher.
ENCRYPT_MODE, privateKey)
;
        Cipher pDeCipher = Cipher.
getInstance(
CIPHER_ALGORITHM)
;
        pDeCipher.init(Cipher.
DECRYPT_MODE, publicKey)
;
        //公钥加密
        byte[] encrypted = Base64.
encodeBase64(
autoFinal(data.getBytes(
"utf-8")
, pEnCipher
, MAX_ENCRYPT_BLOCK))
;
        System.
out.println(
"加密后:" + 
new String(encrypted))
;
        //私钥解密
        byte[] decrypted = 
autoFinal(Base64.
decodeBase64(encrypted)
, vDeCipher
, MAX_DECRYPT_BLOCK)
;
        System.
out.println(
"解密后:" + 
new String(decrypted
, "utf-8"))
;
        //私钥加密
        byte[] encrypted2 = Base64.
encodeBase64(
autoFinal(data.getBytes(
"utf-8")
, vEnCipher
, MAX_ENCRYPT_BLOCK))
;
        System.
out.println(
"加密后:" + 
new String(encrypted2))
;
        //公钥解密
        byte[] decrypted2 = 
autoFinal(Base64.
decodeBase64(encrypted2)
, pDeCipher
, MAX_DECRYPT_BLOCK)
;
        System.
out.println(
"解密后:" + 
new String(decrypted2
, "utf-8"))
;
    }
    
/**
     * 将base64编码后的公钥字符串转成PublicKey实例
     */
    private static PublicKey 
getPublicKey(String publicKey) 
throws Exception {
        
byte[] keyBytes = 
new Base64().decode(publicKey.getBytes())
;
        X509EncodedKeySpec keySpec = 
new X509EncodedKeySpec(keyBytes)
;
        KeyFactory keyFactory = KeyFactory.
getInstance(
KEY_ALGORITHM)
;
        return keyFactory.generatePublic(keySpec)
;
    }
    
/**
     * 将base64编码后的私钥字符串转成PrivateKey实例
     */
    private static PrivateKey 
getPrivateKey(String privateKey) 
throws Exception {
        
byte[] keyBytes = 
new Base64().decode(privateKey.getBytes())
;
        PKCS8EncodedKeySpec keySpec = 
new PKCS8EncodedKeySpec(keyBytes)
;
        KeyFactory keyFactory = KeyFactory.
getInstance(
KEY_ALGORITHM)
;
        return keyFactory.generatePrivate(keySpec)
;
    }
    
private static byte[] 
autoFinal(
byte[] data
, Cipher cipher
, int blockSize) 
throws Exception {
        
int inputLen = data.
length;
        byte[] rs
;
        try (ByteArrayOutputStream out = 
new ByteArrayOutputStream()) {
            
int offSet = 
0;
            byte[] cache
;
            int i = 
0;
            // 对数据分段加密
            while (inputLen - offSet > 
0) {
                
if (inputLen - offSet > blockSize) {
                    cache = cipher.doFinal(data
, offSet
, blockSize)
;
                } 
else {
                    cache = cipher.doFinal(data
, offSet
, inputLen - offSet)
;
                }
                out.write(cache
, 0, cache.
length)
;
                i++
;
                offSet = i * blockSize
;
            }
            rs = out.toByteArray()
;
        }
        
return rs
;
    }
}