java.lang.NullPointerException Error during the SecurityHelper.prepareSignatureParams() OpenSAML call [duplicate] - java

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I have hit a dead end using the OpenSAML support for preparing a SAML Payload to accomplish a SSO transaction with another service that I am working with. I receive a NullPointerException that is thrown after I use the SecurityHelper.prepareSignatureParams() routine. I have a Stacktrace, but it wold be pretty ugly to append.
Let me first say what I was able to do...
For the purposes of learning the technology and to make sure it would work, I was able to successfully build a SAML payload, sign it using a Certificate and a Private Key that was stored in a Java Key Store file that I created locally on my workstation using the Keytool program with the -genkeypair option.
As I understand things, my JKS file contains a Self Signed Certificate and a Private Key. I was able to open the JKS file, gather the Certificate and the Private Key to build a Signing Certificate. The Signing Certificate was used to sign the SAML Payload that I created You'll see how I did this if you look at the code samples that I'll append.
What isn't working...
I want to use the same SAML support to sign my SAML Payload using a Trusted Certificate that I have for my website that I received from GoDaddy. To do this, I installed the Trusted Certificate into my webserver's keystore at: '\Program Files\Java\jre1.8.0_102\lib\security\cacerts'. I understand that the cacerts file is the KeyStore for our webserver. I installed the Trusted Certificate using the Keytool -importcert command. One big difference is that the Trusted Certificate DOESN'T have a Private Key. So when preparing the Signing Certificate using the Open SAML support, I am not able to add a Private Key to the Credential object (because I don't have one).
When attempting the above for the Trusted Certificate, I am able to get to the part where I am preparing the Signature Parms (SecurityHelper.prepareSignatureParams()). That's where I get the Null Pointer.
If you could take a look at the code that I am using. I am including the code (that signs my payload successfully) that reads from the local JKS file and also the code (that gets the Null Pointer Exception) when I try to using the Trusted Certificate on the server (both cases). There's not much different between the two cases:
// Signing process using OpenSAML
// Get instance of an OpenSAML 'KeyStore' object...
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// Read KeyStore as File Input Stream. This is either the local JKS
// file or the server's cacerts file.
File ksFile = new File(keyStoreFileName);
// Open an Input Stream with the Key Store File
FileInputStream ksfInputStream = new FileInputStream(ksFile);
// Load KeyStore. The keyStorePassord is the password assigned to the keystore, Usually 'changeit'
// before being changed.
keyStore.load(ksfInputStream, keyStorePassword);
// Close InputFileStream. No longer needed.
ksfInputStream.close();
// Used to get Entry objects from the Key Store
KeyStore.PrivateKeyEntry pkEntry = null;
KeyStore.TrustedCertificateEntry tcEntry = null;
PrivateKey pk = null;
X509Certificate x509Certificate = null;
BasicX509Credential credential = null;
// The Java Key Store specific code...
// Get Key Entry From the Key Store. CertificateAliasName identifies the
// Entry in the KeyStore. KeyPassword is assigned to the Private Key.
pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(certificateAliasName, new KeyStore.PasswordProtection(keyPassword));
// Get the Private Key from the Entry
pk = pkEntry.getPrivateKey();
// Get the Certificate from the Entry
x509Certificate = (X509Certificate) pkEntry.getCertificate();
// Create the Credential. Assign the X509Certificate and the Privatekey
credential = new BasicX509Credential();
credential.setEntityCertificate(x509Certificate);
credential.setPrivateKey(pk);
// The Trusted Certificate specific code...
// Accessing a Certificate that was issued from a trusted source - like GoDaddy.com
//
// Get Certificate Entry From the Key Store. CertificateAliasName identifies the Entry in the KeyStore.
// There is NO password as there is no Private Key associate with this Certificate
tcEntry = (TrustedCertificateEntry) keyStore.getEntry(certificateAliasName, null);
// Get the Certificate from the Entry
x509Certificate = (X509Certificate) tcEntry.getTrustedCertificate();
// Create the Credential. There is no Provate Ley to assign into the Credential
credential = new BasicX509Credential();
credential.setEntityCertificate(x509Certificate);
// Back to code that is not specific to either method...
//
// Assign the X509Credential object into a Credential Object. The BasicX509Credential object
// that has a Certificate and a Private Key OR just a Certificate added to it is now saved as a
// Cendential object.
Credential signingCredential = credential;
// Use the OpenSAML builder to create a signature object.
Signature signingSignature = (Signature) Configuration.getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME).build Object(Signature.DEFAULT_ELEMENT_NAME);
// Set the previously created signing credential
signingSignature.setSigningCredential(signingCredential);
// Get a Global Security Configuration object.
SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();
// KeyInfoGenerator. Not sure what this is, but the example I am working from shows
// this being passed as null.
String keyInfoGeneratorProfile = "XMLSignature";
// Prepare the Signature Parameters.
//
// This works fine for the JKS version of the KeyStore, but gets a Null Pointer exception when I run to the cacerts file.
SecurityHelper.prepareSignatureParams(signingSignature, signingCredential, secConfig, keyInfoGeneratorProfile <or null>);
// I need to set into the SigningSignature object the signing algorithm. This is required when using the TrustedCertificate
signingSignature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
// This is my code that builds a SAML Response. The SAML Payload contains data
// about the SSO session that I will be creating...
Response samlResponse = createSamlResponse.buildSamlResponseMessage();
// Sign the Response using the Certificate that was created earlier
samlResponse.setSignature(signingSignature);
// Get the marshaller factory to marshall the SamlResponse
MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory();
Marshaller responseMarshaller = marshallerFactory.getMarshaller(samlResponse);
// Marshall the Response
Element responseElement = responseElement = responseMarshaller.marshall(samlResponse);
// Sign the Object...
Signer.signObject(signingSignature);
NOTE: My attempt to sign a SAML Payload was modeled after an OPENSAML example that I found here: https://narendrakadali.wordpress.com/2011/06/05/sign-assertion-using-opensaml/
Hoping that someone can show me the error of my ways or what I am missing.
Thanks for any suggestions.
EDIT (01/26/2016)
I was able to get past the NULL pointer I was receiving while preparing the Signature Params (SecurityHelper.prepareSignatureParams()). Code changes included updating my xmlsec.jar file to version 2.0.8 (xmlsec-2.0.8.jar) and I explicitly setting the signature algorithm to SHA256 when using the Trusted Certificate (from GoDaddy). See my code example for the use of:
signingSignature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
The above changes allows the SAML payload to be built and sent to the connection endpoint.
However, I am still not establishing the SSO connection to my endpoint.
Here's what I see happening:
During processing while the SAML payload is being constructed and specifically, the SAML payload's Signature is being signed:
Signer.signObject(signature);
I get an an error message from SAML:
ERROR: org.opensaml.xml.signature.Signer - An error occured computing the digital signature
The stack trace (just the ending portion):
org.apache.xml.security.signature.XMLSignatureException: Sorry, you supplied the wrong key type for this operation! You supplied a null but a java.security.PrivateKey is needed.
at org.apache.xml.security.algorithms.implementations.SignatureBaseRSA.engineInitSign(SignatureBaseRSA.java:149)
at org.apache.xml.security.algorithms.implementations.SignatureBaseRSA.engineInitSign(SignatureBaseRSA.java:165)
at org.apache.xml.security.algorithms.SignatureAlgorithm.initSign(SignatureAlgorithm.java:238)
at org.apache.xml.security.signature.XMLSignature.sign(XMLSignature.java:631)
at org.opensaml.xml.signature.Signer.signObject(Signer.java:77)
I searched the error messages, but I am not coming up with much.
I don't understand the root of the error message - That the wrong key type was supplied (null) and that OpenSAML seems to be expecting a java.Security.PrivateKey.
When using the Trusted Certificate, I don't have a Private Key, Correct? How would I be able to provide a Private Key? In the case of the Trusted Certificate I read a Trusted Certificate (TrustedCertificateEntry) from the KeyStore. The TrustedCertificateEntry object allows me to access the Certificate, but there's no method for obtaining a Private Key (as well there shouldn't be).
However, when I use my Self Signed Certificate to perform the signing operation, I understand that I do have both the Certificate (the Public Key) and the Private Key contained in the JKS file (the KeyStore). I think that's why when I read from the JKS file, I am able to read a Private Key Entry (KeyStore.PrivateKeyEntry) that has methods for accessing both the Public Key (the Certificate) and the Private Key.
What am I missing about the Trusted Certificate case? The OpenSAML support seems to be expecting a Private key to be able to compute the Signature.
In the case of the Trusted Certificate, is there a way to package the original Private Key into my Key Store (along with the Trusted Certificate)? I am not sure if this is what is normally done or even possible.
Hopefully some guidance as to what I am doing here, Please!
EDIT (01/26/2017) - 2 to provide additional detail.
I'll share a portion of the SAML payload that gets sent...
In the case of the Self Signed Certificate, I see a SignatureValue tag and a X509Certificate tag. Both have binary data included within the begin and end of the tag.
In the case of the Trusted Certificate, I've got an empty Signature Value tag that looks like:
<ds:SignatureValue/>
The Certificate tag is still present and contains the certificate bytes.
So, looking at the error I see from OpenSAML, it is more obvious that it can't compute a Signature using the data that is available in the Trusted Certificate.

Ok, this quite a long question. As I understand the root of the problem is the message "Sorry, you supplied the wrong key type for this operation! You supplied a null but a java.security.PrivateKey is needed." You are trying to sign a message using a public key. This is not possible. Just looking logically on it, signing using a public key would not provide any proof that the signer is intended as it is available to everyone.
What you need to do is sign using a private key. in your case you have generated a public and private key on you computer, then sent CSR to the CA and recieved a certificate signed by the CA.
You should use the privat key from you local computer to sign the message and send the CA signed certificate to the recipient so they can use it to confirm your signature.
In this blog post of mine I explain how to obtain credentials, including the private key from a Java keystore.

Related

Trying to understand how mutual authentication key pairs and certificates work in a gRPC context

so after reading countless articles on how and what to generate key pairs, certificates, trust managers i'm incredibly confused.
This is my situation, i have a client:
SslContextBuilder builder = GrpcSslContexts.forClient();
// builder.trustManager(new File(trustCertCollectionFilePath)); //i've read this should be ignored for the client
builder.keyManager(new File(clientCertChainFilePath), new File(clientPrivateKeyFilePath));
and a server:
SslContextBuilder sslClientContextBuilder = SslContextBuilder.forServer(new
File(certChainFilePath), new File(privateKeyFilePath));
sslClientContextBuilder.trustManager(new File(trustCertCollectionFilePath));
sslClientContextBuilder.clientAuth(ClientAuth.REQUIRE);
I'm using these from an example found here:
https://github.com/grpc/grpc-java/tree/master/examples/example-tls/src/main/java/io/grpc/examples/helloworldtls
As far as I've understood it should work like this:
For the client:
1. You have to generate a RSA key pair.
2. Generate a certificate.
3. Put the public key inside the certificate.
clientCertChainFilePath = certificate with public key inside
clientPrivateKeyFilePath = client private key
For the server:
1. You have to generate a trusted authority certificate(CA) with a server private key
2. Get the certificate from the client
3. Register the client certificate inside the trusted authority somehow.
certChainFilePath = certificate from the client with public key inside
privateKeyFilePath = private server key for the trust authority certificate(CA)
trustCertCollectionFilePath = trusted authority certificate(CA)
Please correct me or tell me how exactly all of this binds together to make this work, if you have any specific links on how to generate everything properly it's highly appreciated.
Client and server both have their own public and private keys. These are two separate pairs of public-private keys.
On the server side, certChainFilePath is the server certificate with server's public key inside. privateKeyFilePath is server's private key.
Without mutual TLS, client only needs the CA certificate to verify server's certificate received during handshake.
With mutual TLS, client is requested to send its certificate (with client public key inside) to server.

Validating a leaf certificate from a third party in java 8

I am looking for a way to validate a java.security.cert.X509Certificate object that is sent from a third party. The certificate provider is using dns or ldap to fetch the certificate. I have included a link with additional information on how the certificate is being retrieved.
http://wiki.directproject.org/w/images/2/24/Certificate_Discovery_for_Direct_Project_Implementation_Guide_v4.1.pdf
I also need to know protocols and default ports that would be used in any of the verification steps. The certificate needs to meet the following criteria from page 13 section 4 of this document:
http://wiki.directproject.org/w/images/e/e6/Applicability_Statement_for_Secure_Health_Transport_v1.2.pdf
Has not expired.
Has a valid signature with a valid message digest
Has not been revoked
Binding to the expected entity
Has a trusted certificate path
Item 1 is straight forward to compare the dates from the getNotAfter and getNotBefore methods on the certificate object to the current date or to use the checkValidity method which throws a checked exception.
For item #2, I see a method to get the signature, but I am unsure how to generate the message digest and verify that the signature and message digest are both valid.
For item #3, The certification revocation list seems to be mixed with some other data by calling this method on the certificate getExtensionValue("2.5.29.31"). Retrieving the certification revocation list data seems possible over http, and ocsp seems to be based on http. I haven't been able to find how to do this in java.
For item #4, I am not sure what binding means in the context of certificates, or what is involved in verifying it.
For item #5, It looks like the data for intermediary certificates is mixed with some other data by calling this method on the certificate getExtensionValue("1.3.6.1.5.5.7.1.1"). CertPathValidator looks like it may be able to help verify this information once the certificates data is retrieved over http.
Certificate validation is a complex task. You can perform all the validations you need manually (expiration, revocation, certification chain) using native Java 8 support or Bouncycastle. But the option that I would recommend is to use a specific library that has already taken into account all the possibilities.
Take a look to DSS documentation and Certificate Verification example
// Trusted certificates sources, root and intermediates (#5 )
CertificateSource trustedCertSource = null;
CertificateSource adjunctCertSource = null;
// The certificate to be validated
CertificateToken token = DSSUtils.loadCertificate(new File("src/main/resources/keystore/ec.europa.eu.1.cer"));
// Creates a CertificateVerifier using Online sources. It checks the revocation status with the CRL lists URLs or OCSP server extracted from the certificate #3
CertificateVerifier cv = new CommonCertificateVerifier();
cv.setAdjunctCertSource(adjunctCertSource);
cv.setTrustedCertSource(trustedCertSource);
// Creates an instance of the CertificateValidator with the certificate
CertificateValidator validator = CertificateValidator.fromCertificate(token);
validator.setCertificateVerifier(cv);
// We execute the validation (#1, #2, #3, #5)
CertificateReports certificateReports = validator.validate();
//The final result. You have also a detailedReport and DiagnosticData
SimpleCertificateReport simpleReport = certificateReports.getSimpleReport();
The validation will perform all the steps you indicate, including expiration, signing of the certificate, revocation, and checking the chain of trust (including the download of intermediate certificates).
Step # 4 I don't know exactly what you mean. I suppose to validate that the certificate corresponds to one of the certification entities of the trusted list
To load the trusted certificate sources see this
CertificatePool certPool = new CertificatePool();
CommonCertificateSource ccc = new CommonCertificateSource(certPool);
CertificateToken cert = DSSUtils.loadCertificate(new File("root_ca.cer"));
CertificateToken adddedCert = ccc.addCertificate(cert);
I will split the answer to 3 pieces. The first is the background , the second is the choice of library , implementation code (references that i had used for my implementation with due credit)
In the past i had implemented a very similar use case. I had IOT fabrications done by a vendor and to onboard them i had implement the same X509 verification process that you have mentioned.
Implementation :
For my implementation i had refered the following . You can include BC as your defaultProvider (Security.setProvider) and use the following code . The code directly solves 1,3,5. The code is here : https://nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-chain-and-verify-clr-with-bouncy-castle/
Now coming to 2 , the answer to that depends on how you will get the certificate from your client and what additional data will be provided by the application.
The high level flow of that is as follows
a) Client produces the certificate
b) Client does a Digest using some algorithm that is acceptable. SHA256 are quite popular, you can increase the strength based on the needs and how much compute you have. Once the client creates the digest , to prove he is the owner of the certificate you can get the digest signed using the private key of the device. This can be then transmitted to the verifier application
c) Once you have the certificate and the signature , you can then use the Certificate and Public key associated with it to verify the signature applying the same digest and then verifying the signature.A good reference is here : http://www.java2s.com/Code/Java/Security/SignatureSignAndVerify.htm
I am not a 100% sure on what 4 means. But if it means proof of identity (who is producing the certificate is bound to be who they are , signing and verifying will provide the same)
Though you can realize the use cases using java security API's , I used bouncycastle core API for realizing the use cases. Bouncycastle API's are far more rich and battle tested especially for weird EC curve algorithms we had to use & you will find that many folks swear by BouncyCastle.
Hope this helps!

Add a pkcs#11 certificate into a keystore object with setCertificateEntry(cert) fails

Note
I don't want to import a certificate into a keystore with keytool. What I want, is to add a java.security.cert.Certificate object into a java.security.KeyStore object.
Scenario
My apps works with FireFox user certificates and those in a smartcard, with NSS + JSS. The APP first loads all user certificates via NSS and stores them into a KeyStore object, and stores their SN, DN, etc. Then, with org.mozilla.jss.CryptoManager I load all external modules, then every token of the modules, at last, with this line, I get a CryptoStore with all certificates of the token:
CryptoStore store = null;
store = token.getCryptoStore();
org.mozilla.jss.crypto.X509Certificate[] certs = store.getCertificates();
Then, I read these certificates and store their SN, DN, etc., along with those data of user certificates, for UI part to show.
When doing an authentication, the user is prompted a dialog with all certificates to choose. This window has a table with rows containing alias of each certificate, users' + card. Then I pick the certificate from the keyStore with the selected alias.
Error
Here comes the problem: the keyStore only has entries of user certificates from FireFox, not those in the card. If I pick one from the card, and do this:
certificate = keyStore.getCertificate(alias);
It returns null. It makes sense because those from the card are stored in CryptoStore. Now, if I do this:
keyStore.setCertificateEntry(alias, cert); //cert is a certificate from card
no exception occurs. Then, when I do this again:
certificate = keyStore.getCertificate(alias);
A NullPointerException from sun.security.pkcs11.p11keyStore.P11KeyStore.getID() happens.
Question
Adding a certificate from a smartcard to a java.security.KeyStore is possible with my approach here?
Why the exception?

Why do I get the error "Cannot store non-PrivateKeys" when creating an SSL Socket in Java?

I am working on an older IBM iSeries (IBM-i, i5OS, AS/400, etc), with a Java 5 JVM (Classic, not ITJ J9) on O/S version V5R3M0.
Here is the scenario in a nutshell:
I created a key-store of type JKS using Portecle 1.7 (Note: I did try converting my key-store to JCEKS but that was rejected as an unsupported format, so it appears that JKS is the only option with the iSeries machine (at least the version I am on).
I then created a key-pair and CSR and sent the CSR to Thawte to be signed.
I imported the signed certificate from Thawte successfully using the PKCS#7 format to import the entire certificate chain, which included my certificate, the Thawte intermediary and the Thawte server root.
This all worked as expected.
However, when I ran up the JVM, configured properly to point to the store and supply it's password (which I have done in the past with self-signed certificates created in Portecle for testing), and try to start my web server on 443, I get the following security exception:
java.security.KeyStoreException: Cannot store non-PrivateKeys
Can anyone tell me where I went wrong, or what I should check next?
The "Cannot store non-PrivateKeys" error message usually indicates you are trying to use secret symmetric keys with a JKS keystore type. The JKS keystore type only supports asymmetric (public/private) keys. You would have to create a new keystore of type JCEKS to support secret keys.
As it turns out, this was a subtle problem, and it's worth giving the answer here in case someone else has something similar.
The TLDR answer is that I did not check that my key and certificate were not null and as a result attempted to add a null key and certificate to a key-store. The longer answer follows.
The way we have our web server set up to use SSL, specifically to support our user's typical configuration where the IP address is used to configure the web site listen address rather than a DNS name, is that it locates the certificate in the master key-store using the alias, and creates an ephemeral key-store containing just the certificate for that web site, using that key-store to configure an SSL context and an SSL socket factory, like so:
// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE
try {
final char[] BLANK_PWD=new char[0];
SSLContext ctx=SSLContext.getInstance("TLS");
KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Key ctfkey=mstkst.getKey(svrctfals,BLANK_PWD);
Certificate[] ctfchn=mstkst.getCertificateChain(svrctfals);
KeyStore sktkst;
sktkst=KeyStore.getInstance("jks");
sktkst.load(null,BLANK_PWD);
sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
kmf.init(sktkst,BLANK_PWD);
ctx.init(kmf.getKeyManagers(),null,null);
ssf=ctx.getServerSocketFactory();
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr);
}
Notice that it uses a blank password for extracting the private key and certificates from the master key-store. That was my problem - I had, out of habit from using keytool, created the private key-pair with a password (the same password as the key-store).
Because I had a password on the certificate, the key and certificate were not extracted, and null was passed to sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn); However, setKeyEntry checks the passed Key using instanceof and concludes (correctly) that null is not an instanceof PrivateKey, resulting in the misleading error I was seeing.
The corrected code checks that a key and certificate are found and sends appropriate errors:
// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE
try {
final char[] BLANK_PWD=new char[0];
SSLContext ctx=SSLContext.getInstance("TLS");
KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Key ctfkey=mstkst.getKey(svrctfals,BLANK_PWD);
Certificate[] ctfchn=mstkst.getCertificateChain(svrctfals);
KeyStore sktkst;
if(ctfkey==null) {
throw new IOException("Cannot create server socket factory: No key found for alias '"+svrctfals+"'");
}
if(ctfchn==null || ctfchn.length==0) {
throw new IOException("Cannot create server socket factory: No certificate found for alias '"+svrctfals+"'");
}
sktkst=KeyStore.getInstance("jks");
sktkst.load(null,BLANK_PWD);
sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn);
kmf.init(sktkst,BLANK_PWD);
ctx.init(kmf.getKeyManagers(),null,null);
ssf=ctx.getServerSocketFactory();
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr);
}
Instead of using an ephemeral keystore, you could handle everything within a single SSLContext.
You would need to initialise your SSLContext using an custom X509KeyManager instead of using the one given by the default KeyManagerFactory. In this X509KeyManager,chooseServerAlias(String keyType, Principal[] issuers, Socket socket) should return a different alias depending on the local address obtained from the socket.
This way, you wouldn't have to worry about copying the private key from one keystore to another, and this would even work for keystore types from which you can't extract (and thus copy) but only use the private key, e.g. PKCS#11.

Validate X509 certificates using Java APis

I am trying to validate a certificate against java key store and this is the code I am using is as below. If it completes succesfully then I assume the validation has gone through correctly, else if an exception is thrown, then the validation fails.
My concern is:
Is the code below sufficient to validate a certificate? As in is there something I am missing here (Like checking the data signed by the computer sending me the certificate?)?
2. Should the signature contained within the certificate be verified? If yes, how?
Thanks in advance for the response!
pradeep
// To check the validity of the dates
cert.checkValidity();
//Check the chain
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<X509Certificate> mylist = new ArrayList<X509Certificate>();
mylist.add(cert);
CertPath cp = cf.generateCertPath(mylist);
PKIXParameters params = new PKIXParameters(getTrustStore());
params.setRevocationEnabled(false);
CertPathValidator cpv =
CertPathValidator.getInstance(CertPathValidator.getDefaultType());
PKIXCertPathValidatorResult pkixCertPathValidatorResult =
(PKIXCertPathValidatorResult) cpv.validate(cp, params);
Normally, a certificate will be issued by an intermediate issuing authority, not a "root" authority (which is all that should be in your trust store). Most protocols encourage sending a "chain" of certificates, not just the entity's certificate.
You should add all of the intermediate certs so that a complete chain can be formed.
In order to be certain that the certificate is still valid, you should not disable revocation checks. If you don't want to retrieve a CRL (which can be large), the issuer may offer OCSP support. But, this has to be enabled in the Java runtime by setting certain system properties.
If the path validator returns successfully, you don't need to check anything else. If the certificate is not valid, an exception will be raised.
Also, an explicit check on the validity date is unnecessary. This occurs during validation (using the current time, unless you specify a time via the PKIXParameters).
For a more extensive discussion of validation, including sample code, see a previous answer of mine.
If you're happy with the default trust settings (as they would be used for the default SSLContext), you could build an X509TrustManager independently of SSL/TLS and use if to verify your certificate independently.
It would look like this:
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore)null);
// you could use a non-default KeyStore as your truststore too, instead of null.
for (TrustManager trustManager: trustManagerFactory.getTrustManagers()) {
if (trustManager instanceof X509TrustManager) {
X509TrustManager x509TrustManager = (X509TrustManager)trustManager;
x509TrustManager.checkServerTrusted(...);
}
}
(You should also check the server's identity and the certificate match, see RFC 6125 (Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)).)
What you are doing here is verifying if a certificate (in your example cert) has been signed (directly) by any of the trusted CA's in the truststore.
Additionally you check for expiration but no revocation checking is performed.
So if the cert has not been signed by any of the trusted CA's you will get an exception.
So the code is sufficient to verify if cert has been signed by any of the trusted CAs
If you are refering to server authentication, then the code in the post is not sufficient.
This code just verifies that a specific certificate is signed by a trusted CA.
You have no indication though if the "entity" that send you this certificate is actually the owner of the certificate (i.e. they own the private key associated with this certificate).
This is part of the SSL authentication, where e.g. the client sends the ClientKeyExchange message encrypted with the remote server's public key and is certain that if the other party is fake then it will not be possible to decrypt the message

Categories

Resources