Skip to content

使用openssl生成Java可以读取的密钥和自签名证书

密钥生成

私钥生成

shell
openssl genrsa -out rsa_private.pem 2048
  • genrsa:是openssl生成RSA私钥的标准命令
  • -out:t 指定输出的文件
  • 2048 指rsa算法长度,可以替换为其他长度,例如1024。

PKCS#8标准转换

需要注意的是openssl默认生成的是PKCS#1标准的密钥,存储格式是PEM。Java标准库默认是不支持PKCS#1标准,因此需要转换为PKCS#8标准。

shell
openssl pkcs8 -topk8 -inform PEM -in rsa_private.pem -outform PEM -nocrypt > rsa_private_pkcs8.pem
  • pkcs8:是openssl转换pcks8的标准命令
  • -topk8:通常,输入时需要 PKCS#8 私钥,并且将写入传统格式的私钥。使用 -topk8 选项,情况正好相反:它读取传统格式的私钥并写入 PKCS#8 格式的密钥。
  • -inform:输入key的格式,可以是PEM,也可以是DER
  • -in:输入文件
  • -outform:输出格式,可以是PEM,也可以是DER
  • -nocrypt:对密钥不加密 PEM和DER是密钥的两种编码方式,PEM格式是以-----BEGIN PRIVATE KEY-----或-----BEGIN PUBLIC KEY-----开头,以-----END PRIVATE KEY-----或者-----END PUBLIC KEY-----结尾,中间是经过Base64编码后的内容,每64字节做一次换行。 DER格式是直接密钥的二进制编码。 如果需要从PKCS#8转换为PKCS#1格式可以使用如下命令
shell
openssl rsa -in rsa_private_pkcs8.pem -out rsa_private.pem

导出公钥

shell
-- 导出pem格式
openssl rsa -in rsa_private.pem -pubout -outform PEM -out  rsa_public.pem
-- 导出DER格式
openssl rsa -in rsa_private.pem -pubout -outform DER -out  rsa_public.der

标准转换方法与私钥转换一致

自签名证书生成

shell
# 使用自有私钥生成证书
openssl req -new -x509 -days 365 -key rsa_private.key -out cert.crt
  • -new:是生成证书请求
  • -x509: 生成证书格式
  • -key:指定私钥,这里pkcs#1或者PKCS#8都是可以的
  • -out:输出证书文件

Java读取密钥文件

读取PEM格式私钥

Java
public static PrivateKey readPemPrivateKey(String filename) throws Exception {
        // 读取文件
        String key = new String(Files.readAllBytes(Paths.get(filename)), Charset.defaultCharset());
        String privateKeyPEM = key
            .replace("-----BEGIN PRIVATE KEY-----", "")
            .replaceAll("\n", "")
            .replace("-----END PRIVATE KEY-----", "");
        byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
        PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(encoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(pkcs8);
    }

读取PEM格式公钥

Java
public static PublicKey readPemPublicKey(String filename) throws Exception {
    String key = new String(Files.readAllBytes(Paths.get(filename)), Charset.defaultCharset());

    String publicKeyPEM = key
        .replace("-----BEGIN PUBLIC KEY-----", "")
        .replaceAll(System.lineSeparator(), "")
        .replace("-----END PUBLIC KEY-----", "");

    byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
    return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}

读取DER格式私钥

Java
public static PrivateKey readDerPrivateKey(String filename) throws Exception {
    // 读取文件
    byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
    PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PrivateKey privateKey = kf.generatePrivate(pkcs8);
    // 转换编码
    String data = Base64.getEncoder().encodeToString(privateKey.getEncoded());
    System.out.println(data);
    return privateKey;
}

读取DER格式公钥

Java
public static PrivateKey readDerPrivateKey(String filename) throws Exception {
    // 读取文件
    byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
    PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PrivateKey privateKey = kf.generatePrivate(pkcs8);
    // 转换编码
    String data = Base64.getEncoder().encodeToString(privateKey.getEncoded());
    System.out.println(data);
    return privateKey;
}

Java读取X509证书

Java
public static X509Certificate loadCert(String filename) throws Exception {
    FileInputStream in = new FileInputStream(filename);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    X509Certificate validateCert = (X509Certificate) cf.generateCertificate(in);
    return validateCert;
}

使用加载的密钥完成加密和解密

Java
public static void doEncryptAndDecrypt(PublicKey publicKey, PrivateKey privateKey, String msg) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    String out = Base64.getEncoder().encodeToString(cipher.doFinal(msg.getBytes(StandardCharsets.UTF_8)));
    // 解密
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    out = new String(cipher.doFinal(Base64.getDecoder().decode(out)));
    if (msg.equals(out)) {
        System.out.println("加密、解密OK");
    }
}

https://alvinkwok.cn/2021/06/11/security/使用openssl生成Java可以读取的密钥和自签名证书/