使用 Java 中的 Bouncy Castle 自签名 X509 证书

问题描述 投票:0回答:5

我需要使用 Java 中的 Bouncy Castle 创建自签名 X509 证书,但我尝试包含的每个类均已弃用。 我该如何解决这个问题?还有其他课程要包括吗? 谢谢

java cryptography certificate bouncycastle x509
5个回答
37
投票

使用 Bouncycastle 最新版本 - 1.55 1.66

更新@Bewusstsein 的答案。截至此答案(2017 年 5 月 11 日),最新版本中已弃用 bouncycastle 类。如果您使用的是 1.55 或更高版本:

public static Certificate selfSign(KeyPair keyPair, String subjectDN) throws OperatorCreationException, CertificateException, IOException
{
    Provider bcProvider = new BouncyCastleProvider();
    Security.addProvider(bcProvider);

    long now = System.currentTimeMillis();
    Date startDate = new Date(now);

    X500Name dnName = new X500Name(subjectDN);
    BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number

    Calendar calendar = Calendar.getInstance();
    calendar.setTime(startDate);
    calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity

    Date endDate = calendar.getTime();

    String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm.

    ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(keyPair.getPrivate());

    JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate, dnName, keyPair.getPublic());

    // Extensions --------------------------

    // Basic Constraints
    BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity

    certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical.

    // -------------------------------------

    return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner));
}

11
投票

注意:此答案使用具有 11 CVEs 的旧版本库。

这是我正在使用的(使用 BouncyCastle v1.38):

import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.util.Date;

import javax.security.auth.x500.X500Principal;

import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;

import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V3CertificateGenerator;

public class BouncyCastle {

    public static void main(String[] args) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException {
        X509Certificate selfSignedX509Certificate = new BouncyCastle().generateSelfSignedX509Certificate();
        System.out.println(selfSignedX509Certificate);
    }

    public X509Certificate generateSelfSignedX509Certificate() throws CertificateEncodingException, InvalidKeyException, IllegalStateException,
            NoSuchProviderException, NoSuchAlgorithmException, SignatureException {
        addBouncyCastleAsSecurityProvider();

        // generate a key pair
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
        keyPairGenerator.initialize(4096, new SecureRandom());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // build a certificate generator
        X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
        X500Principal dnName = new X500Principal("cn=example");

        // add some options
        certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
        certGen.setSubjectDN(new X509Name("dc=name"));
        certGen.setIssuerDN(dnName); // use the same
        // yesterday
        certGen.setNotBefore(new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000));
        // in 2 years
        certGen.setNotAfter(new Date(System.currentTimeMillis() + 2 * 365 * 24 * 60 * 60 * 1000));
        certGen.setPublicKey(keyPair.getPublic());
        certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
        certGen.addExtension(X509Extensions.ExtendedKeyUsage, true,
                new ExtendedKeyUsage(KeyPurposeId.id_kp_timeStamping));

        // finally, sign the certificate with the private key of the same KeyPair
        X509Certificate cert = certGen.generate(keyPair.getPrivate(), "BC");
        return cert;
    }

    public void addBouncyCastleAsSecurityProvider() {
        Security.addProvider(new BouncyCastleProvider());
    }
}

要使

certGen.generate(keyPair.getPrivate(), "BC");
正常工作,必须将 BouncyCastle 添加为安全提供商。

我确认它可以与此 Maven 依赖项一起使用:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk16</artifactId>
    <version>1.38</version>
</dependency>

7
投票

这是一个完整的自签名 ECDSA 证书生成器,可创建可在客户端和服务器端的 TLS 连接中使用的证书。它已使用 BouncyCastle 1.57 进行了测试。类似的代码可用于创建 RSA 证书。

SecureRandom random = new SecureRandom();

// create keypair
KeyPairGenerator keypairGen = KeyPairGenerator.getInstance("EC");
keypairGen.initialize(256, random);
KeyPair keypair = keypairGen.generateKeyPair();

// fill in certificate fields
X500Name subject = new X500NameBuilder(BCStyle.INSTANCE)
    .addRDN(BCStyle.CN, "stackoverflow.com")
    .build();
byte[] id = new byte[20];
random.nextBytes(id);
BigInteger serial = new BigInteger(160, random);
X509v3CertificateBuilder certificate = new JcaX509v3CertificateBuilder(
    subject,
    serial,
    Date.from(LocalDate.of(2000, 1, 1).atStartOfDay(ZoneOffset.UTC).toInstant()),
    Date.from(LocalDate.of(2035, 1, 1).atStartOfDay(ZoneOffset.UTC).toInstant()),
    subject,
    keypair.getPublic());
certificate.addExtension(Extension.subjectKeyIdentifier, false, id);
certificate.addExtension(Extension.authorityKeyIdentifier, false, id);
BasicConstraints constraints = new BasicConstraints(true);
certificate.addExtension(
    Extension.basicConstraints,
    true,
    constraints.getEncoded());
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.digitalSignature);
certificate.addExtension(Extension.keyUsage, false, usage.getEncoded());
ExtendedKeyUsage usageEx = new ExtendedKeyUsage(new KeyPurposeId[] {
    KeyPurposeId.id_kp_serverAuth,
    KeyPurposeId.id_kp_clientAuth
});
certificate.addExtension(
    Extension.extendedKeyUsage,
    false,
    usageEx.getEncoded());

// build BouncyCastle certificate
ContentSigner signer = new JcaContentSignerBuilder("SHA256withECDSA")
    .build(keypair.getPrivate());
X509CertificateHolder holder = certificate.build(signer);

// convert to JRE certificate
JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
converter.setProvider(new BouncyCastleProvider());
X509Certificate x509 = converter.getCertificate(holder);

// serialize in DER format
byte[] serialized = x509.getEncoded();

3
投票

这是 BouncyCastle 自身用来生成 X.509 证书的代码。 您将需要 BC 的 thisthis 库才能使用它。 有关如何使用它的更多详细信息,请查看this问题(主类)。

    public class BCCertGen {
    public static String _country = "Westeros",
                         _organisation = "Targaryen",
                         _location = "Valyria",
                         _state = "Essos",
                         _issuer = "Some Trusted CA";

    public BCCertGen(String country, String organisation, String location, String state, String issuer){
        _country = country;
        _organisation = organisation;
        _location = location;
        _state = state;
        _issuer = issuer;
    }
    public static X509Certificate generate(PrivateKey privKey, PublicKey pubKey, int duration, String signAlg, boolean isSelfSigned) throws Exception{
        Provider BC = new BouncyCastleProvider();

        // distinguished name table.
        X500NameBuilder builder = createStdBuilder();

        // create the certificate
        ContentSigner sigGen = new JcaContentSignerBuilder(signAlg).build(privKey);
        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
                new X500Name("cn="+_issuer),    //Issuer
                BigInteger.valueOf(1),      //Serial
                new Date(System.currentTimeMillis() - 50000),   //Valid from
                new Date((long)(System.currentTimeMillis() + duration*8.65*Math.pow(10,7))),    //Valid to
                builder.build(),    //Subject
                pubKey              //Publickey to be associated with the certificate
        );

        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));

        cert.checkValidity(new Date());

        if (isSelfSigned) {
            // check verifies in general
            cert.verify(pubKey);
            // check verifies with contained key
            cert.verify(cert.getPublicKey());
        }

        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);

        return (X509Certificate) fact.generateCertificate(bIn);
    }

    private static X500NameBuilder createStdBuilder() {
        X500NameBuilder builder = new X500NameBuilder(RFC4519Style.INSTANCE);

        builder.addRDN(RFC4519Style.c, _country);
        builder.addRDN(RFC4519Style.o, _organisation);
        builder.addRDN(RFC4519Style.l, _location);
        builder.addRDN(RFC4519Style.st, _state);

        return builder;
    }
}

编辑:我不记得我到底是从哪个BC测试中进行的,但这里有类似的东西https://github.com/bcgit/bc-java/blob/master/prov/src/test/java/org /bouncycastle/pqc/jcajce/provider/test/KeyStoreTest.java


0
投票

使用 Bouncycastle 版本 1.76(2023 年 7 月)

这是最新 BC 版本的更新。
此代码已使用 JDK 17 进行测试。
另外,在可能的情况下,我尝试使用可用的常量,而不是字符串。

package uk.co.bc;

import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.time.ZonedDateTime;
import java.util.Date;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

public class GenerateSelfSigned {

    public  static void main(final String[] args) throws Exception {

        final var x509Cert = getSelfSignedCert();

        Files.write(Path.of("self.signed.x509.cer"), x509Cert.getEncoded());
    }

    private static X509Certificate getSelfSignedCert() throws Exception {

        final var            bcProvider = new BouncyCastleProvider();

        Security.addProvider(bcProvider);

        final var prng             = new SecureRandom();

        final var sn               = new BigInteger(Long.SIZE, prng);

        final var keyPairGenerator = KeyPairGenerator.getInstance("RSA", bcProvider);
        ;         keyPairGenerator.initialize(4096, prng);

        final var keyPair          = keyPairGenerator.generateKeyPair();
        final var keyPublic        = keyPair.getPublic();
        final var keyPublicEncoded = keyPublic.getEncoded();
        final var keyPublicInfo    = SubjectPublicKeyInfo.getInstance(keyPublicEncoded);

        final var x500subject      = new X500Name(new RDN[] {new RDN (
                new AttributeTypeAndValue[] {
                new AttributeTypeAndValue(BCStyle.CN, new DERUTF8String("localhost")),
                new AttributeTypeAndValue(BCStyle.OU, new DERUTF8String("You nit!")),
                new AttributeTypeAndValue(BCStyle.O,  new DERUTF8String("Organised")),
                new AttributeTypeAndValue(BCStyle.L,  new DERUTF8String("Settee")),
                new AttributeTypeAndValue(BCStyle.ST, new DERUTF8String("Stayt")),
                new AttributeTypeAndValue(BCStyle.C,  new DERUTF8String("uk"))
        }) });
        final var x500issuer       = x500subject;

        final var notBefore        = ZonedDateTime.now();
        final var notAfter         = notBefore.plusYears(100);

        final var dateNotBefore    = Date.from(notBefore.toInstant());
        final var dateNotAfter     = Date.from(notAfter .toInstant());

        try(final var bais = new ByteArrayInputStream(keyPublicEncoded);
            final var ais  = new      ASN1InputStream(bais))
        {
            final var asn1Sequence         = (ASN1Sequence) ais.readObject();

            final var subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(asn1Sequence);
            final var subjectPublicKeyId   = new BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo);

            /*
             * Now build the Certificate, add some Extensions & sign it...
             */
            final var signatureAlgorithm   = "SHA256WithRSA";

            final var certBuilder          = new X509v3CertificateBuilder(x500issuer, sn, dateNotBefore, dateNotAfter, x500subject, keyPublicInfo);
            final var contentSigner        = new  JcaContentSignerBuilder(signatureAlgorithm).build(keyPair.getPrivate());
            /*
             * BasicConstraints instantiated with "CA=true"
             * The BasicConstraints Extension is usually marked "critical=true"
             * 
             * The Subject Key Identifier extension identifies the public key certified by this certificate.
             * This extension provides a way of distinguishing public keys if more than one is available for
             * a given subject name.
             */
            final var certHolder           = certBuilder
                    .addExtension(Extension.basicConstraints,     true,  new BasicConstraints(true))
                    .addExtension(Extension.subjectKeyIdentifier, false, subjectPublicKeyId)
                    .build(contentSigner);

            return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certHolder);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.