Получите открытый и закрытый ключ из зашифрованного сертификата pem ASN1

Я пытаюсь подписать конкретную строку закрытым ключом, который я получу из сертификата 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, но я не могу это сделать.

Я использую это и это в качестве справки.

Спасибо!


person harshul gandhi    schedule 22.05.2015    source источник
comment
Для подписи вам не нужен открытый ключ.   -  person user207421    schedule 02.06.2015
comment
да, вы правы, подпись выполняется только с закрытым ключом, но проверка может производиться с открытым ключом!   -  person harshul gandhi    schedule 02.06.2015


Ответы (1)


После большой помощи коллег / товарищей по партии я, наконец, смог решить эту проблему. Вот что я сделал:
Сначала я преобразовал сертификат 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);
    }
}

Все, что делает этот класс, - это принимает строку, которую нужно подписать, как командную строку формы ввода и подписывает ее, используя сертификат, предоставленный в качестве ввода.

Надеюсь, что все, кто столкнется с чем-то похожим на то, что сделал я, смогут извлечь из этого некоторую помощь. Спасибо!

person harshul gandhi    schedule 02.06.2015