Bouncy Castle: Signed Certificate with an existing CA - java

I'm trying to create a certificate (A) which is signed for other certificate (B) stored in a p12 keystore. This stored certificate (B) was added to the trusted certificate store of my local machine.
Certificate A is used to sign a pdf document using bouncy castle 1.52 library, but the digital signature that I obtain in the signed document is invalid.
I'm going to explain the steps done just if somebody can help me.
First, I create a CSR from the p12 keystore(B):
private static PKCS10CertificationRequest generateCSR() {
PKCS10CertificationRequest csr = null;
try {
initCACert();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal("CN=Requested Test Certificate"), CAcert.getPublicKey());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(CApk);
csr = p10Builder.build(signer);
} catch (Exception e) {
log.error(e);
}
return csr;
}
Then, a certificate was generated (A) with this CSR.
private static Certificate signCSR() throws Exception {
PKCS10CertificationRequest csr = generateCSR();
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
X500Name issuer = X500Name.getInstance(CAcert.getSubjectX500Principal().getEncoded());
BigInteger serial = new BigInteger(32, new SecureRandom());
Calendar c = Calendar.getInstance();
c.add(Calendar.SECOND, -1);
Date from = c.getTime();
c.add(Calendar.YEAR, 5);
Date to = c.getTime();
X509v1CertificateBuilder certBuilder = new X509v1CertificateBuilder(issuer, serial, from, to, csr.getSubject(),
csr.getSubjectPublicKeyInfo());
ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
.build(PrivateKeyFactory.createKey(CApk.getEncoded()));
X509CertificateHolder holder = certBuilder.build(signer);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(holder.getEncoded());
Certificate cert = certFactory.generateCertificate(in);
return cert;
}
Finally, I use this generated certificate (A) to sign my pdf.
Certificate cert = signCSR();
SignerInfoGeneratorBuilder signerInfoBuilder = new SignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
);
signerInfoBuilder.setSignedAttributeGenerator( signedAttributeGenerator );
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder( "SHA1WITHRSA" );
contentSignerBuilder.setProvider("BC");
X509CertificateHolder certificateHolder = new X509CertificateHolder( cert.getEncoded( ) );
generator.addSignerInfoGenerator(
signerInfoBuilder.build( contentSignerBuilder.build( CApk ),
certificateHolder )
);
ArrayList<X509CertificateHolder> signingChainHolder = new ArrayList<X509CertificateHolder>( );
certificateHolder = new X509CertificateHolder( cert.getEncoded() );
certificateHolder = new X509CertificateHolder( CAcert.getEncoded() );
signingChainHolder.add( certificateHolder );
Store certs = new JcaCertStore( signingChainHolder );
generator.addCertificates( certs );
CMSTypedData content = new CMSProcessableByteArray(datos);
CMSSignedData signedData = generator.generate( content, true );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new DEROutputStream(baos).writeObject(signedData.toASN1Structure());
result = baos.toByteArray();
All the process is executed apparently right, but when I open the pdf the signature is invalid:
EDIT: I have made an export of the generated certificate. This is the result obtained.
I will appreciate any comment or information that can help me to fix this.
Thanks in advance.

generator.addSignerInfoGenerator(
signerInfoBuilder.build( contentSignerBuilder.build( CApk ),
certificateHolder )
);
If I see that right, you're using the CA's private key to sign the data. It should be the certificate's. So private and public key don't match and therefor a signature validation check is failing.

I have identified the problem: I was building the certificate chain in the opposite order that should be.
I had this order:
certificateHolder = new X509CertificateHolder( cert.getEncoded() );
certificateHolder = new X509CertificateHolder( CAcert.getEncoded() );
And the right order is this:
certificateHolder = new X509CertificateHolder( CAcert.getEncoded() );
certificateHolder = new X509CertificateHolder( cert.getEncoded() );
I hope somebody can find this information useful!

Related

create PKCS7 with presigned data using bouncy castle

I would like to create a detached signature in a PDF file using a PKCS7 container. The data (hash) is being signed beforehand on a different device with the private key. I want to create a PKCS7 containing the signed data along with the certificate with the public key. I can't seem to create the PKCS7 with bouncy castle without supplying the private key and having the library signing the data. This doesn't seem to work:
InputStream inStream = new FileInputStream("1_public.pem");
BufferedInputStream bis = new BufferedInputStream( inStream );
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<Certificate> certList = new ArrayList<Certificate>();
Certificate certificate = cf.generateCertificate(bis);
certList.add(certificate);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addCertificates( certs );
CMSProcessableInputStream msg = new CMSProcessableInputStream( new ByteArrayInputStream( "signedhash".getBytes() ) );
CMSSignedData signedData = gen.generate(msg, false);
byte[] pkcs7 = signedData.getEncoded() ) );
I managed to do this by providing a ContentSigner that doesn't sign, actually quite simple:
InputStream inStream = new FileInputStream("1_public.pem");
BufferedInputStream bis = new BufferedInputStream( inStream );
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<Certificate> certList = new ArrayList<Certificate>();
Certificate certificate = cf.generateCertificate(bis);
certList.add(certificate);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addCertificates( certs );
final byte[] signedHash = "signedhash".getBytes();
ContentSigner nonSigner = new ContentSigner() {
#Override
public byte[] getSignature() {
return signedHash;
}
#Override
public OutputStream getOutputStream() {
return new ByteArrayOutputStream();
}
#Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return new DefaultSignatureAlgorithmIdentifierFinder().find( "SHA256WithRSA" );
}
};
org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded()));
JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());
sigb.setDirectSignature( true );
gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert)));
CMSProcessableInputStream msg = new CMSProcessableInputStream( new ByteArrayInputStream( "not used".getBytes() ) );
CMSSignedData signedData = gen.generate(msg, false);
byte[] pkcs7 = signedData.getEncoded();
In case the "external signature" is performed by a hardware device it is possible that it also contains "signed attributes". In this case the code must also contain:
AttributeTable signedAttributes = signer.getSignedAttributes();
signerInfoBuilder.setSignedAttributeGenerator(new SimpleAttributeTableGenerator(signedAttributes));
signatureGenerator.addSignerInfoGenerator(signerInfoBuilder.build(nonSigner, signCertificate));
you should also remove the
signatureGenerator.setDirectSignature(true)
a complete example can be found here https://www.len.ro/work/attach-payload-into-detached-pkcs7-signature/. Since I spend a lot of time searching for a solution and this post provided a vital clue I thought I should complete with the information I still missed in an article. Thanks.

Correct way to instantiate class so I'm able to validate path chains in certificate

I have 3d party library(sdk for Kalkan provider). Part of this library checks certificate paths I believe. Problem is I should pass two parameters to instantiate class correctly that I could use one of its method I need.
Here's code:
final PKIXCertPathReviewer checker = new PKIXCertPathReviewer(cp, params);
boolean test = checker.isValidCertPath();
Here's part of constructor:
public PKIXCertPathReviewer(CertPath certPath, PKIXParameters params)
About task a bit. I have signed document by client certificate. I want to validate path in client's certificate. So I have client's X509Certificate instance of certificate, one middle certificate and one root certificate. Last two are files in cer format on disk. As I understand I should combine all those three certificates together. If someone showed me how to create those cp and params it would be really helpfull. Thanks in advance.
So I've managed to solve my problem. Here's code if someone would have the same problems as me.
CertificateFactory cf = CertificateFactory.getInstance("X.509", KalkanProvider.PROVIDER_NAME);
java.security.cert.Certificate rootCertificate = /*root certificate*/;
java.security.cert.Certificate clientCertificate = /*client certificate*/;
List mylist = new ArrayList();
mylist.add(clientCertificate);
CertPath cp = cf.generateCertPath(mylist);
TrustAnchor rootAnchor = new TrustAnchor((X509Certificate) rootCertificate, null);
Set<TrustAnchor> trustStore = new HashSet<>();
trustStore.add(rootAnchor);
if (null != /*chain contains middle cert besides root*/) {
java.security.cert.Certificate middleCertificate = /*middle certificate*/;
TrustAnchor middleAnchor = new TrustAnchor((X509Certificate) middleCertificate, null);
trustStore.add(middleAnchor);
}
PKIXParameters params = new PKIXParameters(trustStore);
params.setRevocationEnabled(false);//true - if you need ocsp validation
final PKIXCertPathReviewer checker = new PKIXCertPathReviewer(cp, params);
errors = checker.getErrors();
boolean result = checker.isValidCertPath();
Here's better solution:
Provider p = ProviderUtil.loadKalkanProvider();
X509Certificate ca = X509Util.loadX509Certificate("/tmp/ca/root_ca_rsa.cer", p);
X509Certificate nca = X509Util.loadX509Certificate("/tmp/ca/root_nca_rsa.cer", p);
X509Certificate cert = X509Util.loadX509Certificate("/tmp/cert/user.cer", p);
X509CRL crl = X509Util.loadX509CRL("/tmp/ca/rsa.crl", p);
X509CRL rcrl = X509Util.loadX509CRL("/tmp/ca/root_rsa.crl", p);
ArrayList<X509Extension> list = new ArrayList<X509Extension>();
list.add(nca);
list.add(cert);
list.add(crl);
list.add(rcrl);
CollectionCertStoreParameters certStoreParameters = new CollectionCertStoreParameters(list);
CertStore certStore = CertStore.getInstance("Collection", certStoreParameters, p);
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", p);
// проверка до корневого CA, можно сократить до промежуточного NCA
TrustAnchor anchor = new TrustAnchor(ca, null);
Set<TrustAnchor> anchors = new HashSet<TrustAnchor>();
anchors.add(anchor);
X509CertSelector selector = new X509CertSelector();
// задаем параметры для селектора конечного сертификата или можно указать полное соответствие сертификата
//selector.setSerialNumber(cert.getSerialNumber());
//selector.setIssuer(cert.getIssuerX500Principal());
selector.setCertificate(cert);
PKIXBuilderParameters builderParameters = new PKIXBuilderParameters(anchors, selector);
// если не добавляли список CRL, то надо отключить проверку
//builderParameters.setRevocationEnabled(false);
builderParameters.addCertStore(certStore);
builderParameters.setSigProvider(p.getName());
PKIXCertPathBuilderResult builderResult = (PKIXCertPathBuilderResult) builder.build(builderParameters);
System.out.println(builderResult);
// можем закодировать и сохранить полученный путь в форматах PkiPath/PKCS7/PEM
//builderResult.getCertPath().getEncoded("PkiPath");

Sign CSR with BouncyCastle

I have been looking for the past few days for a solution on my Problem and couldn't find anything. I am missing something in my Code but i cant figure out what :( Somehow when I sign my PKCS#10 the chain is broken.
Basically i have a server and a client. I want to have the client send a CSR to the server and the server signs it so they can communicate. Now i did set up a PKCS#12 with BouncyCastle for the Client and i did set up a RootCertificate for the Server (again with BouncyCastle, which is in my understanding just a PKCS#12 with the extension to be able to sign Certificates)
In Code it looks like this:
Provider BC = new BouncyCastleProvider();
Security.addProvider(BC);
//create KeyPair
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
kpGen.initialize(2048, new SecureRandom());
pair = kpGen.generateKeyPair();
//building groundbase for certificate
X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
builder.addRDN(BCStyle.CN, commonName);
builder.addRDN(BCStyle.OU, organizationalUnit);
builder.addRDN(BCStyle.O, organization);
builder.addRDN(BCStyle.L, city);
builder.addRDN(BCStyle.ST, state);
builder.addRDN(BCStyle.C, country);
Date notBefore = new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24); //Yesterday
Date notAfter = new Date(System.currentTimeMillis() + 1000L * 365L * 24L * 60L * 60L); //in a year
BigInteger serial = BigInteger.valueOf(new SecureRandom().nextLong());
//creating a self-signed certificate from information in builder
X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(),
serial, notBefore, notAfter, builder.build(), pair.getPublic());
//The next line will make the difference between a Certificate and a Ca Certificate
certGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
ContentSigner sigGen = new JcaContentSignerBuilder(").setProvider(BC).build(pair.getPrivate());
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
Now i create a CSR for the Client (Client keystore has the just created PKCS#12 at first position):
String alias = keystore.aliases().nextElement();
X509Certificate cert = (X509Certificate) keystore.getCertificate(alias);
X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
//builder for the PKCS10
PKCS10CertificationRequestBuilder requestBuilder = new JcaPKCS10CertificationRequestBuilder(x500name, cert.getPublicKey());
//algorithm identifier
DefaultSignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder();
DefaultDigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
AlgorithmIdentifier sigAlgId = sigAlgFinder.find("SHA512WithRSA");
digAlgFinder.find(sigAlgId);
//content Signer
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA512WithRSA");
//and build the Cert
ContentSigner signer = contentSignerBuilder.build((PrivateKey) keystore.getKey(alias, password));
PKCS10CertificationRequest req = requestBuilder.build(signer);
JcaPKCS10CertificationRequest req2 = new JcaPKCS10CertificationRequest(req.getEncoded()).setProvider("BC");
I send this JcaPKCS10CertificationRequest encoded over the network. The Server gets it and creates his CA Certificate and now has to sign the PKCS#10 but i am missing something here because he is not including the chain. The Certificate he is creating has the information about the issuer and BasicConstraints but the certification path is only including the Clients Certificate and NOT the Certificate of the Server so it is not trustworthy since the chain is broken.
This is what i do (Server Keystore has the CA Certificate at position 0, CSR is the JcaPKCS10CertificationRequest):
String alias = keystore.aliases().nextElement();
// PKCS#12 Root Certificate
X509Certificate cert = (X509Certificate) keystore.getCertificate(alias);
// generated Serial
BigInteger serial = BigInteger.valueOf(new SecureRandom().nextLong());
//identify algorithm
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA512WithRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find( sigAlgId );
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
JcaX509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(cert, serial, cert.getNotBefore(), cert.getNotAfter(),
CSR.getSubject(), CSR.getPublicKey());
certGen.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(cert));
certGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(false));
certGen.addExtension(Extension.subjectKeyIdentifier, true, extUtils.createSubjectKeyIdentifier(inputCSR.getPublicKey()));
certGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.nonRepudiation));
ContentSigner signer = new JcaContentSignerBuilder(sigAlgName).setProvider("BC").build((PrivateKey)keystore.getKey(alias, password));
X509CertificateHolder holder = certGen.build(signer);
X509Certificate signedCert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(holder);
signedCert.verify(cert.getPublicKey());
JcaPEMWriter pemWriter = new JcaPEMWriter(new FileWriter(new File("cer.cer")));
pemWriter.writeObject(signedCert);
pemWriter.writeObject(cert);
pemWriter.close();
Now as i said the generated File "cer.cer" has not the Chain in it. How can i add the chain? Can i then send that signedCert back to the client and it can be used in a ssl handshake?
To add the chain, this worked for me
After X509CertificateHolder holder = certGen.build(signer);
byte[] certencoded = holder.toASN1Structure().getEncoded();
ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").build(caPrivateKkey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
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);
byte certificateP7b[] = signedData.getEncoded();
With this code you get a Certificate with the full chain in PCKS#7 format. If you prefer to work with X509 format
public static List<X509Certificate> p7BToX509(byte signedCert[]) throws CertificateException{
ByteArrayInputStream is = new ByteArrayInputStream( signedCert);
CertificateFactory cf = CertificateFactory.getInstance( "X.509" );
ArrayList<X509Certificate> certificates = new ArrayList<X509Certificate>();
Iterator i = cf.generateCertificates( is ).iterator();
while ( i.hasNext() ){
X509Certificate c = (X509Certificate)i.next();
certificates.add(c);
}
return certificates;
}
This is the public certificate. In your client you should have the private key. These are all elements you need to perform and ssl handshake

CSR generated with Bouncycastle missing public key and attributes

I am using Bouncy castle to generate CSR. This certificate is intended for CA. When i use OpenSSL to view the text information of the CSR, I find the public key and attributes missing. Any help is appreciated.
Versions:
Bouncy castle: bcpkix-jdk15on:1.47
String principal = "CN=company1, OU=company1, O=company1, C=GB"
AsymmetricKeyParameter keyParam = PrivateKeyFactory.createKey(pair.getPrivate().getEncoded());
AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder()
.find("SHA1WITHRSA");
AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find("SHA-1");
ContentSigner signer = new BcRSAContentSignerBuilder(signatureAlgorithm, digestAlgorithm).build(keyParam);
SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(signatureAlgorithm, pair.getPublic()
.getEncoded());
PKCS10CertificationRequestBuilder csrBuilder = new PKCS10CertificationRequestBuilder(
new X500Name(principal), publicKeyInfo);
csrBuilder.addAttribute(X509Extension.basicConstraints, new BasicConstraints(true));
csrBuilder.addAttribute(X509Extension.keyUsage, new KeyUsage(KeyUsage.cRLSign | KeyUsage.keyCertSign));
csr = csrBuilder.build(signer);
Pem of the generated csr
-----BEGIN CERTIFICATE REQUEST-----
MIICvjCCAaYCAQAwRjERMA8GA1UEAwwIY29tcGFueTExETAPBgNVBAsMCGNvbXBh
bnkxMREwDwYDVQQKDAhjb21wYW55MTELMAkGA1UEBhMCR0IwggE6MA0GCSqGSIb3
DQEBBQUAA4IBJwAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCf8WK4
L1yaBqlvV8cqlTerv53I5MchllrkR94oE42JNuQ0vmQlh/wc8WfqB1lkYvQdf04g
IQ69VQKCIfQeahODnQ9N/Ct4wIfoCz3KtZZq7DZgoIsMNf2tWlGwJMTbPYLJYjPv
rfxGMh79dF6VpxMDIHLrvhgYzDFfPxhQXpTTVNXY9pMkrQ+8ZnlqpSQLToQ5JUFZ
ZDiJtZvmhELGOrDxDDHBlmBRMadjRx5bP6JtJYtv540p55trUnJVRmGjjMvWw5aE
cKm7Z1BcoTwLsn0gzBR43el0J9QB+RMiDsJKhaBugzv3/852Ih8eZis6G4dRWnm9
BvAgRQfiW4ciJEnZAgMBAAGgGzALBgNVHQ8xBAMCAQYwDAYDVR0TMQUwAwEB/zAN
BgkqhkiG9w0BAQUFAAOCAQEAI6s+Wybusc2JBN36RMMG4qf8awIVJo/d1KwAhm9Y
7eO+ILLXk3wkZEdX5vEPQAdN7ZYYr1lCQfU2QuxDm3OCYuqJBt0fZGWAPYlfp6QD
AnQLEuLIIP/jZSgn2YzLeOuwO2n+7I9sx2lBihfkzNIK9PEiYM2TOA+4Rac7XdFA
o20GnruZ1Gq79C043Yz+G8iMNS44vaVjlshDovvmD5YDtjmQRtvDzoB2lyqEVwsS
Xy+vc0NdyWHJxAUPeOl+iqjF5YeACH92fFw9WV46syCAW7t9dCqdntvhKQRV+Me0
dOelvZPcqKtd7fsWYpKgUYkk61uWskeLIgnSonEyHWVSwQ==
-----END CERTIFICATE REQUEST-----
Missing public key, basic constraints and key usage
Certificate Request:
Data:
Version: 0 (0x0)
Subject: CN=company1, OU=company1, O=company1, C=GB
Subject Public Key Info:
Public Key Algorithm: sha1WithRSAEncryption
Unable to load Public Key
140432158140064:error:0609E09C:digital envelope routines:PKEY_SET_TYPE:unsupported algorithm:p_lib.c:239:
140432158140064:error:0B07706F:x509 certificate routines:X509_PUBKEY_get:unsupported algorithm:x_pubkey.c:155:
Attributes:
X509v3 Key Usage :unable to print attribute
X509v3 Basic Constraints :unable to print attribute
Signature Algorithm: sha1WithRSAEncryption
23:ab:3e:5b:26:ee:b1:cd:89:04:dd:fa:44:c3:06:e2:a7:fc:
6b:02:15:26:8f:dd:d4:ac:00:86:6f:58:ed:e3:be:20:b2:d7:
93:7c:24:64:47:57:e6:f1:0f:40:07:4d:ed:96:18:af:59:42:
41:f5:36:42:ec:43:9b:73:82:62:ea:89:06:dd:1f:64:65:80:
3d:89:5f:a7:a4:03:02:74:0b:12:e2:c8:20:ff:e3:65:28:27:
d9:8c:cb:78:eb:b0:3b:69:fe:ec:8f:6c:c7:69:41:8a:17:e4:
cc:d2:0a:f4:f1:22:60:cd:93:38:0f:b8:45:a7:3b:5d:d1:40:
a3:6d:06:9e:bb:99:d4:6a:bb:f4:2d:38:dd:8c:fe:1b:c8:8c:
35:2e:38:bd:a5:63:96:c8:43:a2:fb:e6:0f:96:03:b6:39:90:
46:db:c3:ce:80:76:97:2a:84:57:0b:12:5f:2f:af:73:43:5d:
c9:61:c9:c4:05:0f:78:e9:7e:8a:a8:c5:e5:87:80:08:7f:76:
7c:5c:3d:59:5e:3a:b3:20:80:5b:bb:7d:74:2a:9d:9e:db:e1:
29:04:55:f8:c7:b4:74:e7:a5:bd:93:dc:a8:ab:5d:ed:fb:16:
62:92:a0:51:89:24:eb:5b:96:b2:47:8b:22:09:d2:a2:71:32:
1d:65:52:c1
I found the problem. I was associating public key with a signature algorithm instead of an encryption algorithm.
AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder()
.find("SHA1WITHRSA");
AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find("SHA-1");
ContentSigner signer = new BcRSAContentSignerBuilder(signatureAlgorithm, digestAlgorithm).build(keyParam);
SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(signatureAlgorithm, pair.getPublic().getEncoded());
Here is the working code that would generate a CSR for RSA keys using signature algorithm SHA1WithRSA
String principal = "CN=company1, OU=company1, O=company1, C=GB";
AsymmetricKeyParameter privateKey = PrivateKeyFactory.createKey(pair.getPrivate().getEncoded());
AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder()
.find("SHA1WITHRSA");
AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find("SHA-1");
ContentSigner signer = new BcRSAContentSignerBuilder(signatureAlgorithm, digestAlgorithm).build(privateKey);
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(new X500Name(
principal), pair.getPublic());
ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
extensionsGenerator.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(true));
extensionsGenerator.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign
| KeyUsage.cRLSign));
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensionsGenerator.generate());
csr = csrBuilder.build(signer);
Thanks a lot Rag,
Didn't know ExtensionGenerator before and was trying to add as csrBuilder.addAttribute(..)
It never failed but didn't generated extension properly.
Adding few more lines to add ExtendedKeyUsage and Subject Alternate Name since many people are facing this issue.
Vector<KeyPurposeId> extendedKeyUsageVector = new Vector<KeyPurposeId>();
for (String extendedKeyUsage : bean.getExtendedKeyUsage()) {
extendedKeyUsageVector.add(new KeyPurposeId(extendedKeyUsage));
}
extnGen.addExtension(X509Extension.extendedKeyUsage, false, new ExtendedKeyUsage(extendedKeyUsageVector));
GeneralName[] subjectAltName = new GeneralName[2];
subjectAltName[0] = new GeneralName(GeneralName.dNSName, "abc.com");
subjectAltName[1] = new GeneralName(GeneralName.dNSName, "xyz.com");
extnGen.addExtension(X509Extension.subjectAlternativeName, false, new GeneralNames(subjectAltName));
X509Extension has been deprecated:
http://www.borelly.net/cb/docs/javaBC-1.4.8/prov/deprecated-list.html
So here's an update:
static private X500Name getX500Name(){
final String testPostalCode = "92156-4105";
return new X500NameBuilder( BCStrictStyle.INSTANCE )
.addRDN( BCStyle.CN, "JD" )
.addRDN( BCStrictStyle.POSTAL_CODE, testPostalCode )
.addRDN( BCStrictStyle.C, "US" )
.build();
}//getX500Name
static public PKCS10CertificationRequest genCSR(){
try{
KeyPair pair = getKeyPair();
GeneralNames subjectAltName = new GeneralNames(
new GeneralName(GeneralName.rfc822Name, "MyAwesomeAddr#email.com"));
ExtensionsGenerator extnsnGenr = new ExtensionsGenerator();
extnsnGenr.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
getX500Name()
, pair.getPublic() )
.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extnsnGenr.generate());
//.setLeaveOffEmptyAttributes(false)
;
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder( SHA256withECDSA );
ContentSigner signer = csBuilder.build( pair.getPrivate() );
PKCS10CertificationRequest CSR = p10Builder.build( signer );
return CSR;
}
catch ( IOException| OperatorCreationException X ){ mLog.error( CRYPTOERR.toString() ); }
}//genCSR

Sign CSR using Bouncy Castle

I cannot find any code/doc describing how to sign a CSR using BC. As input I have a CSR as a byte array and would like to get the cert in PEM and/or DER format.
I have gotten this far
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)
I have trouble figuring out get from a certificate generator to store this in PEM or DER format.
Or am I going down the wrong path all together?
Ok ... I was looking to do the same stuff and for the life of me I couldn't figure out how. The APIs all talk about generating the key pairs and then generating the cert but not how to sign a CSR. Somehow, quite by chance - here's what I found.
Since PKCS10 represents the format of the request (of the CSR), you first need to put your CSR into a PKCS10Holder. Then, you pass it to a CertificateBuilder (since CertificateGenerator is deprecated). The way you pass it is to call getSubject on the holder.
Here's the code (Java, please adapt as you need):
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;
}
As you can see, I've generated the request outside this method, but passed it in. Then, I have the PKCS10CertificationRequestHolder to accept this as a constructor arg.
Next, in the X509v3CertificateBuilder arguments, you'll see the pk10Holder.getSubject - this is apparently all you need? If something is missing, please let me know too!!! It worked for me. The cert I generated correctly had the DN info I needed.
Wikipedia has a killer section on PKCS - http://en.wikipedia.org/wiki/PKCS
The following code is based on the above answers but will compile and, given a PEM encoded CSR (of the kind exported by keytool), will return a valid PEM-encoded signedData object containing a signed Certificate chain (of the type that can be imported by keytool).
Oh and it's against 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");
}
Archie thanks!
I made some changes to you code, see below.
The main changes are to pass the issuer's name and use the public key from the 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))
#Mike B - have you tested your example thoroughly ? I get a strange behavior with your code:
Im using bc15on version. When I sign the client request with a self signed CA I import it in IE and it shows the certificate as valid with the CA in the chain
However you can see that when imported in FF the images on the right the CA in the chain is missing and ff cannot verify it to a Trusted Authority. Also with IE or FF when attempting to authenticate to the web server with it it fails as http cannot verify it to a trusted authority too.
Ive made some changes to your code just to suit my needs but in general it should be the same, can anyone give me some pointers onto what Im doing wrong here:
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;
}
In the old version of my code where I used bouncy castle 1.4 we used the X509V3CertificateGenerator and just before returning the content we used to build the chain like so:
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"));
UPDATE: OK Case solved. Thanks to this thread Obviously when using:
cacert.getSubjectX500Principal().getName()
I got the names of the issuer in reverse, which broke the chain, using instead:
cert.getSubjectX500Principal().getEncoded() solved it for me! So when your CA does not get verified upto trusted authority make sure you are getting the names correctly.
In the end, this is what worked for me:
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);
Where code is
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;
}
And this can be converted to 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();
}
Here's my solution in C# using BouncyCastle NuGet package version 1.8.9:
public static X509Certificate2 SignCertificate(TextReader pemEncodedCsrReader, X509Certificate2 caCertificate, AsymmetricKeyParameter issuerPrivateKey)
{
var csr = (Pkcs10CertificationRequest)new PemReader(pemEncodedCsrReader).ReadObject();
var certificateGenerator = new X509V3CertificateGenerator();
certificateGenerator.SetNotBefore(DateTime.Now.Date);
certificateGenerator.SetNotAfter(DateTime.Now.Date.AddYears(20));
certificateGenerator.SetIssuerDN(new X509Name(caCertificate.IssuerName.Name));
certificateGenerator.SetSubjectDN(csr.GetCertificationRequestInfo().Subject);
certificateGenerator.SetPublicKey(PublicKeyFactory.CreateKey(csr.GetCertificationRequestInfo().SubjectPublicKeyInfo);
var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
certificateGenerator.AddExtension(X509Extensions.BasicConstraints, false, new BasicConstraints(cA: false));
certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
new SubjectKeyIdentifier(csr.GetCertificationRequestInfo().SubjectPublicKeyInfo));
certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false,
new AuthorityKeyIdentifier(new GeneralNames(new GeneralName(new X509Name(caCertificate.SubjectName.Name))), new BigInteger(caCertificate.SerialNumber, 16)));
var signatureFactory = new Asn1SignatureFactory(SignatureAlgorithm, issuerPrivateKey, random);
var certificate = certificateGenerator.Generate(signatureFactory);
return new X509Certificate2(certificate.GetEncoded());
}

Categories

Resources