I'm using Java implementation for revocation checking using CRL which is like the following code (I tailored the code to be short and clear)
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
PKIXRevocationChecker rc = (PKIXRevocationChecker)cpb.getRevocationChecker();
rc.setOptions(EnumSet.of(
PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.ONLY_END_ENTITY,
PKIXRevocationChecker.Option.NO_FALLBACK,
PKIXRevocationChecker.Option.SOFT_FAIL));
PKIXBuilderParameters pkixParams =
new PKIXBuilderParameters(getTrustStore(), new X509CertSelector());
pkixParams.setRevocationEnabled(false);
pkixParams.addCertPathChecker(rc);
tmf.init(new CertPathTrustManagerParameters(pkixParams));
SSLContext contect = SSLContext.getInstance("TLSv1.2");
context.init(null, getTrustManagers(), null);
defaultFactory = context.getSocketFactory();
The above code is in the constructor of a custom SSLSocketFactory, and there is a custom TrustManager (returned by getTrustManagers()) which overrides checkServerTrusted() to perform a few certificate checks and executes default checkServerTrusted() at the end.
Everything works fine except that I need that validation of CRL to be failed when it is signed by a CA that does not have the cRLsign key usage
bit set, but it doesn't.
I know that this is a requirement by RFC3280, and somewhere on the web I read that Java implementation is compliant with the RFC and I even saw the methods in the Java base code to do that, But it seems it does not come into play when I run the application.
I did all the revocation checking with a custom code to achieve that but I think Java implementation is way more complete that my custom code and I strongly prefer to use Java implementation.
Any solution?
Related
Premise: I have a certificate and I want to verify that the system 'trusts' this certificate (signed by a trusted root CA by Java / Operating System)
I have found some varying solutions on how to accomplish this.
Option 1:
Use SSL classes to derive trust.
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init((KeyStore) null);
for (TrustManager trustManager : tmfactory.getTrustManagers()) {
if (trustManager instanceof X509TrustManager) {
try {
((X509TrustManager) trustManager).checkClientTrusted(new X509Certificate[] {new JcaX509CertificateConverter().getCertificate(holder)}, "RSA");
System.out.println("This certificate is trusted by a Root CA");
} catch (CertificateException e) {
e.printStackTrace();
}
}
}
Since this approach relies heavily on SSL classes (which are not needed by the current project) we are looking for alternatives.
Option 2:
Load Java's cacertsfile into a keystore and check each 'most-trusted' certificate against my certificate for equality.
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());
// This class retrieves the most-trusted CAs from the keystore
PKIXParameters params = new PKIXParameters(keystore);
// Get the set of trust anchors, which contain the most-trusted CA certificates
Set<X509Certificate> rootCertificates = params.getTrustAnchors().parallelStream().map(TrustAnchor::getTrustedCert).collect(Collectors.toSet());
return rootCertificates.contains(holderX509);
The problem with this approach is that it requires a password to verify integrity of the JKS encoded file. While the SSL one seemingly does not (or rather uses System.getProperty("javax.net.ssl.trustStorePassword") which again is heavily tied to SSL.
Question: Does there exist a solution that is in between manually loading certificates from a file and pure SSL? I feel as if there should be some class that I can call to simply verify the system trust of a certificate without having to jump through a couple hoops.
After reading Beginning Cryptography With Java by David Hook I have produced the following example to verify a certificate chain (which accomplishes the original goal of using the system truststore to verify Root CA's)
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", new BouncyCastleProvider());
InputStream is = new ByteArrayInputStream(some bytes in an array);
CertPath certPath = certificateFactory.generateCertPath(is, "PKCS7"); // Throws Certificate Exception when a cert path cannot be generated
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", new BouncyCastleProvider());
PKIXParameters parameters = new PKIXParameters(KeyTool.getCacertsKeyStore());
PKIXCertPathValidatorResult validatorResult = (PKIXCertPathValidatorResult) certPathValidator.validate(certPath, parameters); // This will throw a CertPathValidatorException if validation fails
This also accomplishes the goal of not having to use SSL classes - instead Java security classes / algorithms are used.
Short of downloading a third-party library, there probably isn't another alternative.
Why are you trying to avoid the "SSL" library? It's part of the standard library and so puts no burden on your program.
In any case, certificate verification is a big part of SSL. I doubt anyone's gone to the trouble of creating a library that does so without also implementing some substantial subset of the SSL protocol. There's just no real reason to do so.
I have been unable to find a solution to this problem elsewhere so I am hoping someone here can provide some insight. My setup below:
keystore, myKeys.jks:
mine-private, 3/6/2014, PrivateKeyEntry
mine-trusted, 3/6/2014, trustedCertEntry
trust store, myTrust.jks:
trusted-cert-1, 3/6/2014, trusterCertEntry
trusted-cert-2, 3/6/2014, trusterCertEntry
mine-trusted, 3/6/2014, trustedCertEntry <-- this is mine
What ends up happening is I get a message stating that my client has not been authenticated. Let me know if there is more information necessary
Responses to questions:
First off: what classes/library are you using? Simply the default https in java?
Apache HTTP Client, code below:
HttpClient client = new HttpClient();
GetMethod method = new GetMethod("https://foo.bar.baz/rest");
client.executeMethod(method);
Secondly: how exactly are you registering the keystore/truststore? You need a custom SSLContext for this.
Don't think so, but could be wrong
-Djavax.net.ssl.trustStore="path/to/myTrust.jks"
-Djavax.net.ssl.trustStorePassword="password"
-Djavax.net.ssl.keyStore="path/to/myKeys.jks"
-Djavax.net.ssl.keyStorePassword="password"
First off: what classes/library are you using? Simply the default https in java?
Secondly: how exactly are you registering the keystore/truststore? You need a custom SSLContext for this.
Initial example:
SSLContext context = SSLContext.getInstance();
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, password);
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
context.init(keyFactory.getKeyManagers(), trustFactory.getTrustManagers(), null);
Most libraries that I know support setting a custom SSLContext or SSLSocketFactory which can be obtained from the context.
I have written a sample that does the exact same thing. You can find the particular code in [1].
[1] https://github.com/wso2/carbon-identity/blob/v5.0.7/components/authentication-framework/org.wso2.carbon.identity.application.authentication.endpoint.util/src/main/java/org/wso2/carbon/identity/application/authentication/endpoint/util/TenantMgtAdminServiceClient.java#L155
Oh wise and noble Oracle,
I'm adding SSL to a TCP client I've written on my Android phone. I can
successfully connect to servers with properly signed certificates, and I can
connect to self-certifying hosts by cooking up a TrustManager implementation
that always thinks everything is fine.
I now have a decorator TrustManager capturing the certificates (before
delegating to its decoratee) for self-certifying hosts and presenting them for
my breathless perusal, but what I can't work out is how to implement ssh's
behaviour of warning that a host is unknown and offering to remember it for
next time - and doing so.
I presumed all I needed was to store the public key - as ssh does with
known_hosts - and re-represent it, but with this code and 'sslTrust' holding
the public key:
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null); // initialise!
ks.setKeyEntry("dbentry", Base64.decode(sslTrust, Base64.NO_WRAP), null);
tmf.init(ks);
tms = tmf.getTrustManagers();
ss.stm = new SnoopyTrustManager((X509TrustManager) tms[0]);
// ...
SLContext context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] { ss.stm } , null);
ss.factory = context.getSocketFactory();
// ...
SocketFactory factory = ss.getFactory();
mSocket = factory.createSocket(host, port);
attempting to establish a connection results in
SSLHandshakeException: InvalidAlgorithmParameterException: trustAnchors.isEmpty()
which is fair enough: I don't know how to cook things up from the certificate
offered by the remote server. I'm also fairly sure this isn't how I tell a
TrustManager about a remote server's public key anyway.
Since the site is self-certifying, I imagine could probably just verify that
the public keys match in a trivial TrustManager, but I'd like to understand
how this 'should' be done - adding a CA on a per-connection basis, since
I won't trust that CA for anything else.
You need to use your own trust store on pre-ICS version, and add the serer's certificates to it on first error. Subsequent connections will load it from the trust store and thus trust the remote certificate. This is not a complete solution, but here's one way to do it (code on Github), along with some discussion:
http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store-on.html
I'm currently having a self signed certificate for my HTTPS webserver.
In my java program there is a SSLSocketFactory that will create a socket to the webserver. The default implementation of sun blocks the self signed certificate. With an own implementation of a X509TrustManager I can only check whether the date of the certificate is valid.
Is there any possibility to let the default implementation check the validity (date and hostname, ...), and if it fails to show a dialog to let the user accept this certificate?
Each code I found until now only disabled the ssl check and accepted every invalid certificate.
I haven't actually tried this, but why can't you implement your own trust manager, which first delegates to the default trust manager to check if the certificate is valid and if not, asks the user if he still wants to accept the certificate?
You can initialize most of the security classes with null arguments to use default values. To obtain the default trust manager, you must get the available trust managers and choose the first one in the mgrs arrays to implement the X509TrustManager interface. Usually, the array will only contain one elment anyway.
TrustManagerFactory trustmanagerfactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustmanagerfactory.init((KeyStore)null);
TrustManager[] mgrs = trustmanagerfactory.getTrustManagers();
After you've wrapped the default trust manager with your own extension, you have to initialize an SSL context and get a socket factory from it:
SSLContext sslContext=SSLContext.getInstance("SSL","SunJSSE");
sslContext.init(null, new TrustManager[] {myTm}, null);
SSLSocketFactory sf = sslContext.getSocketFactory();
Then use this socket factory to create new client sockets or pass it to HttpsURLConnection.setDefaultSSLSocketFactory to use the https protocol in URLs with your own trust manager.
Ideally, I only need a simple SSLSocketChannel.
I already have a component that reads and writes message over ordinary SocketChannel, but for some of these connections, I have to use SSL over the wire; the operations over these connections, however, are the same.
Does anyone knows a free SSLSocketChannel implementation (with the appropriate selector) or something similar? I've found this, but the selector doesn't accept it since its vendor isn't SUN.
I'm decoupling the reading_from/writing_to net logic from the insertion and retrieval of network data via a simple object, in order to use a SSLEngine without getting mad, but it's really tricky to implement that correctly, given the fact that I don't know the internals of SSL protocol...
Jetty has an NIO SSL implementation for their server: SslSelectorChannelConnector. You might want to peek at it for details on what its doing.
There is also an old (but decent) article from O'Reilly that explains the details about NIO + SSL along with example code.
TLS Channel is a simple library that does exactly that: wrapping a SSLContext (or SSLEngine) and exposing a ByteChannel interface, doing the heavy lifting internally.
(Disclaimer: I am the library's main author).
Check out Restlet's implementation it may do what you need, and it's all about NIO.
Restlet Engine Javadoc
Specifically the HttpClientCall. SetProtocol(HTTPS) - getResponseEntityChannel returns a ReadableByteChannel (getEntityChannel returns a WriteableByteChannel)
Not sure if this is what you're looking for, but may help... To create SSL/TLS enabled server sockets, I'm currently using code like the following (keystore.jks contains a self signed private/public key pair used for securing confirmation) - clients have a similar trust store which contains the signed certificate with the public key of that pair.
A bit of googling around getting that configured should get you underway.
String keyStorePath = "keystore.jks";
String keyStorePassword = "password";
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = new KeyStore();
keyStore.load(new FileInputStream(keyStorePath), keyStorePassword);
keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLContext sslContext = getServerSSLContext(namespace.getUuid());
SSLServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory();
// Create sockets as necessary