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)`
Related
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.
I would like to save a X509Certificate and its private key into the Android KeyStore, I tought I should 'merge' the X509Certificate (containing the public key) and its private key. The private key is used to create a CSR and then a server party sign the certificate and return to the application, can I merge the cert and the private key into one unique cert? Also I'm using spongycastle (aka bouncycastle's android wrapper).
I have no idea about Android KeyStore, but maybe you can try something like:
PrivateKey privateKey = ... //this is what you already have
X509Certificate certificate = ... //this is what you already have
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Certificate[] certChain = new Certificate[1];
certChain[0] = certificate;
char[] myKeyPassword = "myKeyPassword".toCharArray();
keyStore.setKeyEntry("mykeyalias", (Key)privateKey, myKeyPassword, certChain);
See https://docs.oracle.com/javase/9/docs/api/java/security/KeyStore.html#setKeyEntry-java.lang.String-java.security.Key-char:A-java.security.cert.Certificate:A- for more information about KeyStore.setKeyEntry
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
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.
I have the following code to export certificates and keys from a keystore, that I created with keytool in Windows:
final KeyStore keystore = KeyUtil.loadKeystore("keystore.jks", "pass");
UserInfo userinfo = new UserInfo(WSusername, WSpassword);
X509Certificate clientcert = KeyUtil.getCertificate(CLIENT_KEY_ALIAS, keystore);
X509Certificate servercert = KeyUtil.getCertificate(SERVER_KEY_ALIAS, keystore);
PrivateKey clientprivate = KeyUtil.getPrivateKey(CLIENT_KEY_ALIAS, CLIENT_KEY_PASSWORD, keystore);
Yet it fails at the last line with "unable to retrieve private key for signing"
It can retrieve the clientcert, but when it tries clientprivate, it fails.
My problem was that when I generated the keystore with keytool, it did not create the PrivateKey as a key entry. To fix this, I imported the .p12 keystore as the private key, which worked. My original code above then worked.