Suppose, there is an external CA. Let's say there is an URL and other config parameters.
In runtime I obtain a chain of certificates I need to validate using the CA.
So root certificate of the chain must be registered somehow in the CA. Also the CA handles CRL and some of the certificates in the chain could be revoked.
I have code like this
CertificateFactory cf = CertificateFactory.getInstance(certificateType);
List<Certificate> certx = //list of certificates obtaining
//creating certification path (chain to be validated)
CertPath path = cf.generateCertPath(certx);
//validator creation. Algorithm to be defined
CertPathValidator validator = CertPathValidator.getInstance(certPathValidatorAlgorithm);
//get key store
KeyStore keystore = getKeyStore(keyStorePassword);
//get list of CRLs (certificate revoke list)
Collection<? extends CRL> crls = getCrls(cf);
PKIXParameters params = new PKIXParameters(keystore);
CertStore store = CertStore.getInstance(certStoreType, new CollectionCertStoreParameters(crls));
params.addCertStore(store);
//Validate will throw an exception on invalid chains.
PKIXCertPathValidatorResult r = (PKIXCertPathValidatorResult) validator.validate(path, params);
r.getTrustAnchor();
In case of local cacert and CRLs the logic works fine but the CA must be external.
So the getKeyStore() and getCrls() must be customized to access external CA server, establish trusted connection etc. etc.
Could you explain what config and protocols (CMP ?) to be used to support this? Is there something like this in e.g. BouncyCastle?
UPDATE: In fact it's not related to browsers and https at all.
In an XML we get a base64 encoded certificates chain. The certificates were issued by a CA. We need to validate the chain - send it to CA somehow and get Ok/Not Ok response or get a list of CRLs from the CA and provide mechanism to keep valid issued certificates as roots for our chains.
Related
I am receiving the following String from a certificate stored in Azure Key Vault. I am using the Secret API in order to retrieve both the certificate and the private key related to this cert.
Initially the certificate was uploaded using a .pfx file to Azure Key vault. Now I need to create a Certificate and a PrivateKey to allow client authentication to a 3rd party system and I am using the given String retrieved from the API, however I am note sure how to get around that in Java.
I took some hints from this link in C# however I am pretty certain that this method doesn't work like that in Java. In particular an X509Certificate or a Certificate in general doesn't hold any information about the PrivateKey in Java, unlike C#, and I am not sure how to extract that information from given String in Java.
This works as expected to retrieve the certificate from the String retrieved from the API
String secret = azureSecret.getValue();
byte[] certkey = Base64.getDecoder().decode(secret);
ByteArrayInputStream inputStream = new ByteArrayInputStream(certkey);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(inputStream);
The azureSecret.getValue() format is like the following however I am not sure how to get PrivateKey out of the given String
MIIKvgIBaaZd6Euf3EYwYdHrIIKYzCC...
YES, Java X509Certificate and Certificate is only the certificate. Instead use KeyStore which can contain multiple entries each of which is either a 'trusted' certificate (for someone else), or a privatekey plus certificate plus other chain cert(s) (if applicable) for yourself, or (not relevant here) a 'secret' (symmetric) key. PKCS12 is supported as one type of KeyStore along with others not relevant here, so after the base64-decoding you already have do something like:
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inputstreamfromvaultvalue, password);
// then
PrivateKey pkey = (PrivateKey) ks.getKey(alias, password);
// and
Certificate cert = ks.getCertificate(alias); // if you only need the leaf cert
// or
Certificate[] chain = ks.getCertificateChain(alias); // usually
But if you want to do client authentication in TLS/SSL (including HTTPS), you give the JSSE KeyManager the whole keystore object not the individual pieces (privatekey and certificates). Similarly to verify the peer in TLS/SSL, you give TrustManager a keystore containing trusted certificates, usually root CAs and often defaulted to a built-in set of public root CAs.
I have a document that is digitally signed using CAdES. I use BouncyCastle APIs to get the X509Certificate[] instances of the signers, but let's assume the list contains one and one only element.
I need to verify whether this certificate is trusted or not at today's date, and I don't want to use the system's standard trust store which is normally used to trust SSL certificates. No, I want to build my own trust list with a list of .cer files in my classpath. At the moment, a single CA is trusted but obviously in the future a few more certificates may be added.
So far I have read this and tried to implement in my code. I need no SSLContext, I need to check the validity of a digitally signed document. I am now confused.
X509TrustManager APIs do however provide only methods for validating client/server certificates, but mine only have digital signature/non-repudiation usage flags.
The questions can be formulated in two ways that lead to the same:
How does one in Java check the validity of an X509Certificate instance against a custom root CA list that can be loaded in memory?
How does one check that a digitally-signed document is signed with a certificate deriving from a known CA of a custom list?
Extract from the CAdES signature for each signer the signer's certificate and also the intermediate certificates as a X509Certificate list. Build also a set with all root CA certificates
Then you can use this (slightly adapted) example code to verify and build the certification chain using Java and BouncyCastle. It will return the certification chain if verification is successful
public PKIXCertPathBuilderResult verifyCertificateChain(
X509Certificate cert,
Set<X509Certificate> trustedRootCerts,
Set<X509Certificate> intermediateCerts) throws GeneralSecurityException {
// Create the selector that specifies the starting certificate
X509CertSelector selector = new X509CertSelector();
selector.setCertificate(cert);
// Create the trust anchors (set of root CA certificates)
Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
for (X509Certificate trustedRootCert : trustedRootCerts) {
trustAnchors.add(new TrustAnchor(trustedRootCert, null));
}
// Configure the PKIX certificate builder algorithm parameters
PKIXBuilderParameters pkixParams =
new PKIXBuilderParameters(trustAnchors, selector);
// Disable CRL checks (this is done manually as additional step)
pkixParams.setRevocationEnabled(false);
// Specify a list of intermediate certificates
// certificate itself has to be added to the list
intermediateCerts.add(cert);
CertStore intermediateCertStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(intermediateCerts), "BC");
pkixParams.addCertStore(intermediateCertStore);
// Build and verify the certification chain
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
PKIXCertPathBuilderResult result =
(PKIXCertPathBuilderResult) builder.build(pkixParams);
return result;
}
If you do not want to deal with CAdES complexity, I suggest to use SD-DSS open-source project
I'm reading the certificate file with the following methods :
File certFile = new File("E:/mycert.cer");
InputStream stream = new FileInputStream(certFile);
CertificateFactory factory = CertificateFactory.getInstance("X509");
X509Certificate certificate = (X509Certificate) factory.generateCertificate(stream);
how to get the root certificate of the certificate with java programming.?
How to get sub-root certificates in the certificate?
You can't. They aren't in the certificate. You need to:
Have a stream that contains a certificate chain, not just a single certificate, and
Call generateCertificates() instead of generateCertificate(), so as to get a Collection<? extends Certificate>, or
Call generateCertPath() so as to get a CertPath, which contains an ordered List of Certificate, from which you can get the root, the leaf, and the intermediates.
I have an XML document that is digitally signed. I use the XML Digital Signature API to verify the signature. But this document is a SAML 2.0 Assertion that will be used for single sign-on into our web application. As such I need to establish trust in the X.509 certificate used to sign the XML document.
The code I am using to try and establish this trust is:
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
PKIXParameters params = new PKIXParameters(keystore);
params.setRevocationEnabled(false);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
CertPath certPath = certFactory.generateCertPath(Arrays.asList(signatureCertificate));
CertPathValidator certPathValidator = CertPathValidator.getInstance(CertPathValidator.getDefaultType());
CertPathValidatorResult result = certPathValidator.validate(certPath, params);
PKIXCertPathValidatorResult pkixResult = (PKIXCertPathValidatorResult) result;
TrustAnchor ta = pkixResult.getTrustAnchor();
X509Certificate cert = ta.getTrustedCert();
When run the call to certPathValidator.validate() throws a CertPathValidatorException with the message Path does not chain with any of the trust anchors.
Examining the certificate, it says it has been issued by OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY LTD.(c)97 VeriSign,OU=VeriSign International Server CA - Class 3,OU=VeriSign\, Inc.,O=VeriSign Trust Network.
This is not one of the trust anchors in the JDK's cacerts key store.
However, using IE to inspect a similar certificate's trust chain I see that www.verisign.com/CPS Incorp.'s was issued by VeriSign Class 3 Public Primary CA, which does appear to be one of the trust anchors in the JDK's cacerts key store.
My question: how can I get Java to validate this certificate?
Insert the public cert of the issuing CA into the cacert keystore.
edit: You can use keytool or one of the other tools out there. Article describes keytool use: keytool-Key and Certificate Management Tool
Exactly what xelco said - add the intermediate CA:
OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY LTD.(c)97 VeriSign,OU=VeriSign International Server CA - Class 3,OU=VeriSign\, Inc.,O=VeriSign Trust Network
To the JDK's key store. You can use keytool to do it.
Reason: Generally when an XML message is signed, the signature includes only the signing cert. X509 Certificates are like singly linked lists. The End Entity points to its issuer. The issuer points to its issuer until you get to a self-signed Root CA, which points to itself. To verify a certificate according to PKIX, the validator needs to be able to build the whole CA chain from end entity to self signed root, so every part of the chain (except for the end entity) must be in your certificate store.
I've got better solution. I've found java service that can be run and do everything for us.
Java:
http://code.google.com/p/java-use-examples/source/browse/trunk/src/com/aw/ad/util/InstallCert.java
through the bouncycastle wiki page I was able to understand how to create a X.509 root certificate and a certification request, but I do not quite understand how to proceed concept- and programming wise after that.
Lets assume party A does a cert request and gets his client certificate from the CA. How can some party B validate A's certificate? What kind of certificate does A need? A root certificate? A 'normal' client certificate?
And how does the validation work on programming level, if we assume that A has successfully send his certificate in DER or PEM format to B?
Any help is much appreciated.
Best Regards,
Rob
From a programmer's perspective, you need a few things to validate an X.509 certificate.
A set of "trust anchors"—the root certificates of CAs that you rely on. These should be protected from tampering, so that an attacker doesn't replace a CA certificate with his own fake. The public keys in these certificates are used to verify the digital signatures on other certificates.
A collection of Intermediate certificates. The application might keep a collection of these, but most protocols, like SSL and S/MIME, that use certificates have a standard way to provide extra certificates. Storing these doesn't require any special care; their integrity is protected by the signature of a root CA.
Revocation information. Even if a certificate was issued by a CA, it might have been revoked prematurely because the private key was disclosed, or the end entity changed their identity. (For example, a person switches jobs and a certificate with their old company's name in it is revoked.) CRLs or a web-service like OCSP can be used to get an update about the status of a certificate.
With these inputs available, you can use the built-in PKIX support to construct and validate a certificate path.
/* Givens. */
InputStream trustStoreInput = ...
char[] password = ...
List<X509Certificate> chain = ...
Collection<X509CRL> crls = ...
/* Construct a valid path. */
KeyStore anchors = KeyStore.getInstance(KeyStore.getDefaultType());
anchors.load(trustStoreInput, password);
X509CertSelector target = new X509CertSelector();
target.setCertificate(chain.get(0));
PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, target);
CertStoreParameters intermediates = new CollectionCertStoreParameters(chain)
params.addCertStore(CertStore.getInstance("Collection", intermediates));
CertStoreParameters revoked = new CollectionCertStoreParameters(crls);
params.addCertStore(CertStore.getInstance("Collection", revoked));
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
/*
* If build() returns successfully, the certificate is valid. More details
* about the valid path can be obtained through the PKIXBuilderResult.
* If no valid path can be found, a CertPathBuilderException is thrown.
*/
PKIXBuilderResult r = (PKIXBuilderResult) builder.build(params);
An important thing to note is that if a path cannot be found, you don't get much information about the reason. This can be frustrating, but it is that way by design. In general, there are many potential paths. If they all fail for different reasons, how would the path builder decide what to report as the reason?
Ok, the idea behind CAs is as follows:
CAs are people everyone trusts. To this end, a selection of Trusted CAs is available in your browser/email client/even on my mobile. In your case, your public root key (certificate) should be in your application.
Users send requests to the CA for a certificate in PEM format with the public key. CAs do some (I leave this ambiguous deliberately) form of verification of the end user, such as charging them money or in the case of enhanced verification (green) certs, background checks.
If the CA doesn't think the user's request is valid, they communicate this somehow.
If they do, they sign the public key and produce a certificate containing this information. This is where you process the cert-req and turn it into an X.509 cert.
Other users come across our fictitious user and want to know if they can trust them. So, they take a look at the certificate and find it is digitally signed by someone they have in their trust list. So, the fact that they trust the root CA and only the root CA could sign (via their private key) this user's public key and the CA trusts the user, we deduce that the new user can trust mr fictitious.
On a programmatic level, you implement this by reading the X.509 certificate and working out who the CA is supposed to be. Given that CA's fingerprint, you find it in your database and verify the signature. If it matches, you have your chain of trust.
This works because, as I've said, only the CA can create the digital signature but anyone can verify it. It is exactly the reverse of the encryption concept. What you do is "encrypt with the private key" the data you wish to sign and verify that the "decrypt with the public key" equals the data you've got.