Java web

Java 实现 RSA 加密

2020-04-12  本文已影响0人  又语

本文介绍 RSA 加密的原理、示例、安全性分析及 Java 语言实现。


目录


RSA 简介

RSA 是一种非对称加密算法,于1977年由 MIT 的 Ron Rivest,Adi Shamir 和 Leonard Adleman 三人一起提出。

原理及示例
  1. 选择 2 个质数 pq
  2. 计算 n = p * q
  3. 根据欧拉函数 φ(n) = (p - 1) * (q - 1) 计算出 φ(n)
  4. 确定公钥(整数)e,要求:1 < e < φ(n)eφ(n) 互质
  5. 确定私钥(整数)d,要求:(e * d) / φ(n) 的余数为 1
  6. 加密:原文 m,计算 me 次幂除以 n,求余数 cc 就是加密后所得的密文
  7. 解密:密文 c,计算 cd 次幂除以 n,求余数得到原文 m
示例

来自 wikipedia

  1. p = 61q = 53
  2. n = 61 * 53 = 3233
  3. φ(n) = (61 - 1) * (53 - 1) = 60 * 52 = 3120
  4. e = 17
  5. d = 2753
  6. 公钥 (3233,17),私钥 (3233,2753)
  7. 原文 18,公钥加密密文 2100,私钥解密得到原文 18
  8. 原文 81,私钥加密密文 2083,公钥解密得到原文 81
安全性
  1. 加解需要 n 和公钥 e 生成密文 c
  2. 解密需要 n 、密钥 d 和密文 c
  3. 公开场合窃听者只能获取 n e c,但是获取不到密钥 d,需要通过 e 计算出 d
  4. 如果想通过 e 计算出 d 则必须知道 φ(n)
  5. 想知道 φ(n) 必须求出 pq
  6. 因为 n = p * qn 已知,所以必须进行【质因数分解】,数学证明大数质因数分解十分困难,这也是 RSA 算法安全性的根本保证。

美国国家标准与技术局和 ANSI X9 已经设定了最小密钥长度的要求,RSA 是 2048 位,这样在 2030 年以前是安全的。

常见用途

代码实现

package tutorial.java.util;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.util.Base64;

public class RsaUtils {

    /**
     * 算法名称
     */
    private static final String ALGORITHM_RSA = "RSA";

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

    /**
     * 生成密钥对
     *
     * @param keySize 密钥长度
     * @return 密钥对
     */
    public static KeyPair keyPair(int keySize) throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_RSA);
        keyPairGenerator.initialize(keySize);
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * RSA 私钥加密
     *
     * @param privateKey 私钥
     * @param content    待加密内容
     * @return 加密后密文字节数组
     */
    public static byte[] encryptByPrivateKey(PrivateKey privateKey, byte[] content) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return cipher.doFinal(content);
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No Such Algorithm");
        } catch (NoSuchPaddingException e) {
            throw new UnsupportedOperationException("No Such Padding");
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (BadPaddingException e) {
            throw new UnsupportedOperationException("Bad Padding");
        } catch (IllegalBlockSizeException e) {
            throw new UnsupportedOperationException("Illegal Block Size");
        }
    }

    /**
     * RSA 公钥加密
     *
     * @param publicKey 公钥
     * @param content   待加密内容
     * @return 加密后密文字节数组
     */
    public static byte[] encryptByPublicKey(PublicKey publicKey, byte[] content) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(content);
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No Such Algorithm");
        } catch (NoSuchPaddingException e) {
            throw new UnsupportedOperationException("No Such Padding");
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (BadPaddingException e) {
            throw new UnsupportedOperationException("Bad Padding");
        } catch (IllegalBlockSizeException e) {
            throw new UnsupportedOperationException("Illegal Block Size");
        }
    }

    /**
     * RSA 私钥解密
     *
     * @param privateKey 私钥
     * @param content    密文
     * @return 解密后原文字节数组
     */
    public static byte[] decryptByPrivateKey(PrivateKey privateKey, byte[] content) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(content);
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No Such Algorithm");
        } catch (NoSuchPaddingException e) {
            throw new UnsupportedOperationException("No Such Padding");
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (BadPaddingException e) {
            throw new UnsupportedOperationException("Bad Padding");
        } catch (IllegalBlockSizeException e) {
            throw new UnsupportedOperationException("Illegal Block Size");
        }
    }

    /**
     * RSA 公钥解密
     *
     * @param publicKey 公钥
     * @param content   密文
     * @return 解密后原文字节数组
     */
    public static byte[] decryptByPublicKey(PublicKey publicKey, byte[] content) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return cipher.doFinal(content);
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No Such Algorithm");
        } catch (NoSuchPaddingException e) {
            throw new UnsupportedOperationException("No Such Padding");
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        } catch (BadPaddingException e) {
            throw new UnsupportedOperationException("Bad Padding");
        } catch (IllegalBlockSizeException e) {
            throw new UnsupportedOperationException("Illegal Block Size");
        }
    }

    /**
     * 用私钥生成数字签名
     *
     * @param content    签名内容
     * @param privateKey 私钥
     * @return 数字签名
     */
    public static String sign(byte[] content, PrivateKey privateKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(privateKey);
            signature.update(content);
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No Such Algorithm");
        } catch (SignatureException e) {
            throw new UnsupportedOperationException("Signature Exception");
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        }
    }

    /**
     * 用公钥验证数字签名
     *
     * @param content   签名内容
     * @param publicKey 公钥
     * @param sign      签名
     * @return 验证结果
     */
    public static boolean verify(byte[] content, PublicKey publicKey, String sign) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(publicKey);
            signature.update(content);
            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("No Such Algorithm");
        } catch (SignatureException e) {
            throw new UnsupportedOperationException("Signature Exception");
        } catch (InvalidKeyException e) {
            throw new UnsupportedOperationException("Invalid Key");
        }
    }
}

单元测试:

package tutorial.java.util;

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

import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;

public class RsaUtilsTest {

    @Test
    public void test() throws NoSuchAlgorithmException {
        String content = "RSA encrypt/decrypt demo";
        KeyPair keyPair = RsaUtils.keyPair(2048);
        // 测试私钥加密公钥解密
        byte[] encryptByPrivateKeyResult = RsaUtils.encryptByPrivateKey(keyPair.getPrivate(),
                content.getBytes(StandardCharsets.UTF_8));
        byte[] decryptByPublicKeyResult = RsaUtils.decryptByPublicKey(keyPair.getPublic(),
                encryptByPrivateKeyResult);
        Assert.assertEquals(content, new String(decryptByPublicKeyResult, StandardCharsets.UTF_8));
        // 测试公钥加密私钥解密
        byte[] encryptByPublicKeyResult = RsaUtils.encryptByPublicKey(keyPair.getPublic(),
                content.getBytes(StandardCharsets.UTF_8));
        byte[] decryptByPrivateKeyResult = RsaUtils.decryptByPrivateKey(keyPair.getPrivate(),
                encryptByPublicKeyResult);
        Assert.assertEquals(content, new String(decryptByPrivateKeyResult, StandardCharsets.UTF_8));
        // 测试签名
        String sign = RsaUtils.sign(content.getBytes(StandardCharsets.UTF_8), keyPair.getPrivate());
        Assert.assertTrue(RsaUtils.verify(content.getBytes(StandardCharsets.UTF_8), keyPair.getPublic(), sign));
    }
}
上一篇 下一篇

猜你喜欢

热点阅读