web-dev-qa-db-fra.com

Signez la RSE à l'aide du château gonflable

Je ne trouve aucun code/doc décrivant comment signer un CSR en utilisant BC. En entrée, j'ai un CSR comme un tableau d'octets et je voudrais obtenir le certificat au format PEM et/ou DER.

Je suis arrivé jusqu'ici

def signCSR(csrData:Array[Byte], ca:CACertificate, caPassword:String) = {
  val csr = new PKCS10CertificationRequestHolder(csrData)
  val spi = csr.getSubjectPublicKeyInfo

  val ks = new Java.security.spec.X509EncodedKeySpec(spi.getDEREncoded())
  val kf = Java.security.KeyFactory.getInstance("RSA")
  val pk = kf.generatePublic(ks)

  val (caCert, caPriv) = parsePKCS12(ca.pkcs12data, caPassword)

  val fromDate : Java.util.Date = new Java.util.Date // FixMe
  val toDate = fromDate // FixMe
  val issuer = PrincipalUtil.getIssuerX509Principal(caCert)
  val contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(caPriv)
  val serial = BigInt(CertSerialnumber.nextSerialNumber)
  val certgen = new JcaX509v3CertificateBuilder(new X500Name(issuer.getName), serial.bigInteger, fromDate, toDate, csr.getSubject, pk)

J'ai du mal à trouver un générateur de certificats pour le stocker au format PEM ou DER.

Ou est-ce que je suis sur le mauvais chemin tous ensemble?

32
Fredrik Jansson

Ok ... Je cherchais à faire la même chose et pour la vie de moi, je ne pouvais pas comprendre comment. Les API parlent toutes de la génération des paires de clés, puis de la génération du certificat, mais pas de la façon de signer un CSR. D'une certaine manière, par hasard - voici ce que j'ai trouvé.

Étant donné que PKCS10 représente le format de la demande (de la CSR), vous devez d'abord placer votre CSR dans un PKCS10Holder. Ensuite, vous le transmettez à un CertificateBuilder (puisque CertificateGenerator est obsolète). La façon dont vous le passez est d'appeler getSubject sur le support.

Voici le code (Java, veuillez l'adapter selon vos besoins):

public static X509Certificate sign(PKCS10CertificationRequest inputCSR, PrivateKey caPrivate, KeyPair pair)
        throws InvalidKeyException, NoSuchAlgorithmException,
        NoSuchProviderException, SignatureException, IOException,
        OperatorCreationException, CertificateException {   

    AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
            .find("SHA1withRSA");
    AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()
            .find(sigAlgId);

    AsymmetricKeyParameter foo = PrivateKeyFactory.createKey(caPrivate
            .getEncoded());
    SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pair
            .getPublic().getEncoded());

    PKCS10CertificationRequestHolder pk10Holder = new PKCS10CertificationRequestHolder(inputCSR);
    //in newer version of BC such as 1.51, this is 
    //PKCS10CertificationRequest pk10Holder = new PKCS10CertificationRequest(inputCSR);

    X509v3CertificateBuilder myCertificateGenerator = new X509v3CertificateBuilder(
            new X500Name("CN=issuer"), new BigInteger("1"), new Date(
                    System.currentTimeMillis()), new Date(
                    System.currentTimeMillis() + 30 * 365 * 24 * 60 * 60
                            * 1000), pk10Holder.getSubject(), keyInfo);

    ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
            .build(foo);        

    X509CertificateHolder holder = myCertificateGenerator.build(sigGen);
    X509CertificateStructure eeX509CertificateStructure = holder.toASN1Structure(); 
    //in newer version of BC such as 1.51, this is 
    //org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure = holder.toASN1Structure(); 

    CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

    // Read Certificate
    InputStream is1 = new ByteArrayInputStream(eeX509CertificateStructure.getEncoded());
    X509Certificate theCert = (X509Certificate) cf.generateCertificate(is1);
    is1.close();
    return theCert;
    //return null;
}

Comme vous pouvez le voir, j'ai généré la demande en dehors de cette méthode, mais je l'ai transmise. Ensuite, j'ai le PKCS10CertificationRequestHolder pour l'accepter comme argument de constructeur.

Ensuite, dans les arguments X509v3CertificateBuilder, vous verrez le pk10Holder.getSubject - c'est apparemment tout ce dont vous avez besoin? S'il manque quelque chose, faites-le moi savoir !!! Ça a marché pour moi. Le certificat que j'ai généré correctement contenait les informations DN dont j'avais besoin.

Wikipedia a une section de tueur sur PKCS - http://en.wikipedia.org/wiki/PKCS

33
Archie

Le code suivant est basé sur les réponses ci-dessus mais sera compilé et, étant donné un CSR encodé PEM (du type exporté par keytool), retournera un objet signéData codé PEM valide contenant une chaîne de certificats signée (du type qui peut être importé par keytool).

Oh et c'est contre BouncyCastle 1.49.

import Java.security.*;
import Java.io.*;
import Java.util.Date;
import Java.math.BigInteger;
import Java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.asn1.x500.*;
import org.bouncycastle.asn1.pkcs.*;
import org.bouncycastle.openssl.*;
import org.bouncycastle.pkcs.*;
import org.bouncycastle.cert.*;
import org.bouncycastle.cms.*;
import org.bouncycastle.cms.jcajce.*;
import org.bouncycastle.crypto.util.*;
import org.bouncycastle.operator.*;
import org.bouncycastle.operator.bc.*;
import org.bouncycastle.operator.jcajce.*;
import org.bouncycastle.util.encoders.Base64;

/**
 * Given a Keystore containing a private key and certificate and a Reader containing a PEM-encoded
 * Certificiate Signing Request (CSR), sign the CSR with that private key and return the signed
 * certificate as a PEM-encoded PKCS#7 signedData object. The returned value can be written to a file
 * and imported into a Java KeyStore with "keytool -import -trustcacerts -alias subjectalias -file file.pem"
 *
 * @param pemcsr a Reader from which will be read a PEM-encoded CSR (begins "-----BEGIN NEW CERTIFICATE REQUEST-----")
 * @param validity the number of days to sign the Certificate for
 * @param keystore the KeyStore containing the CA signing key
 * @param alias the alias of the CA signing key in the KeyStore
 * @param password the password of the CA signing key in the KeyStore
 *
 * @return a String containing the PEM-encoded signed Certificate (begins "-----BEGIN PKCS #7 SIGNED DATA-----")
 */
public static String signCSR(Reader pemcsr, int validity, KeyStore keystore, String alias, char[] password) throws Exception {
    PrivateKey cakey = (PrivateKey)keystore.getKey(alias, password);
    X509Certificate cacert = (X509Certificate)keystore.getCertificate(alias);
    PEMReader reader = new PEMReader(pemcsr);
    PKCS10CertificationRequest csr = new PKCS10CertificationRequest((CertificationRequest)reader.readObject());

    AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
    AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
    X500Name issuer = new X500Name(cacert.getSubjectX500Principal().getName());
    BigInteger serial = new BigInteger(32, new SecureRandom());
    Date from = new Date();
    Date to = new Date(System.currentTimeMillis() + (validity * 86400000L));

    X509v3CertificateBuilder certgen = new X509v3CertificateBuilder(issuer, serial, from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo());
    certgen.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
    certgen.addExtension(X509Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(csr.getSubjectPublicKeyInfo()));
    certgen.addExtension(X509Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifier(new GeneralNames(new GeneralName(new X509Name(cacert.getSubjectX500Principal().getName()))), cacert.getSerialNumber()));

    ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(PrivateKeyFactory.createKey(cakey.getEncoded()));
    X509CertificateHolder holder = certgen.build(signer);
    byte[] certencoded = holder.toASN1Structure().getEncoded();

    CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
    signer = new JcaContentSignerBuilder("SHA1withRSA").build(cakey);
    generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer, cacert));
    generator.addCertificate(new X509CertificateHolder(certencoded));
    generator.addCertificate(new X509CertificateHolder(cacert.getEncoded()));
    CMSTypedData content = new CMSProcessableByteArray(certencoded);
    CMSSignedData signeddata = generator.generate(content, true);

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    out.write("-----BEGIN PKCS #7 SIGNED DATA-----\n".getBytes("ISO-8859-1"));
    out.write(Base64.encode(signeddata.getEncoded()));
    out.write("\n-----END PKCS #7 SIGNED DATA-----\n".getBytes("ISO-8859-1"));
    out.close();
    return new String(out.toByteArray(), "ISO-8859-1");
}
8
Mike B

Merci Archie!

J'ai apporté quelques modifications à votre code, voir ci-dessous.

Les principales modifications consistent à transmettre le nom de l'émetteur et à utiliser la clé publique du CSR.

val caCert = PEMToCert(issuerPEM).get
val issuer = PrincipalUtil.getIssuerX509Principal(caCert)
val csr = new PKCS10CertificationRequestHolder(csrData)
val serial = BigInt(CertSerialNumber.nextSerialNumber)
val spi = csr.getSubjectPublicKeyInfo();

val certgen = new X509v3CertificateBuilder(
    new X500Name(issuer.getName),
    serial.bigInteger,
    new Java.util.Date(),
    new Date(System.currentTimeMillis() + 30 * 365 * 24 * 60 * 60 * 1000),
    csr.getSubject,
    csr.getSubjectPublicKeyInfo())

certgen.addExtension(
    X509Extension.subjectKeyIdentifier,
    false,
    spi
)

val issuerPK = PEMToPK(issuerPKPEM, caPassword).get
val contentSigner = new JcaContentSignerBuilder(contentSignerAlg).setProvider(BC).build(issuerPK.getPrivate())
val x509 = (new JcaX509CertificateConverter).setProvider(BC).getCertificate(certgen.build(contentSigner))
5
Fredrik Jansson

Au final, c'est ce qui a fonctionné pour moi:

KeyPair serverKeyPair = keyPairLoader.getKeyPair(); //my own class
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate serverCertificate = getServerCertificate(certificateFactory);

org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure = signCertificateSigningRequest(
  jcaPKCS10CertificationRequest, keyPair, serverCertificate);

Java.security.cert.X509Certificate signedCertificate = readCertificateFromASN1Certificate(
  eeX509CertificateStructure, certificateFactory);

Où est le code

  private org.spongycastle.asn1.x509.Certificate signCertificateSigningRequest(
    JcaPKCS10CertificationRequest jcaPKCS10CertificationRequest,
    KeyPair keyPair, X509Certificate serverCertificate)
      throws IOException, OperatorCreationException, NoSuchAlgorithmException, InvalidKeyException
  {
    // Signing CSR
    AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
      .find("SHA1withRSA");

    X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
        serverCertificate, 
        new BigInteger("1"), //serial
        new Date(System.currentTimeMillis()),
        new Date(System.currentTimeMillis() + 30L * 365L * 24L * 60L * 60L * 1000L),
        jcaPKCS10CertificationRequest.getSubject(),
        jcaPKCS10CertificationRequest.getPublicKey()
    /*).addExtension(
        new ASN1ObjectIdentifier("2.5.29.35"),
        false,
        new AuthorityKeyIdentifier(keyPair.getPublic().getEncoded())*/
    ).addExtension(
            new ASN1ObjectIdentifier("2.5.29.19"),
            false,
            new BasicConstraints(false) // true if it is allowed to sign other certs
    ).addExtension(
            new ASN1ObjectIdentifier("2.5.29.15"),
            true,
            new X509KeyUsage(
                X509KeyUsage.digitalSignature |
                    X509KeyUsage.nonRepudiation   |
                    X509KeyUsage.keyEncipherment  |
                    X509KeyUsage.dataEncipherment));

    AsymmetricKeyParameter asymmetricKeyParameter =
          PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
    //ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(asymmetricKeyParameter);
    ContentSigner sigGen = new JcaContentSignerBuilder("SHA1withRSA").build(keyPair.getPrivate());


    X509CertificateHolder x509CertificateHolder = certificateBuilder.build(sigGen);
    org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure =
      x509CertificateHolder.toASN1Structure();
    return eeX509CertificateStructure;
  }

  private X509Certificate readCertificateFromASN1Certificate(
    org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure,
    CertificateFactory certificateFactory)
    throws IOException, CertificateException {
    // Read Certificate
    InputStream is1 = new ByteArrayInputStream(eeX509CertificateStructure.getEncoded());
    X509Certificate signedCertificate =
      (X509Certificate) certificateFactory.generateCertificate(is1);
    return signedCertificate;
  }

Et cela peut être converti en PEM:

  private String convertCertificateToPEM(X509Certificate signedCertificate) throws IOException {
    StringWriter signedCertificatePEMDataStringWriter = new StringWriter();
    JcaPEMWriter pemWriter = new JcaPEMWriter(signedCertificatePEMDataStringWriter);
    pemWriter.writeObject(signedCertificate);
    pemWriter.close();
    log.info("PEM data:");
    log.info("" + signedCertificatePEMDataStringWriter.toString());
    return signedCertificatePEMDataStringWriter.toString();
  }
2
EpicPandaForce

@Mike B - avez-vous testé votre exemple à fond? J'obtiens un comportement étrange avec votre code: j'utilise la version bc15on. Lorsque je signe la demande du client avec une autorité de certification auto-signée, je l'importe dans IE et il montre que le certificat est valide avec l'autorité de certification dans la chaîneenter image description here

Cependant, vous pouvez voir que lors de l'importation dans FF les images à droite, l'autorité de certification dans la chaîne est manquante et ff ne peut pas le vérifier auprès d'une autorité de confiance. Également avec IE ou FF lors de la tentative d'authentification auprès du serveur Web avec lui, il échoue car http ne peut pas non plus le vérifier auprès d'une autorité de confiance.

J'ai apporté quelques modifications à votre code juste pour répondre à mes besoins, mais en général, cela devrait être le même, quelqu'un peut-il me donner des conseils sur ce que je fais mal ici:

    public static String GenCert(long SerNum, int addYear, int addHours,
                             String reqText,
                             String reqName) throws Exception,
                                                    SQLException {

    String result = "";
    reqText = csr; // hard code base64 csr for testing purposes
    reqText =
        "-----BEGIN CERTIFICATE REQUEST-----\n" + reqText +
        "\n-----END CERTIFICATE REQUEST-----\n";

    try {


        String castr = ca + "\n"; // hard code base64 CA pub key for testing
        String strPriv = caPrivk + "\n"; // hard code base64 CA private key for testing


        byte[] encKey = castr.getBytes();
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate caCert =
            (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(encKey));

        PEMParser pr = new PEMParser(new StringReader(strPriv));
        Object obj = pr.readObject();
        JcaPEMKeyConverter converter =
            new JcaPEMKeyConverter().setProvider("BC");
        KeyPair kp;
        kp = converter.getKeyPair((PEMKeyPair)obj);

        PrivateKey privateKey = kp.getPrivate();

        // parse the request
        PEMParser pRd =
            new PEMParser(new InputStreamReader(new ByteArrayInputStream(reqText.getBytes())));
        PKCS10CertificationRequest csr =
            (PKCS10CertificationRequest)pRd.readObject();

        String strReq = csr.getSubject().toString();

        strReq = strReq.substring(strReq.indexOf("CN=") + 3).trim();
        if (strReq.indexOf(",") > 0)
            strReq = strReq.substring(0, strReq.indexOf(",")).trim();
        if (!strReq.equals(reqName)) {
            return "";
        }

        AlgorithmIdentifier sigAlgId =
            new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA"); //SHA1withRSA
        AlgorithmIdentifier digAlgId =
            new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
        X500Name issuer =
            new X500Name(caCert.getSubjectX500Principal().getName());
        BigInteger serial = BigInteger.valueOf(SerNum);

        // The date object returns GMT format
        Date date = new Date(System.currentTimeMillis() - 180 * 1000);
        date.setHours(date.getHours() + addHours);
        Calendar cal = Calendar.getInstance();
        Date from = date;
        cal.setTime(date);
        cal.add(1, addYear);
        Date to = cal.getTime();

        SubjectPublicKeyInfo pkInfo = csr.getSubjectPublicKeyInfo();
        //SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded());
        RSAKeyParameters rsa =
            (RSAKeyParameters)PublicKeyFactory.createKey(pkInfo);
        RSAPublicKeySpec rsaSpec =
            new RSAPublicKeySpec(rsa.getModulus(), rsa.getExponent());
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PublicKey rsaPub = kf.generatePublic(rsaSpec);


        X509v3CertificateBuilder certgen =
            new X509v3CertificateBuilder(issuer, serial, from, to,
                                         csr.getSubject(),
                                         csr.getSubjectPublicKeyInfo());

        certgen.addExtension(X509Extension.basicConstraints, false,
                             new BasicConstraints(false));
        certgen.addExtension(X509Extension.subjectKeyIdentifier, false,
                             new SubjectKeyIdentifier(pkInfo));
        //            certgen.addExtension(X509Extension.subjectKeyIdentifier, false,
        //                                 new SubjectKeyIdentifierStructure(rsaPub)); // In old version done with much more extensive parsing
        certgen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
                             new AuthorityKeyIdentifierStructure(caCert));
        //            certgen.addExtension(X509Extension.authorityKeyIdentifier, false,
        //                                 new AuthorityKeyIdentifier(new GeneralNames(new GeneralName(new X509Name(caCert.getSubjectX500Principal().getName()))),
        //                                                            caCert.getSerialNumber()));

        // add certificate purposes
        ASN1EncodableVector vector = new ASN1EncodableVector();
        vector.add(new DERObjectIdentifier("1.3.6.1.5.5.7.3.2"));
        vector.add(new DERObjectIdentifier("1.3.6.1.4.1.311.20.2.2"));
        vector.add(new DERObjectIdentifier("1.3.6.1.4.1.311.10.3.12"));
        vector.add(new DERObjectIdentifier("1.3.6.1.5.5.7.3.4"));


        DERSequence seq = new DERSequence(vector);
        certgen.addExtension(X509Extensions.ExtendedKeyUsage, false, seq);


        ContentSigner signer =
            new BcRSAContentSignerBuilder(sigAlgId,
                                          digAlgId).build(PrivateKeyFactory.createKey(privateKey.getEncoded()));
        X509CertificateHolder holder = certgen.build(signer);
        byte[] certencoded = holder.toASN1Structure().getEncoded();

        CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
        signer =
            new JcaContentSignerBuilder("SHA1withRSA").build(privateKey);
        generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer,
                                                                                                                                   caCert));
        generator.addCertificate(new X509CertificateHolder(certencoded));
        generator.addCertificate(new X509CertificateHolder(caCert.getEncoded()));
        CMSTypedData content = new CMSProcessableByteArray(certencoded);
        CMSSignedData signeddata = generator.generate(content, true);

        result = Base64Utils.base64Encode(signeddata.getEncoded());

    } catch (Exception e) {
        result = e.toString();
        getStackTrace(e);
    }
    return result;
}

Dans l'ancienne version de mon code où j'utilisais Bouncy Castle 1.4, nous utilisions le X509V3CertificateGenerator et juste avant de retourner le contenu, nous avons utilisé pour construire la chaîne comme ceci:

            X509Certificate newCert =
            certGen.generateX509Certificate(privateKey, "BC");
        //=============================
        List chain = new ArrayList();
        chain.add(newCert);
        //-------------------------------------------------
        //  create the CertPath with old BouncyCastle
        CertificateFactory fact =
            CertificateFactory.getInstance("X.509", "BC");
        CertPath path = fact.generateCertPath(chain);
        result = Base64Utils.base64Encode(path.getEncoded("PKCS7"));

[~ # ~] mise à jour [~ # ~] : OK Cas résolu. Merci à ce fil Évidemment lors de l'utilisation:

cacert.getSubjectX500Principal (). getName ()

J'ai reçu les noms de l'émetteur à l'envers, ce qui a brisé la chaîne, en utilisant à la place:

cert.getSubjectX500Principal (). getEncoded () l'a résolu pour moi! Par conséquent, lorsque votre autorité de certification n'est pas vérifiée auprès d'une autorité de confiance, assurez-vous d'obtenir correctement les noms.

1
Tsonev