convert CertStore into X509Certificate[ ] array JAVA - java

I made a cert store and want to add another certificate from another function in this class.
Make chain in class 1 with caCert and intermediateCert:
List<X509Certificate> certList = new ArrayList<X509Certificate>();
certList.add(0, interCert);
certList.add(1, caCert);
Store<X509Certificate> certs = new JcaCertStore(certList);
certStore = new JcaCertStoreBuilder().addCertificates(certs).build();
I want to add a new build user certificate in class 2:
certStore = new JcaCertStoreBuilder().addCertificate(certHolder).build();
To save the new key to my KeyStore I need an array of the certificates (X509Certificate[]). How can I convert the certStore file to an array to get the privatekeyentry working?
PrivateKeyEntry privKeyEntry = new PrivateKeyEntry(pair.getPrivate(), chain);
store.setEntry(alias, privKeyEntry, new KeyStore.PasswordProtection(storePassword));

I'm using BouncyCastle 1.56 and JDK 1.7.
I think the easiest way is to get all the certificates from the cert store and add them to an array (instead of creating another cert store and converting).
To get all the certificates in the cert store, you can do:
// get all certificates in certStore
Collection<? extends Certificate> allCerts = certStore.getCertificates(null);
Some implementations don't accept the null argument. In this case, you must create a selector like this (using java.security.cert.X509CertSelector class):
Collection<? extends Certificate> allCerts = certStore.getCertificates(new X509CertSelector() {
#Override
public boolean match(Certificate cert) {
// match all certificates (so it'll return all of them)
return true;
}
});
After that, allCerts will have the 2 certificates that are in certStore.
Now you create your array and add all the certificates you need:
// create array
X509Certificate[] certificatesArray = new X509Certificate[3];
// add certificates in allCerts (the 2 that were in certStore)
int i = 0;
for (Certificate c : allCerts) {
certificatesArray[i] = (X509Certificate) c;
i++;
}
// add the new certificate (newCert being a X509Certificate)
certificatesArray[2] = newCert;
Note:
If your new certificate's type is a org.bouncycastle.cert.X509CertificateHolder, you can convert it to a java.security.cert.X509Certificate using the org.bouncycastle.cert.jcajce.JcaX509CertificateConverter class:
X509CertificateHolder certHolder = ...;
X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certHolder);
Or you can do it manually with a java.security.cert.CertificateFactory:
X509CertificateHolder certHolder = ...;
CertificateFactory f = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) f.generateCertificate(new ByteArrayInputStream(certHolder.getEncoded()));

Related

Not able to store multiple alias in same Keystore using private key and Certificate in java

void createKeyStore(String alias) throws Exception {
if(keyStoreType == null || keyStoreType.isEmpty()){
keyStoreType = KeyStore.getDefaultType();
}
keyStore = KeyStore.getInstance(keyStoreType);
KeyPair kp = generateRSAKeyPair();
Certificate[] certChain = new Certificate[1];
certChain[0] = generateCertificate(kp);
//load
char[] pwdArray = keyStorePassword.toCharArray();
keyStore.load(null, pwdArray);
keyStore.setKeyEntry(alias, kp.getPrivate(), keyStorePassword.toCharArray(), certChain);
// Save the keyStore
FileOutputStream fos = new FileOutputStream(keyStoreName,true);
keyStore.store(fos, pwdArray);
fos.close();
}
Using this function for create and write multiple alias in keystore
In the first request it create keystore file and store the alias with private key and certificate but in the second request with alias it not storing any data in same keystore file.
I am working on a Java project. I have created certificate using my function but now I have only one certificate . I am expecting to store multiple alias in the same file but it happen only with first request but not with second request.

Use of isKeyEntry(alias) in KeyStore - Java

I wanted to load the keystore file and get the details of its root SSL Certificate.
Link to Sample
I saw the code in the above link. The code is also visible below. What is the use of keyStore.isKeyEntry(alias)?
I check JavaDocs and it says
Returns true if the entry identified by the given alias was created by a call to setKeyEntry, or created by a call to setEntry with a PrivateKeyEntry or a SecretKeyEntry.
So, What is the use of it?
boolean isAliasWithPrivateKey = false;
KeyStore keyStore = KeyStore.getInstance("JKS");
// Provide location of Java Keystore and password for access
keyStore.load(new FileInputStream(jksPath), jksPassword.toCharArray());
// iterate over all aliases
Enumeration<String> es = keyStore.aliases();
String alias = "";
while (es.hasMoreElements()) {
alias = (String) es.nextElement();
// if alias refers to a private key break at that point
// as we want to use that certificate
if (isAliasWithPrivateKey = keyStore.isKeyEntry(alias)) {
break;
}
}
if (isAliasWithPrivateKey) {
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias,
new KeyStore.PasswordProtection(jksPassword.toCharArray()));
PrivateKey myPrivateKey = pkEntry.getPrivateKey();
// Load certificate chain
Certificate[] chain = keyStore.getCertificateChain(alias);
certDetails = new CertificateDetails();
certDetails.setPrivateKey(myPrivateKey);
certDetails.setX509Certificate((X509Certificate) chain[0]);
}
You have 3 types of entries in a java keystore:
privateKeyEntry, private key and associated certificate chain
trustedKeyEntry, a trusted certificate (e.g. certificate from CA like Verisign, GoDaddy ...)
secretKeyEntry, a encryption key (e.g. a symmetric key AES)
as described in the java docs, KeyStore.isKeyEntry(alias) returns true is you have secret or a private key.
The code example will fail if the entry is a secret key:
`KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(jksPassword.toCharArray()));`
it would be preferable to use:
`KeyStore.entryInstanceOf(alias , KeyStore.PrivateKeyEntry.class)`

Getting root and intermediate certificates from an end-entity

still being a noob in cryptography I stumble upon simple things every day. And today is just one of those days.
I want to validate smime messages in java with the bouncy castle library, and I think I almost figured it out, but the problem at this moment is the building of the PKIXparameters object.
Let's say, I have an end-entity x509certificate with the following structure:
root certificate
+->intermediate certificate
+->end-entity certificate
In order to validate message I need to build a chain of trust first, but I cannot figure out how to extract a root and intermediate certificates from the end-entity.
I tried to use end-entity as root but it didn't work:
InputStream isCert = GetFISCertificate();
List list = new ArrayList();
X509Certificate rootCert = (X509Certificate) certificateFactory.generateCertificate(isCert);
list.add(rootCert);
CollectionCertStoreParameters params = new CollectionCertStoreParameters(list);
CertStore store = CertStore.getInstance("Collection", params, BC);
//create cert path
List certChain = new ArrayList();
certChain.add(rootCert);
CertPath certPath = certificateFactory.generateCertPath(certChain);
Set trust = Collections.singleton(new TrustAnchor(rootCert, null));
//validation
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", BC);
PKIXParameters pKIXParameters = new PKIXParameters(trust);
pKIXParameters.addCertStore(store);
pKIXParameters.setDate(new Date());
try {
CertPathValidatorResult result = certPathValidator.validate(certPath, pKIXParameters);
System.out.println("certificate path validated");
} catch (CertPathValidatorException e) {
System.out.println("validation failed on certificate number " + e.getIndex() + ", details: " + e.getMessage());
}
Got this exception:
validation failed on certificate number -1, details: Trust anchor for certification path not found.
And btw, can I just use only the end-entity certificate to validate messages, as if it were the self-signed certificate?
I've used BouncyCastle 1.56 for this test.
One way to get the issuer's certificate from the end entity is to look for the Authority Information Access extension.
This extension may be present (it's not mandatory) and may contain the URL to get the issuer's certificate (issuer is the certificate "above" the current one, so the end entity's issuer is the intermediate, and the intermediate's issuer is the root).
You can get this extension value with BouncyCastle:
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
X509Certificate cert = // end entity certificate
// get Authority Information Access extension (will be null if extension is not present)
byte[] extVal = cert.getExtensionValue(Extension.authorityInfoAccess.getId());
AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(X509ExtensionUtil.fromExtensionValue(extVal));
// check if there is a URL to issuer's certificate
AccessDescription[] descriptions = aia.getAccessDescriptions();
for (AccessDescription ad : descriptions) {
// check if it's a URL to issuer's certificate
if (ad.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_caIssuers)) {
GeneralName location = ad.getAccessLocation();
if (location.getTagNo() == GeneralName.uniformResourceIdentifier) {
String issuerUrl = location.getName().toString();
// http URL to issuer (test in your browser to see if it's a valid certificate)
// you can use java.net.URL.openStream() to create a InputStream and create
// the certificate with your CertificateFactory
URL url = new URL(issuerUrl);
X509Certificate issuer = (X509Certificate) certificateFactory.generateCertificate(url.openStream());
}
}
}
So you can use this code with the end entity certificate to get the intermediate. Then you use it again with the intermediate to get the root.
Then you add the root to your TrustAnchor and the validation should work.
Note: But as I said, this extension is not mandatory and may not be present. In this case, getExtensionValue will return null, and the only alternative that I know is to search for the certificates in google and download them (those certificate chains are usually public and not hard to find)
btw, if we have out certificate installed in windows, everything is much simpler:
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
String alias = "your alias";
ArrayList<X509Certificate> certsChain = new ArrayList<>();
if (ks.isCertificateEntry(alias)) {
Certificate[] chain = ks.getCertificateChain(alias);
System.out.println("Chain length: " + chain.length);
for(Certificate c : chain) certsChain.add((X509Certificate)c);
}
Collections.reverse(certsChain);
certsChain.forEach(MainClass::printDBG);
boom, and the whole certificates chain is ready

Adding Certificates to CMS Signed Data

I am currently using the java Bouncy Castle libraries in order to create CMS signed data (or PKCS7 signed data). I seem however to be stuck with adding certificates (even though the certificate signer is properly added).
I checked out this question about properly signing data, but it didn't respond the needs of my SCEP server. The code I used was from EJBCA but doesn't seem to add certificates to the PKCS7 signed data.
When I parse the signed data with the openssl cms tool, I see that the "certificates" field is "EMPTY". Additionally, when I try to the print the certs with openssl pkcs7 [...] -print_certs, I get nothing.
Here is how I sign my data with Bouncy Castle (it's a lot code but enough to reproduce the issue):
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
CMSTypedData msg;
List<X509Certificate> certList = new ArrayList<>();
// Make sure the certificate is not null
if (this.certificate != null) {
certList.add((X509Certificate) this.certificate);
}
/**
* Create the signed CMS message to be contained inside the envelope
* this message does not contain any message, and no signerInfo
**/
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Collection<JcaX509CertificateHolder> x509CertificateHolder = new ArrayList<>();
try {
for (X509Certificate certificate : certList) {
x509CertificateHolder.add(new JcaX509CertificateHolder(certificate));
}
CollectionStore<JcaX509CertificateHolder> store = new CollectionStore<>(x509CertificateHolder);
gen.addCertificates(store);
} catch (Handle all exceptions) {}
This snippet of code above should normally add certificates. I took this from EJBCA.
Here is how I complete the signed data:
CMSSignedDataGenerator gen1 = new CMSSignedDataGenerator();
// I add ALL of my attributes here
// Once they're added...
Certificate caCert = this.caCertificate;
try {
String provider = BouncyCastleProvider.PROVIDER_NAME;
ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithmName).
setProvider(provider).
build(signerKey);
JcaDigestCalculatorProviderBuilder calculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder().
setProvider(provider);
JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(calculatorProviderBuilder.build());
builder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(attributes)));
gen1.addSignerInfoGenerator(builder.build(contentSigner, (X509Certificate) ca));
} catch (Handle all exceptions) {}
// Create the signed data
CMSSignedData sd = gen1.generate(msg, true);
byte[] results = sd.getEncoded();
The bytes array results is the DER formatted PKCS7 signed data... but no certificate is added.
Am I missing something? Thank you for your help!
The CMSSignedDataGenerator gen1 has to explicitly add the certificate, which I wasn't aware of.
It can simply be done by:
Adding the certificates to a List of X509Certificates;
Converting that List into a Collection of JcaX509CertificateHolder;
Adding this collection to a CollectionStore of JcaX509CertificateHolder;
Adding the store the CMSSignedDataGenerator.
Code sample:
CMSSignedDataGenerator gen1 = new CMSSignedDataGenerator();
List<X509Certificate> certificates = new ArrayList<>();
// I chose to add the CA certificate
certificates.add((X509Certificate) this.caCertificate);
// In this case, this is a certificate that I need to add
if (this.certificate != null)
certificates.add((X509Certificate) this.certificate);
// This is the recipient certificate
if (this.recipientCert != null)
certificates.add((X509Certificate) this.recipientCert);
Collection<JcaX509CertificateHolder> x509CertificateHolder = new ArrayList<>();
// Of course, we need to handle the exceptions...
for (X509Certificate certificate : certificates) {
x509CertificateHolder.add(new JcaX509CertificateHolder(certificate));
}
CollectionStore<JcaX509CertificateHolder> store = new CollectionStore<>(x509CertificateHolder);
// The final stage.
gen1.addCertificates(store);
Hope this helps anyone in the future.

Verifying PKCS#7 certificates in Java

Need some help with crypto routines in Java.
Given a PKCS#7 signature, I want to verify all certificates it contains against a trusted store. I assume that all certificates contained in signature are in the correct order to form a valid certificate path (or chain, whatever), so that
topmost (#0) is a signing certificate;
next one (#1) is an intermediate certificate, used to sign #0;
next one (#2) is another intermediate certificate, used to sign #1;
and so on.
The last certificate (#N) is signed by CA.
That's what I've managed to hack so far:
// Exception handling skipped for readability
//byte[] signature = ...
pkcs7 = new PKCS7(signature); // `sun.security.pkcs.PKCS7;`
// *** Checking some PKCS#7 parameters here
X509Certificate prevCert = null; // Previous certificate we've found
X509Certificate[] certs = pkcs7.getCertificates(); // `java.security.cert.X509Certificate`
for (int i = 0; i < certs.length; i++) {
// *** Checking certificate validity period here
if (cert != null) {
// Verify previous certificate in chain against this one
prevCert.verify(certs[i].getPublicKey());
}
prevCert = certs[i];
}
//String keyStorePath = ...
KeyStore keyStore = KeyStore.getInstance("JKS"); // `java.security.KeyStore`
keyStore.load(new FileInputStream(keyStorePath), null);
// Get trusted VeriSign class 1 certificate
Certificate caCert = keyStore.getCertificate("verisignclass1ca"); // `java.security.cert.Certificate`
// Verify last certificate against trusted certificate
cert.verify(caCert.getPublicKey());
So the question is -- how can this be done using standard Java classes like CertPath and friends? I have a strong feeling I'm re-inventing a bicycle. Or, if someone has an example with BouncyCastle library, that would also be fine.
Bonus question: how to verify a certificate against a trusted store so that root certificate is selected automatically?
Found the solution myself. So, here's how one can extract and validate a certificate chain against the trusted store (exception handling skipped for readability):
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// Get ContentInfo
//byte[] signature = ... // PKCS#7 signature bytes
InputStream signatureIn = new ByteArrayInputStream(signature);
DERObject obj = new ASN1InputStream(signatureIn).readObject();
ContentInfo contentInfo = ContentInfo.getInstance(obj);
// Extract certificates
SignedData signedData = SignedData.getInstance(contentInfo.getContent());
Enumeration certificates = signedData.getCertificates().getObjects();
// Build certificate path
List certList = new ArrayList();
while (certificates.hasMoreElements()) {
DERObject certObj = (DERObject) certificates.nextElement();
InputStream in = new ByteArrayInputStream(certObj.getDEREncoded());
certList.add(cf.generateCertificate(in));
}
CertPath certPath = cf.generateCertPath(certList);
// Load key store
//String keyStorePath = ...
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(keyStorePath), null);
// Set validation parameters
PKIXParameters params = new PKIXParameters(keyStore);
params.setRevocationEnabled(false); // to avoid exception on empty CRL
// Validate certificate path
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
CertPathValidatorResult result = validator.validate(certPath, params);
validate() will throw an exception if validation fails.
Docs: ASN1Set, ContentInfo, SignedData. All other exotic names and related docs can be found in java.security.cert.
No SUN-dependencies here, only BouncyCastle provider library is needed.
This question (and especially an answer) may help too.
You want CertificateFactory. The last example in the javadocs do exactly what you want.

Categories