java - 从 ASN1 加密的 pem 证书中获取公钥和私钥

标签 java security ssl encryption asn.1

我正在尝试使用我将从 pem 证书获得的私钥对特定字符串进行签名。此证书使用密码短语加密。证书格式(.pem 文件)是这样的:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,A60C80692F0FEB16

Fq/awhS....
+..
+..
+..
+..
+....detSug=
-----END RSA PRIVATE KEY-----

我编写的用于从此证书中获取公钥和私钥的代码是:

import java.io.FileReader;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;

import org.bouncycastle.asn1.ASN1Generator;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;


import sun.misc.BASE64Encoder;

class MainClass{
    public static void main(String args[]){
        try{
        PrivateKey pk = readPrivateKey("C:/Input/CERT.pem","passphrase");
        PublicKey pubk = readPublicKey("C:/Input/CERT.pem","passphrase");
        byte[] data = "ABCEFG20150520163306".getBytes("UTF8");

        Signature sig = Signature.getInstance("SHA1WithRSA");
        sig.initSign(pk);
        sig.update(data);
        byte[] signatureBytes = sig.sign();
        System.out.println("Singature:" + new BASE64Encoder().encode(signatureBytes));

        sig.initVerify(pubk);
        sig.update(data);
        System.out.println(sig.verify(signatureBytes));

        }catch(Exception e){

            e.printStackTrace();
        }

    }


    private static PrivateKey readPrivateKey(String privateKeyPath, String keyPassword) throws IOException {

        FileReader fileReader = new FileReader(privateKeyPath);

        PEMParser keyReader = new PEMParser(fileReader);

        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        PEMDecryptorProvider decryptionProv = new JcePEMDecryptorProviderBuilder().build(keyPassword.toCharArray());
        System.out.println(keyReader.getClass());
        Object keyPair = keyReader.readObject();
        PrivateKeyInfo keyInfo;
        System.out.println(keyPair.getClass());


        if (keyPair instanceof PrivateKeyInfo) {
            System.out.println("Correct instance found");

            PEMKeyPair decryptedKeyPair = ((PEMEncryptedKeyPair) keyPair).decryptKeyPair(decryptionProv);
            keyInfo = decryptedKeyPair.getPrivateKeyInfo();
        } else {
            keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo();
        }

        keyReader.close();

        return converter.getPrivateKey(keyInfo);
    }


    private static PublicKey readPublicKey(String privateKeyPath, String keyPassword) throws IOException {

        FileReader fileReader = new FileReader(privateKeyPath);
        PEMParser keyReader = new PEMParser(fileReader);

        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        PEMDecryptorProvider decryptionProv = new JcePEMDecryptorProviderBuilder().build(keyPassword.toCharArray());

        Object keyPair = keyReader.readObject();
        SubjectPublicKeyInfo keyInfo;

        if (keyPair instanceof PEMEncryptedKeyPair) {
            PEMKeyPair decryptedKeyPair = ((PEMEncryptedKeyPair) keyPair).decryptKeyPair(decryptionProv);
            keyInfo = decryptedKeyPair.getPublicKeyInfo();
        } else {
            keyInfo = ((PEMKeyPair) keyPair).getPublicKeyInfo();
        }
        keyReader.close();
        return converter.getPublicKey(keyInfo);
    }
}

这段代码工作正常,但现在我必须为看起来像这样的 ASN1 证书做同样的事情

Bag Attributes
localKeyID: 01 00 00 00 
friendlyName: le-d5391255-2e94-48fe-8327-caca5d9aa498<Microsoft CSP Name: Microsoft Enhanced Cryptographic Provider v1.0
Key Attributes
X509v3 Key Usage: 10 
-----BEGIN PRIVATE KEY-----
MIIEvwIBA....
+..
+..
+..
+..
+....N5kNrDV0Yg==
-----END PRIVATE KEY-----

Bag Attributes
localKeyID: 00 01 00 00 
1.3.6.1.4.1.322.17.3.92: 00 08 00 00 
1.3.6.1.4.1.311.17.3.20: 15 0D 78 6A D0 18 CB A2 D1 7F D1 C2 B2 7A E0 53 70 D7 ED F9 
1.3.6.1.4.1.313.17.3.79: 46 00 61 00 9E 00 65 00 73 00 73 00 61 00 2D 00 50 00 43 00 00 00 
subject=/C=SG/O=Netrust Certificate Authority 1/OU=Netrust CA1 (Server)/OU=ABC-XYZ Private Limited/CN=QICERT
issuer=/C=SG/O=Netrust Certificate Authority 1/OU=Netrust CA
-----BEGIN CERTIFICATE-----
MIIE5DCCA8yg....
+..
+..
+..
+..
+....Y7LF
Byuyq1Pe4QY=
-----END CERTIFICATE-----

Bag Attributes
1.3.6.1.4.1.311.17.3.92: 00 08 00 00 
1.3.1.1.4.1.221.17.3.20: 1D 44 89 B2 45 26 7F 3F 6B 92 C5 3A 7B 72 63 CA D2 70 2A DD 
subject=/C=SG/O=Netrust Certificate Authority 1/OU=Netrust CA1
issuer=/C=SG/O=Netrust Certificate Authority 1/OU=Netrust CA1
-----BEGIN CERTIFICATE-----
MIIESTCCAzGgAwI....
+..
+..
+..
+..
+....Y7LF
iY44mB2Sev4/02GkW7
-----END CERTIFICATE-----

当我为这个新证书运行这段代码时,出现以下错误:

java.lang.ClassCastException: org.bouncycastle.asn1.pkcs.PrivateKeyInfo cannot be cast to org.bouncycastle.openssl.PEMEncryptedKeyPair
    at MainClass.readPrivateKey(MainClass.java:92)
    at MainClass.main(MainClass.java:47)

根据此错误消息,我正在尝试更改我的代码以获取 ASN1 类型的私钥-公钥对,但我无法这样做。

我正在使用 thisthis作为引用。

谢谢!

最佳答案

经过同事/batch-mates的大量帮助,我终于能够解决这个问题。这就是我所做的:
首先,我使用以下命令将 pem 证书转换为 der 证书

openssl pkcs8 -topk8 -inform PEM -outform DER -passin pass:passphrase -in "C:\Input\CERT.pem" -out "C:\Input\CERT.der"-nocrypt

然后我使用以下类根据上面收到的证书对给定字符串进行签名和编码:

import java.io.*;
import java.security.*;
import java.security.spec.*;

import com.sun.org.apache.xml.internal.security.utils.Base64;

class MainClass{
    public static void main(String args[]){
        try {
            MessageDigest cript = MessageDigest.getInstance("SHA-1");
            cript.reset();
                    cript.update(args[0].getBytes("UTF-8"));
                    byte[] b_digest = cript.digest();

            Signature sign = Signature.getInstance("SHA1withRSA");
            PrivateKey pk = get("C:/Input/CERT.der");
            sign.initSign(pk);
            sign.update(b_digest);
            byte[] b1 = sign.sign();

            String signedString = new String(b1);
            String s2 = new String(Base64.encode(b1));

            System.out.println("_______________PrivateKey_________________________");
            System.out.println(pk.toString() +"|");
            System.out.println("_______________Digest_________________________");
            String temp = new String(b_digest);
            System.out.println(temp + "|");
            System.out.println("_______________Signature_________________________");
            System.out.println(signedString +"|");
            System.out.println("_______________Encoded_________________________");
            System.out.println(s2 +"|");

            }
        catch (Exception ex) {
            System.out.println(ex.getMessage());
            ex.printStackTrace();
            }
        }

    public static PrivateKey get(String filename) throws Exception {
        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        byte[] keyBytes = new byte[(int)f.length()];
        dis.readFully(keyBytes);
        dis.close();

        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(spec);
    }
}

这个类所做的就是将要签名的字符串作为输入表单命令行,并使用作为输入提供的证书对其进行签名。

希望所有遇到与我类似情况的人都能从中得到一些帮助。 谢谢!

关于java - 从 ASN1 加密的 pem 证书中获取公钥和私钥,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30392114/

相关文章:

单击时Javafx2修改按钮

ios - iOS 设备越狱后是否安全?

php - 替代SSL- "Manual"加密吗?

node.js - 使用 nginx 代理没有证书的 HTTPS

ssl - 禁用 SSLv2 和 SSLv3 是否对最终用户有任何重大更改?

java - 如何在另一个方法中使用返回的 ArrayList

java - Android编程(尝试下载文件到系统目录)

java - 如何使用 java.security.KeyStore 类存储和加载 key

http - TLS 和网络中介

java - 将 Java 与 Matlab 一起使用 : passing Class type as a method argument