CSR generated with Bouncycastle missing public key and attributes - java

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

Related

Bouncy Castle: Signed Certificate with an existing CA

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!

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

Certificate generated through CSR signing with BouncyCastle considered untrusted

I am struggling with the following issue:
I have a CSR which I am signing with this code:
#Override
public X509Certificate signCSR( Reader pemcsr, int validityDays ) throws APIException
{
try ( PEMParser reader = new PEMParser( pemcsr ) )
{
KeyStore keystore = getKeyStore();
Properties cryptoProps = getCryptoProperties();
String caKeyAlias = cryptoProps.getProperty( PROPERTY_KEYSTORE_CA_CERT_ALIAS );
String caKeyPassword = cryptoProps.getProperty( PROPERTY_KEYSTORE_CA_CERT_PASSWORD );
PrivateKey cakey = (PrivateKey) keystore.getKey( caKeyAlias, caKeyPassword.toCharArray() );
X509Certificate cacert = (X509Certificate) keystore.getCertificate( caKeyAlias );
PKCS10CertificationRequest csr = (PKCS10CertificationRequest) 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() + ( validityDays * 86400000L ) );
DigestCalculator digCalc = new BcDigestCalculatorProvider().get( new AlgorithmIdentifier( OIWObjectIdentifiers.idSHA1 ) );
X509ExtensionUtils x509ExtensionUtils = new X509ExtensionUtils( digCalc );
X509v3CertificateBuilder certgen = new X509v3CertificateBuilder( issuer, serial, from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo() );
// Basic Constraints
// certgen.addExtension( Extension.basicConstraints, true, new
// BasicConstraints( 0 ) );
// Subject Key Identifier
// certgen.addExtension( Extension.subjectKeyIdentifier, false,
// x509ExtensionUtils.createSubjectKeyIdentifier(
// csr.getSubjectPublicKeyInfo() ) );
// Authority Key Identifier
// byte[] caKeyEncoded = cacert.getPublicKey().getEncoded();
// SubjectPublicKeyInfo caSubjectPublicKeyInfo =
// SubjectPublicKeyInfo.getInstance( caKeyEncoded );
// certgen.addExtension( Extension.authorityKeyIdentifier, false,
// x509ExtensionUtils.createAuthorityKeyIdentifier( caSubjectPublicKeyInfo
// ) );
// Key Usage
// certgen.addExtension( Extension.keyUsage, false, new KeyUsage(
// KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign )
// );
ContentSigner signer = new BcRSAContentSignerBuilder( sigAlgId, digAlgId ).build( PrivateKeyFactory.createKey( cakey.getEncoded() ) );
// ContentSigner signer = new JcaContentSignerBuilder(
// "SHA1WithRSAEncryption" ).setProvider( "BC" ).build( cakey );
X509CertificateHolder holder = certgen.build( signer );
return new JcaX509CertificateConverter().setProvider( "BC" ).getCertificate( holder );
}
catch ( NoSuchAlgorithmException | KeyStoreException | CertificateException | OperatorCreationException | UnrecoverableKeyException | CertIOException e )
{
throw new APIException( API_ERROR_CODE.CRYPTOGRAPHY_ERROR, e );
}
catch ( IOException e )
{
throw new APIException( API_ERROR_CODE.IO_ERROR, e );
}
}
This runs through successfully. However, when I try to check the key using:
KeyStore ks = getKeyStore();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
trustManagerFactory.init( ks );
for ( TrustManager trustManager : trustManagerFactory.getTrustManagers() )
{
if ( trustManager instanceof X509TrustManager )
{
X509TrustManager x509TrustManager = (X509TrustManager) trustManager;
x509TrustManager.checkClientTrusted( new X509Certificate[] { certificate }, "RSA" );
}
}
...it fails with a CertificateException. Please note that I am using the VERY SAME keystore here, which means the CA key I am signing with is included therein. Why does this happen?
By the way, strangely enough, when I open the generated signed certificate using Windows's certificate viewer, it DOES show the issuing CA name but its entry does not show up in the certificate chain. It seems as if the CA root certificate were not present in the Windows trusted authirities list, but in fact it is also there.
Even stranger: if I sign the CSR using OpenSSL, the certificate chain looks OK. I also had the idea that the process of importing the CA key pair from OpenSSL to the Java keystore via PKCS12 as an intermediate format wasn't successful, but actually if I export the CA certificate from the Java keystore and open it with the Windows certificate viewer, it is shown as trusted...
UPDATE: For those familiar with ASN.1, here are two encoded certificates. One is made with BouncyCastle and is NOT trusted, the other one is signed by the same CA key with OpenSSL and that IS trusted. They can be decoded with a tool like this: ASN.1 decoder I would be very grateful is somebody could view this decoded data and tell me what could cause the difference between them.
This one is NOT trusted:
-----BEGIN CERTIFICATE-----
MIIC6TCCAlKgAwIBAgIESdsI/TANBgkqhkiG9w0BAQUFADCBgzEgMB4GCSqGSIb3
DQEJARYRdGVzdGNhQHRlc3RjYS5jb20xEDAOBgNVBAMMB1Rlc3QgQ0ExEDAOBgNV
BAsMB1Rlc3QgQ0ExEDAOBgNVBAoMB1Rlc3QgQ0ExDTALBgNVBAcMBFdpZW4xDTAL
BgNVBAgMBFdpZW4xCzAJBgNVBAYTAkFUMB4XDTE0MDUxOTExNTYwM1oXDTE1MDUx
OTExNTYwM1owajELMAkGA1UEBhMCVUsxCzAJBgNVBAgTAlBiMQswCQYDVQQHEwJC
cDETMBEGA1UEChMKZmdmZ2ZnZGZnZDEPMA0GA1UECxMGYWJjZGVmMRswGQYDVQQD
DBJwZXRlcnZlbG9zeV90aWdyaXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCdL7taENsONBazc2iMDV5nw9ACP5mevmnzPwOJRUcd5GlGgry/iSa3tTwL
l6Um3zNc4X0m5nVVskKeJE4dTvYFV3+vJlEKCra86yQfa6XkGllU4EG6SdG8lRhE
Btk1QbOQZKrUz77IdOWWOUvIsNxtDDQcUhnrSjSxHohdoe/yoCl+60RBdjrgUrRo
uctSHFPvVt2uZaVM2rAVovx56vvJHOag2++rcvXaOh9WHvdwRAIZt/4aOv2O4jdI
jKdRrmF8dOudjR89wIeVjX9fvyvx+hw+ZolUio9GOVKLlBcYno6lEupHLUDK9ECs
W8F6y65nYGlm9/0G0+gB7K1yy1dBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAKJpM
7AbkWBH3ho1YV0d1glJvefQ1xaXGpDfd+Tzf3+cR1o3+YxxEyuYvBbiQ/MBxKD9/
hsFCqEWzOfu2lAZ+/6uHvt7BCEGhaLdWKXehoaIw/kEMeISIUDFbKORCsKJNbYRB
xgqBXGglTQ4gVXMDRBxzOmButN31j1VDt55gvn4=
-----END CERTIFICATE-----
This one is TRUSTED, this has been generated using theoretically the same CA certificate but through OpenSSL:
-----BEGIN CERTIFICATE-----
MIIC+TCCAmICAhI4MA0GCSqGSIb3DQEBBQUAMIGDMQswCQYDVQQGEwJBVDENMAsG
A1UECAwEV2llbjENMAsGA1UEBwwEV2llbjEQMA4GA1UECgwHVGVzdCBDQTEQMA4G
A1UECwwHVGVzdCBDQTEQMA4GA1UEAwwHVGVzdCBDQTEgMB4GCSqGSIb3DQEJARYR
dGVzdGNhQHRlc3RjYS5jb20wHhcNMTQwNTE0MTkzMTAzWhcNMTUwNTA5MTkzMTAz
WjCBgDELMAkGA1UEBhMCSFUxETAPBgNVBAgTCEJ1ZGFwZXN0MREwDwYDVQQHEwhC
dWRhcGVzdDEWMBQGA1UEChMNTWVyY2hhbnQgVGVzdDEWMBQGA1UECxMNTWVyY2hh
bnQgVGVzdDEbMBkGA1UEAwwScGV0ZXJ2ZWxvc3lfdGlncmlzMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1vuY4MQ5b9Jb0MiyEuCrR4E+7VgmrvEwlswO
aMIF4H6i538PwPml5dbqx/3whxR/BcQJuJYWI/Hh7xxGS7FvSQ+DNhzxv9TpECKS
/5OZNm+JikPZwTiwrS/Cf4NP+ZcXOjtVZp6ngVtTarn3NC/J7gJVYaHVVO4NbUkt
kCYhdfCXg71QiJ42RWMjMC9tJFrrlfem+SVzh8yMtUCBKm7nbMjQ6LngawjTzDK8
2Zcdqwdzvt2pcYcsYSViO5j5t/r7rIDGjRkjJqRSEiJMOvn0W+sdTdmFoZbyj7Qe
pgyCyf28uFyCO9QZro337D8klPLXaWJOwPDXXiuYOTDYAjBVbwIDAQABMA0GCSqG
SIb3DQEBBQUAA4GBAGU60GVjR+2oEiJMSe1CKU7gf+bGuxaCxXQTzVQLU652i1sp
Fv56o6jnLtw46/rQydNKX4GBH022B/BDEPAQQiQv31YKQAoWtBZod0SRonogcx7p
AULacoma9QEgHSX0l+2yEn42/qo7o0pAmmewJlsCnHVIqI0eU8x1XbCEAf53
-----END CERTIFICATE-----
UPDATE 2:
Thanks to Bruno's answer, the certificate chain now looks OK and the following certificate is generated:
-----BEGIN CERTIFICATE-----
MIIC6TCCAlKgAwIBAgIEI2vbpTANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
QVQxDTALBgNVBAgMBFdpZW4xDTALBgNVBAcMBFdpZW4xEDAOBgNVBAoMB1Rlc3Qg
Q0ExEDAOBgNVBAsMB1Rlc3QgQ0ExEDAOBgNVBAMMB1Rlc3QgQ0ExIDAeBgkqhkiG
9w0BCQEWEXRlc3RjYUB0ZXN0Y2EuY29tMB4XDTE0MDUyMDA3MzkyMFoXDTE1MDUy
MDA3MzkyMFowajELMAkGA1UEBhMCVUsxCzAJBgNVBAgTAlBiMQswCQYDVQQHEwJC
cDETMBEGA1UEChMKZmdmZ2ZnZGZnZDEPMA0GA1UECxMGYWJjZGVmMRswGQYDVQQD
DBJwZXRlcnZlbG9zeV90aWdyaXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCdL7taENsONBazc2iMDV5nw9ACP5mevmnzPwOJRUcd5GlGgry/iSa3tTwL
l6Um3zNc4X0m5nVVskKeJE4dTvYFV3+vJlEKCra86yQfa6XkGllU4EG6SdG8lRhE
Btk1QbOQZKrUz77IdOWWOUvIsNxtDDQcUhnrSjSxHohdoe/yoCl+60RBdjrgUrRo
uctSHFPvVt2uZaVM2rAVovx56vvJHOag2++rcvXaOh9WHvdwRAIZt/4aOv2O4jdI
jKdRrmF8dOudjR89wIeVjX9fvyvx+hw+ZolUio9GOVKLlBcYno6lEupHLUDK9ECs
W8F6y65nYGlm9/0G0+gB7K1yy1dBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAIdFF
h6uLY7ioKQ3O0c4cZHHjRA0HTlWjih8P2xvXY/V9jF914BT7OW52UJ16tQaJlOf+
mAeeBDq9srKnkmOQp3mCejVnkyVZF8pOOzNbqSVzylt0Csg2twnxZ0NcM63Oda5b
YSQI8+arryxykLWkHWH8i/6rPCDCtbAHBo7fSeQ=
-----END CERTIFICATE-----
However, the TrustManager code above rejects it. If I circumvent the TrustManager and do something like this:
KeyStore ks = getKeyStore();
Enumeration<String> aliases = ks.aliases();
while ( aliases.hasMoreElements() )
{
String alias = aliases.nextElement();
Certificate currentCert = ks.getCertificate( alias );
try
{
certificate.verify( currentCert.getPublicKey() );
return true;
}
catch ( Exception e )
{
// the certificate cannot be verified with this key.
}
}
return false;
...it passes. Does anybody know why it fails on the TrustManager check?
P.s. the CA certificate looks like this:
-----BEGIN CERTIFICATE-----
MIICfzCCAegCCQCU+Ah6M5qQGTANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
QVQxDTALBgNVBAgMBFdpZW4xDTALBgNVBAcMBFdpZW4xEDAOBgNVBAoMB1Rlc3Qg
Q0ExEDAOBgNVBAsMB1Rlc3QgQ0ExEDAOBgNVBAMMB1Rlc3QgQ0ExIDAeBgkqhkiG
9w0BCQEWEXRlc3RjYUB0ZXN0Y2EuY29tMB4XDTE0MDQyMzA3MjYzNFoXDTI0MDQy
MDA3MjYzNFowgYMxCzAJBgNVBAYTAkFUMQ0wCwYDVQQIDARXaWVuMQ0wCwYDVQQH
DARXaWVuMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQLDAdUZXN0IENBMRAwDgYD
VQQDDAdUZXN0IENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0Y2FAdGVzdGNhLmNvbTCB
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAldKTo8iqF52dsOwln0Oppu+ODiaG
R4T7Znrca4Cs5FBQOmuMwqUP6ilW115p/WvkBHhm8dZyVACPKdshEfhh4VFAW5r2
mJnosYgjafQpTEv83sc938DwtK6iikZ0uvdBJKG/IuYblNq9TPMLFeTYjD8mgf9j
m6JOvA/Q9J4nRW0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQB8ACYeC+zjV/KqxPr1
cyzfJP9xfUnxDTEKUJS2YVuxJqpfbHeUtvKoN89BfY07XWdnj8cgMDfJp10Kdc2A
clwP2lVDtOgHZS07UUW98q9FKQ33mLHIn0nDKNwTo5VH8t/NJVeMFuZPAbFiI2gj
KH2sTU2GNNvKC4jHh0PS+OZFtg==
-----END CERTIFICATE-----
If you look at the Issuer DN in your two certificates, they don't match (output from openssl x509 -text):
Issuer: C=AT, ST=Wien, L=Wien, O=Test CA, OU=Test CA, CN=Test CA/emailAddress=testca#testca.com
and
Issuer: emailAddress=testca#testca.com, CN=Test CA, OU=Test CA, O=Test CA, L=Wien, ST=Wien, C=AT
As a result, it's not going to be able to match the wrong issuer to the CA's Subject DN.
Unfortunately, X500Name issuer = new X500Name(cacert.getSubjectX500Principal().getName()) doesn't do what you'd expect. The order of the RDNs is reversed. Generally, re-building the DN from a string representation can fail, since there are different ways of serialising the ASN.1 representation into a string. Java's X500Principal has multiple formats available for getName(...) and it even provides a way to provide your own OID to string maps (for more obscure OIDs). The way emailAddress is separated can also cause problems (notice the way it's separated with a comma or with a slash).
Instead, build the X500Name from the encoded form, this should always work:
X500Name x500Name = X500Name.getInstance(cert
.getSubjectX500Principal().getEncoded());

Creating a Key Pair Certificate and Signing It with External CA using BouncyCastle

Here what I have so far generating a Certificate for a User
try {
Security.addProvider(new BouncyCastleProvider()); // adding provider
// to
String pathtoSave = "D://sureshtest.cer";
KeyPair keyPair = generateKeypair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
X509Certificate trustCert = createCertificate(null, "CN=CompanyName",
"CN=Owner", publicKey, privateKey);
java.security.cert.Certificate[] outChain = { trustCert, };
trustCert.checkValidity();
KeyStore outStore = KeyStore.getInstance("PKCS12");
outStore.load(null, null);
outStore.setKeyEntry("my own certificate", privateKey,
"admin123".toCharArray(), outChain);
OutputStream outputStream = new FileOutputStream(pathtoSave);
outStore.store(outputStream, "admin123".toCharArray());
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
The above code generate a certificate with a private and public key.
Now I want to sign that certificate with a signing certificate I've been issued by a certificate authority (CA). After that I'll grant that certificate to user.
I got some input from here and it seems that is not the required answer with my case.
No need for a full implementation, just a valid procedure or some hints will greatly help.
You need to generate a CSR so you can invoke the code from Sign CSR using Bouncy Castle which is using the BC API. Add this to your code above:
final PKCS10 request = new PKCS10(publicKey);
final String sigAlgName = "SHA1WithRSA"; // change this to SHA1WithDSA if it's a DSA key
final Signature signature = Signature.getInstance(sigAlgName);
signature.initSign(privateKey);
final X500Name subject = new X500Name(trustCert.getSubjectDN().toString());
final X500Signer signer = new X500Signer(signature, subject);
// Sign the request and base-64 encode it
request.encodeAndSign(signer);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream writer = new PrintStream(baos);
request.print(writer);
// Remove -----BEGIN NEW CERTIFICATE REQUEST----- and -----END NEW CERTIFICATE REQUEST-----
final String requestBase64 = new String(baos.toByteArray());
String withoutTags = requestBase64.substring(41);
withoutTags = withoutTags.substring(0, withoutTags.length() - 39);
// org.bouncycastle.pkcs.PKCS10CertificationRequestHolder
final PKCS10CertificationRequest holder = new PKCS10CertificationRequest(Base64.decode(withoutTags));
// Feed this into https://stackoverflow.com/questions/7230330/sign-csr-using-bouncy-castle

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