I'm currently trying to transfer data over the internet via SSL/TLS in java and I want both parties to authenticate themselves. I have implemented the KeyManager myself to load the key pair and present the other party the appropriate certificate.
Now, I'm trying to check the certificate and I'm doing that by implementing my own TrustManager (both parties hold the cert of the other party, everything is self-signed). However, getAcceptedIssuers doesn't work like I want it to, because even when I return none the connection still gets established without problem.
Why doesn't the certificate get refused?
protected static class SelectingTrustManager implements X509TrustManager{
final X509TrustManager delegate;
private String[] trustedAliases;
private final KeyStore keystore;
public SelectingTrustManager(X509TrustManager delegate, KeyStore keystore, String[] trustedAliases) {
this.trustedAliases = trustedAliases;
this.keystore = keystore;
this.delegate = delegate;
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException{
delegate.checkClientTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException{
delegate.checkServerTrusted(chain, authType);
}
public X509Certificate[] getAcceptedIssuers(){
return new X509Certificate[0];
}
}
You aren't clear if your code is client or server, so I answer both, although it pretty much matters only for server.
Although the javadoc isn't specific, X509TM.getAcceptedIssuers is NOT used to decide whether to trust a received cert or chain; that is done solely by checkServerTrusted in the client or checkClientTrusted in the server.
The value of getAcceptedIssuers is used for, and affects, only two things:
in the server, (only) if client auth is enabled (by calling needClientAuth(true) or wantClientAuth(true)), the Subject names from its elements are used to create the CA list in the server's CertificateRequest message. This is not forced to be the same as the list of CAs that will be used as trust anchors for the client cert chain, if one is received; in fact, the trustmanager isn't actually required to use a list of trust anchors or even the rest of the standard validation algorithm -- although if your 'delegate' is the standard X509[Extended]TrustManager which uses the standard CertPathValidator that does. However, if you tell client(s) to use cert(s) from certain CAs, and then don't accept valid cert chain(s) from those CAs, you will likely have unhappy client(s) who may come after you with various heavy, sharp and/or otherwise unpleasant objects.
In the specific case of no 'CAs' (an array of 0 length, as you have), the client can send a cert from any CA it chooses, and the server will accept it depending only on checkClientTrusted.
For completeness note the RFCs define an extension for the client to specify what CA(s) it wants the server cert to use, but I don't know any implementation that supports this extension and Java/JSSE definitely doesn't, so in practice the server either has only one cert (per algorithm), or it selects based on SNI (and nothing else), and if that cert isn't trusted by the client too bad.
if you have algorithm constraints in effect (and nowadays you usually do by default even if you don't explicitly set some) they are not enforced on the last cert in a chain (the putative anchor) if it is among the certs returned by getAcceptedIssuers, which are presumed to be (actual) anchors. In other words if a cert is a trust anchor presumably the user has decided to trust it even though it may use algorithms that do not meet current standards (like MD5, or RSA smaller than 1024).
Whether the person who put the cert in a truststore or otherwise made it an anchor actually evaluated its security correctly is a different question that Java doesn't try to answer. Even Stackexchange may not be able to do that, although I'm sure there will be people here glad to try. (I make no commitment whether I will be one of them.)
Related
On this project of mine I needed to implement secure connection using SSL/TLS between a client and a server. I found a good article about that so I've managed to do my task without any problem.
This is the article.
My question is pretty simple but I cannot find an answer anywhere. In this particular case, my clients have the same key in the SSL protocol which is created through tutorial on a previous link and put in some kind of a file. Potential problem in this process is that someone can access that file and since every client has that key, someone can listen to all connections.
What I wanted to ask, is there any chance to dynamically generate keys every time some client wants to access the server and put the generated key in the server truststore?
UPDATE
public static final String PATH_TO_ANDROID_KEYSTORE = "and/client.bks";
public static final String PATH_TO_ANDROID_TRUSTSTORE = "and/clienttruststore.bks";
String pathToKeyStore = PATH_TO_ANDROID_KEYSTORE;
String pathToTrustStore = PATH_TO_ANDROID_TRUSTSTORE;
KeyStore keyStoreKeys = KeyStore.getInstance(keyStoreType);
keyStoreKeys.load(Gdx.files.internal(pathToKeyStore).read(), passphrase);
KeyStore keyStoreTrust = KeyStore.getInstance(keyStoreType);
keyStoreTrust.load(Gdx.files.internal(pathToTrustStore).read(), passphrase);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStoreKeys, passphrase);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStoreTrust);
This is the client code and seems like clients have exported server's certificates in their truststores but they actually use the same private key that is generated only once in the keystore using openssl tool.
In this particular case, my clients have the same key in the SSL protocol which is created through tutorial on a previous link and put in some kind of a file.
Unclear. Do you mean they share the same private key? If so, that is a flaw in your system design. Every client should have its own private key. Otherwise the private key isn't, err, private. And access to that key should be via a keystore whose password only the applicion knows, which provides at least another line of defence.
If you just mean that they all have an exported copy of the server's certificate, in their truststores, there is no security risk attached to that at all: it is perfectly normal.
Potential problem in this process is that someone can access that file and since every client has that key, someone can listen to all connections.
No they can't. SSL is immune to man-in-the-middle attacks provided you don't compromise your server's private key, but if you're talking about client private keys they can masquerade as a real client even if they aren't, if they can break through the keystore-password barrier.
What I wanted to ask, is there any chance to dynamically generate keys every time some client wants to access the server and put the generated key in the server truststore?
Not securely, and not online. If your genuine clients can do it, so can an attacker. That's why trust material must be distributed offline.
I have an Android application that calls web services over SSL. In production we will have normal SSL certificates that are signed by a trusted CA. However, we need to be able to support self-signed certificates (signed by our own CA).
I have successfully implemented the suggested solution of accepting self-signed certificates but this will not work due to the risk of man in the middle attacks. I then created a trustmanager that validates that the certificate chain was in fact signed by our CA.
The problem is I have to bypass the normal SSL validation - the application will now only speak to a server that has one of our self-signed certificates installed.
I am a bit lost, I've googled extensively but can't find anything. I was hoping to find a way of programmatically adding our CA to the trust store on the device as this would be the least intrusive way of dealing with the issue.
What I want to achieve:
1. Full standard support for normal SSL certificates.
2. Additional support for self-signed certificates signed by our own CA.
Any advice?
You haven't posted any code, so I can't be sure what you actually did. However, I'll assume that you're setting up a SSLContext using only your custom X509TrustManager subclass. That's fine, but what you can do is have your custom trust manager implementation also chain to the built-in trust managers. You can do this while setting up your trust manager; something like this should work:
private List<X509TrustManager> trustManagers = new ArrayList<X509TrustManager>();
public MyCustomTrustManager() {
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmFactory.init((KeyStore)null);
for (TrustManager tm : tmFactory.getTrustManagers()) {
if (tm instanceof X509TrustManager)
trustManagers.add((X509TrustManager)tm);
}
}
So now your custom trust manager has a list of all the built-in trust managers. In your override of checkServerTrusted(), you'll want to loop through the built-in trust managers and check each one by calling checkServerTrusted() on each one in turn. If none of them trust the certificate, you can apply your own cert checking. If that passes, you can return normally. If not, just throw a CertificateException as you'd otherwise do.
EDIT: Adding the below about doing things like host name verification.
You can also verify that the hostname in the certificate matches what you expect it to be. You'll want to pass in the valid hostname in your constructor for your custom trust manager and stash it in the class. Your checkServerTrusted() method will get passed an array of X509Certificate. Many "chains" will consist of just a single cert, but others will have several, depending on how the cA signed your cert. Either way, the first cert in the array should be "your" cert that you want to compare against.
After you check for basic cert validity using the trust managers, you'll then want to do something like this:
Principal subjectDN = chain[0].getSubjectDN();
String subjectCN = parseDN(subjectDN.getName(), "CN");
if (this.allowedCN.equals(subjectCN)) {
// certificate is good
}
The implementation of parseDN() is left up to you. subjectDN.getName() will return a comma-separated list of key-value pairs (separated by =), something like C=US,ST=California,L=Mountain View,O=Google Inc,CN=www.google.com. You want the CN ("Common Name") value for your hostname comparison. Note that if you have a wildcard cert, it'll be listed as something like *.example.com, so you'll need to do more than a simple equals match in that case.
Ok so I have a peer to peer (client/server on one host) setup (over a local LAN), this is using Netty, a Java networking framework. I use raw TCP/IP (as in, no HTTP) for communication and transfers.
Currently all data is transferred in "plain-text" and i'm starting the process of securing such transmitted data.
I've had a good read of types of encryption/practices etc (but probably only touched the surface and its melting my brain already)
Netty includes a SSL implemntation, heres some links to hopefully better explain myself:
http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/securechat/package-summary.html
Inside "SecureChatTrustManagerFactory" there are 2 methods:
public void checkClientTrusted(
X509Certificate[] chain, String authType) throws CertificateException {
// Always trust - it is an example.
// You should do something in the real world.
// You will reach here only if you enabled client certificate auth,
// as described in SecureChatSslContextFactory.
System.err.println(
"UNKNOWN CLIENT CERTIFICATE: " + chain[0].getSubjectDN());
}
public void checkServerTrusted(
X509Certificate[] chain, String authType) throws CertificateException {
// Always trust - it is an example.
// You should do something in the real world.
System.err.println(
"UNKNOWN SERVER CERTIFICATE: " + chain[0].getSubjectDN());
}
"SecureChatKeyStore" contains a hard coded certificate from what I can see.
So my questions are:
Do I need to generate a certificate?
if so, each time the application is run?
if so, per client?
if so, is this certification passed between client and server?
if so, how is it done securely?
I'm not entirely sure where to start.
From what I can see the Netty implementation is saying "Here's the basis of creating secure connections, but we have left out the part that actually makes them secure/authenticated".
Any other pointers/tips I should know about?
Thank you in advance.
As others have pointed out, there is a difference between application security and transport link security. I think you are aiming for the last one as you mainly mention encryption. Encryption offers confidentiallity from eavesdroppers. Furhermore, as SSL also incorporates message authentication code, it will also offer protection of a third party altering packets during transit. It does not provide any protection of messages once received.
As you may have noticed on the internet for HTTPS connections, you will need at least a server certificate. This certificate can remain static, although it should contain an expiry date at which time you should replace the certificate. The server certificate should be trusted by the client (e.g. by embedding it as a resource). You can also use SSL with client authentication, but that means you need to have ample security measures to keep the private key on the client safe.
It's probably best to start off with a "self-signed" server certificate only. Thats the one you need to trust in the checkServerTrusted method. Basically, the chain is simply that one certificate.
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
Our system communicates with several web services providers. They are all invoked from a single Java client application. All the web services up until now have been over SSL, but none use client certificates. Well, a new partner is changing that.
Making the application use a certificate for the invocation is easy; setting javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword will do it. However, the problem is now how to make it so that it only uses the certificate when invoking that particular web service. I guess more generally speaking, we'd like to be able to choose the client certificate to be used, if any.
One quick solution could be setting the system properties, invoking the methods, and then unsetting them. The only problem with that is that we're dealing with a multi-threaded application, so now we would need to deal with synchronization or locks or what have you.
Each service client is supposed to be completely independent from each other, and they're individually packaged in separate JARs. Thus, one option that has occurred to me (although we haven't properly analyzed it) is to somehow isolate each JAR, maybe load each one under a different VM with different parameters. That's merely an idea that I don't know how to implement (or if it's even possible, for that matter.)
This post suggests that it is possible to select an individual certificate from a key store, but how to attach it to the request seems to be a different issue altogether.
We're using Java 1.5, Axis2, and client classes generated with either wsimport or wsdl2java.
The configuration is done via an SSLContext, which is effectively a factory for the SSLSocketFactory (or SSLEngine). By default, this will be configured from the javax.net.ssl.* properties. In addition, when a server requests a certificate, it sends a TLS/SSL CertificateRequest message that contains a list of CA's distinguished names that it's willing to accept. Although this list is strictly speaking only indicative (i.e. servers could accept certs from issuers not in the list or could refuse valid certs from CAs in the list), it usually works this way.
By default, the certificate chooser in the X509KeyManager configured within the SSLContext (again you normally don't have to worry about it), will pick one of the certificates that has been issued by one in the list (or can be chained to an issuer there).
That list is the issuers parameter in X509KeyManager.chooseClientAlias (the alias is the alias name for the cert you want to picked, as referred to within the keystore). If you have multiple candidates, you can also use the socket parameter, which will get you the peer's IP address if that helps making a choice.
If this helps, you may find using jSSLutils (and its wrapper) for the configuration of your SSLContext (these are mainly helper classes to build SSLContexts). (Note that this example is for choosing the server-side alias, but it can be adapted, the source code is available.)
Once you've done this, you should look for the documentation regarding the axis.socketSecureFactorysystem property in Axis (and SecureSocketFactory). If you look at the Axis source code, it shouldn't be too difficult to build a org.apache.axis.components.net.SunJSSESocketFactory that's initialized from the SSLContext of your choice (see this question).
Just realized you were talking about Axis2, where the SecureSocketFactory seems to have disappeared. You might be able to find a workaround using the default SSLContext, but this will affect your entire application (which isn't great). If you use a X509KeyManagerWrapper of jSSLutils, you might be able to use the default X509KeyManager and treat only certain hosts as an exception. (This is not an ideal situation, I'm not sure how to use a custom SSLContext/SSLSocketFactory in Axis 2.)
Alternatively, according to this Axis 2 document, it looks like Axis 2 uses Apache HTTP Client 3.x:
If you want to perform SSL client
authentication (2-way SSL), you may
use the Protocol.registerProtocol
feature of HttpClient. You can
overwrite the "https" protocol, or use
a different protocol for your SSL
client authentication communications
if you don't want to mess with regular
https. Find more information at
http://jakarta.apache.org/commons/httpclient/sslguide.html
In this case, the SslContextedSecureProtocolSocketFactory should help you configure an SSLContext.
Java SSL clients will only send a certificate if requested by the server. A server can send an optional hint about what certificates it will accept; this will help a client choose a single certificate if it has multiple.
Normally, a new SSLContext is created with a specific client certificate, and Socket instances are created from a factory obtained from that context. Unfortunately, Axis2 doesn't appear to support the use of an SSLContext or a custom SocketFactory. Its client certificate settings are global.
I initialized EasySSLProtocolSocketFactory and Protocol instances for different endpoints and register the protocol with unique key like this:
/**
* This method does the following:
* 1. Creates a new and unique protocol for each SSL URL that is secured by client certificate
* 2. Bind keyStore related information to this protocol
* 3. Registers it with HTTP Protocol object
* 4. Stores the local reference for this custom protocol for use during furture collect calls
*
* #throws Exception
*/
public void registerProtocolCertificate() throws Exception {
EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory();
easySSLPSFactory.setKeyMaterial(createKeyMaterial());
myProtocolPrefix = (HTTPS_PROTOCOL + uniqueCounter.incrementAndGet());
Protocol httpsProtocol = new Protocol(myProtocolPrefix,(ProtocolSocketFactory) easySSLPSFactory, port);
Protocol.registerProtocol(myProtocolPrefix, httpsProtocol);
log.trace("Protocol [ "+myProtocolPrefix+" ] registered for the first time");
}
/**
* Load keystore for CLIENT-CERT protected endpoints
*/
private KeyMaterial createKeyMaterial() throws GeneralSecurityException, Exception {
KeyMaterial km = null;
char[] password = keyStorePassphrase.toCharArray();
File f = new File(keyStoreLocation);
if (f.exists()) {
try {
km = new KeyMaterial(keyStoreLocation, password);
log.trace("Keystore location is: " + keyStoreLocation + "");
} catch (GeneralSecurityException gse) {
if (logErrors){
log.error("Exception occured while loading keystore from the following location: "+keyStoreLocation, gse);
throw gse;
}
}
} else {
log.error("Unable to load Keystore from the following location: " + keyStoreLocation );
throw new CollectorInitException("Unable to load Keystore from the following location: " + keyStoreLocation);
}
return km;
}
When I have to invoke the web service, I do this (which basically replace "https" in the URL with https1, or https2 or something else depending on the Protocol you initialized for that particular endpoint):
httpClient.getHostConfiguration().setHost(host, port,Protocol.getProtocol(myProtocolPrefix));
initializeHttpMethod(this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix));
It works like a charm!