RSA非对称加密原理

1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。

RSA非对称加密原理

RSA非堆成加密算法利用了大数求模运算实现。这里只讲加解密原理,不讲具体的推到证明。若有需要请转到阮一峰的网络日志
.

密钥的生成步骤

  1. 随机选择两个不相等的质数p和q。(一般情况下这两个质数都是非常大的。实际应用中,这两个质数越大,就越难破解。)
  2. 计算p和q的乘积n。($φ(n) = (p-1)(q-1)$)
  3. 计算n的欧拉函数φ(n)。
  4. 随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质。
  5. 计算e对于φ(n)的模反元素d。($ed ≡ 1 (mod φ(n))$ 等价于 $ed - 1 = kφ(n)$)
  6. 将n和e封装成公钥,n和d封装成私钥。

RSA算法的可靠性

上述步骤出现的六个数字中,公钥用到了两个(n和e),其余四个数字都是不公开的。其中最关键的是d,因为n和d组成了私钥,一旦d泄漏,就等于私钥泄漏。

加密与解密

  • 加密要用公钥 (n,e),公式为:$me ≡ c (mod n)$
  • 解密要用私钥(n,d),公式为:$cd ≡ m (mod n)$

Java生成RSA密钥对

  • 生成KeySize为2048bit的RSA密钥对
    String KEY_ALGORITHM = "RSA";
    //获得对象 KeyPairGenerator 参数 RSA 2048个字节
    KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
    keyPairGen.initialize(2048);
    //通过对象 KeyPairGenerator 获取对象KeyPair
    KeyPair pair = keyPairGen.generateKeyPair();
    //通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey
    PrivateKey privateKey2048 = pair.getPrivate();
    PublicKey publicKey2048 = pair.getPublic();
  • 写入到文件(Hutool工具)
    PemUtil.writePemObject("RSA PRIVATE KEY", privateKey2048.getEncoded(), FileUtil.getOutputStream("private_key.pem"));
    PemUtil.writePemObject("RSA PUBLIC KEY", publicKey2048.getEncoded(), FileUtil.getOutputStream("public_key.pem"));

发行证书

CA证书(根证书)的特点就是自签发:

  1. 颁发者信息与使用者信息一致
  2. 基本约束带有CA标识
  3. 使用证书含有公钥对应的私钥对自己进行证书签发
/**
 *  根据如下参数获取对应base64编码格式的证书文件字符串
 *      issuerName 与 reqName 对象是同一个则认为生成的是CA证书
 * @param issuerName 颁发者信息
 * @param reqName   请求证主题信息
 *                  <br> issuerName == reqName ---> CA
 * @param serial 证书序列号
 *                 <br>eg: BigInteger serial = BigInteger.valueOf(System.currentTimeMillis() / 1000);
 * @param notBefore 有效期开始时间  2018-08-01 00:00:00
 * @param notAfter 有效期截至时间   2028-08-01 00:00:00
 * @param userPublicKey 请求者主题公钥信息
 * @param rootPrivateKey   颁发者私钥信息
 * @return String
 * @throws OperatorCreationException
 * @throws CertificateException
 * @throws IOException
 */
public static String certBuilder(X500Name issuerName, X500Name reqName, BigInteger serial, Date notBefore, Date notAfter, PublicKey userPublicKey, PrivateKey rootPrivateKey) throws OperatorCreationException, CertificateException, IOException {
  JcaX509v3CertificateBuilder x509v3CertificateBuilder = new JcaX509v3CertificateBuilder(
          issuerName, serial, notBefore, notAfter, reqName, userPublicKey);
  // 签发者 与 使用者 信息一致则是CA证书生成,增加CA 基本约束属性
  if(issuerName == reqName){
      BasicConstraints constraint = new BasicConstraints(1);
      x509v3CertificateBuilder.addExtension(Extension.basicConstraints, false, constraint);
  }
  //签名的工具
  ContentSigner signer = new JcaContentSignerBuilder("SHA256WITHRSA").setProvider("BC").build(rootPrivateKey);
  //触发签名产生用户证书
  X509CertificateHolder x509CertificateHolder = x509v3CertificateBuilder.build(signer);
  JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
  certificateConverter.setProvider("BC");
  Certificate userCertificate = certificateConverter.getCertificate(x509CertificateHolder);
  String certStr = genCert(userCertificate);
  return certStr;
}
/**
 * 签发CA证书
 */
public void genCaCertTest() throws Exception{
  //根证书Issue基本信息
  X500Name issuerName = getX500Name("Dev", "VK", "BeiJing", "BeiJing", "CN", "R&D");
  // 证书序列号
  BigInteger serial = BigInteger.valueOf(System.currentTimeMillis() / 1000);
  //证书有 起始日期 与 结束日期
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  Date notBefore = sdf.parse("2018-08-01 00:00:00");
  Date notAfter = sdf.parse("2028-08-01 00:00:00");
  
  //构建 用户证书 对应的公钥
  PublicKey userPublicKey = getPublicKey(16,publicRootMudulus,publicRootexpoent);
  //构建CAroot证书 对应的私钥
  PrivateKey rootPrivateKey = getPrivateKey(16,publicRootMudulus,publicRootexpoent);
  //构建证书的build
  String cert = certBuilder(issuerName, issuerName, serial, notBefore, notAfter, userPublicKey, rootPrivateKey);
  System.out.println("\n"+cert);
}

签发用户证书的特点:

  1. 这里要用用户证书的公钥以及请求信息,使用CA证书的私钥做签名达到证书结果。
  2. 签发证书的颁发者信息 要与生成ca证书时的签发信息一致,不然会出错。证书链验证不过。
/**
 * 生成用户证书
 */
public void genUserCertTest() throws Exception{
   //根证书Issue基本信息
   X500Name issuerName = getX500Name("Dev", "Vk", "BeiJing", "BeiJing", "CN", "R&D");
   // 用户证书 基本使用者
   X500Name reqName = getX500Name("vkuser", "vkuser", "BeiJing", "BeiJing", "CN", "R&D");
   // 证书序列号
   BigInteger serial = BigInteger.valueOf(System.currentTimeMillis() / 1000);
   //证书 起始日期 与 结束日期
   SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   Date notBefore = sdf.parse("2018-08-02 00:00:00");
   Date notAfter = sdf.parse("2028-07-01 00:00:00");

   //构建 用户证书 对应的公钥
   PublicKey userPublicKey = getPublicKey(16,publicUserMudulus,publicUserexpoent);
   //构建CAroot证书 对应的私钥
   PrivateKey rootPrivateKey = getPrivateKey(16,publicRootMudulus,privateRootexpoent);
   //构建证书的build
   String cert = certBuilder(issuerName, reqName, serial, notBefore, notAfter, userPublicKey, rootPrivateKey);
   System.out.println("\n"+cert);
}

RSA非对称加密原理
https://blog.cikaros.top/doc/a3f58b37.html
作者
Cikaros
发布于
2022年9月4日
许可协议