Validate TLS server certificate with BouncyCastle's "lightweight" API - java

I'm using Java BouncyCastle so-called "lightweight" API to establish a TLS connection over a TCP socket.
I want to verify server-provided certificate chain being signed by one of trusted CAs. Sounds like a reasonably common task that every sane TLS client implementation out there does by default, so I expect this should be simple.
For a sake of simplifying the question, I don't ask about verifying anything other than the sign/trust chain, like matching hostname or checking expiration date. Such checks seem trivial to implement.
If I understand the documentation correctly, there's a TlsAuthentication interface, that users are supposed to implement. The sole provided implementation is LegacyTlsAuthentication, which adapts upon now-deprecated CertificateVerifyer interface, which has only AlwaysValidVerifyer implementation (that's just dummy "return true;" under the hood).
So, this is what I have for now:
DefaultTlsClient tlsClient = new DefaultTlsClient() {
#Override
public TlsAuthentication getAuthentication() throws IOException {
TlsAuthentication auth = new TlsAuthentication() {
#Override
public void notifyServerCertificate(Certificate serverCertificate) {
// Here I should validate certificate chain, but this far
// I only managed to print subjects for debugging purposes.
for (org.bouncycastle.asn1.x509.Certificate c : serverCertificate.getCerts()) {
System.out.println("Certificate: " + c.getSubject().toString());
}
}
#Override
public TlsCredentials getClientCredentials(CertificateRequest cr) throws IOException {
return null;
}
};
return auth;
}
};
socket = new Socket(hostname, port);
tlsHandler = new TlsProtocolHandler(socket.getInputStream(), socket.getOutputStream());
tlsHandler.connect(tlsClient);
However, I fail to understand or find any existing example that would check one org.bouncycastle.asn1.x509.Certificate for being correctly signed by another one. Could someone provide some pointers to me, please?
I'm using BounceCastle's proprietary API due to need to use ciphersuites that default Java installations do not allow due to being subject to US cryptographic policy jurisdiction restrictions. For example, AES256 encryption requires installing unlimited strength policy files, and I'd really like to avoid additional end-user installation steps, if possible.

The isSignatureValid method of the X509CertificateHolder class should work for you. This method takes in 1 parameter, a ContentVerifierProvider. You can create an X509CertificateHolder by passing a Certificate into the constructor.
The following code is taken from BC's version 2 API page and should give you a good idea in how to implement this in your solution.
ContentVerifierProvider contentVerifierProvider =
new BcRSAContentVerifierProviderBuilder(
new DefaultDigestAlgorithmIdentifierFinder()).build(lwPubKey);
if (!certHolder.isSignatureValid(contentVerifierProvider))
{
System.err.println("signature invalid");
}
"lwPubKey" is the public key of the signer. So depending on how long your certificate chain is, you would repeatedly call this method starting with the end entity certificate and going up the line to the self-signed root certificate.

Wow what a bunch of deprecated classes. I feel for ya.
Where you need to get to is
http://www.cs.berkeley.edu/~jonah/bc/org/bouncycastle/asn1/x509/KeyUsage.html#keyCertSign
The keyCertSign will be set for a CA issues cert.
You code above serverCertificate.getCerts() will return an array of X509CertificateStructure objects (your chain) for each of these you can call toASN1Object() to get the interface DerEncodable / KeyUsage
I hope this helps.

Related

Is it possible to check for a specific cipher block mode support without providing a whole transform?

I have a unit test which passes in Java 8+ and fails on Java 7 because GCM mode doesn't seem to be supported in Java 7 and earlier.
I know that I can attempt to Create a cipher with the transform e.g. AES/GCM/PKCS5Padding and catch NoSuchAlgorithmException, but that exception might be thrown just for that particular transform and not just because GCM mode itself isn't supported (within any transform).
I can also just check for the JVM version, but that wouldn't be a valid check for an environment where a 3rd-party crypto library is in use (e.g. BouncyCastle) or a JVM with built-in support from another vendor which happens to include support for GCM.
I'd prefer to skip the test only if GCM in general is not supported, and not just if the particular (and complete) transform I choose in my code is not supported.
Is it possible to detect supported cipher block modes from Java? Or is it only possible to try a particular transform and see if it works?
Yes, you can traverse through the providers and services and look for a service that is a cipher and contains GCM in the name, e.g.
Provider[] provs = Security.getProviders();
for (Provider prov : provs) {
Set<Service> services = prov.getServices();
for (Service service : services) {
if (!service.getType().matches("(?i)Cipher")) {
break;
}
String algo = service.getAlgorithm();
if (algo.matches("(?i).*/GCM/.*")) {
System.out.println(service);
}
}
}
Beware that you may want to check for unlimited crypto for older Java runtimes, e.g. using my answer here.
You may want to consider that GCM is only valid for 128 bit ciphers, and that it is unlikely that you'll find implementations that do not use AES. Furthermore, there aren't any parameters other than "NoPadding" that make sense for GCM mode (in the algorithm string anyway, I'm not talking about GCCMParameterSpec of course).
Beware that later providers may not return "AES/GCM/NoPadding" but return "AES_128/GCM/NoPadding", "AES_192/GCM/NoPadding" and "AES_256/GCM/NoPadding" instead. This also influences, the Provider#getService(type, algorithm) call, rendering it useless in case you want to check for "AES/GCM/NoPadding", i.e. AES with any valid key size.

Java Kerberos set delegate flag using logincontext

I am using the apache httpcomponents 4.5.3. and the java Krb5LoginModule to handle Kerberos authentication to IIS. IIS is set to only accept Kerberos authentication. This is working as expected for the single hop scenario. I need to have the kerberos ticket marked as "ok to delegate" so it will go from IIS to an SSAS server. I have a python and .net library working with Kerberos on the same IIS server, so I know that it works. The java library needs to have the "ok to delegate" flag set, but I cannot figure out how to set it. I am using java's logincontext and apache's httpclient to make the calls.
To give you an idea of the code, here it is:
LoginContext context = null;
try {
// Create a LoginContext with a callback handler
context = new LoginContext(name, callbackHandler);
// Perform authentication
context.login();
} catch (LoginException e) {
System.err.println("Login failed");
e.printStackTrace();
System.exit(-1);
}
// Perform action as authenticated user
Subject subject = context.getSubject();
if (verbose) {
System.out.println(subject.toString());
} else {
System.out.println("Authenticated principal: " +
subject.getPrincipals());
}
Subject.doAs(subject, action);
The solution I used for quick and dirty was to update the apache httpcomponents library. I downloaded the source code and modified one line of code to make the double hop scenario work. In the class GGSSchemeBase.java, the function createGSSContext, I added a line at line no 138, which reads:
gssContext.requestCredDeleg(true);
This now forces every kerberos interaction to create a ticket with credential delegation. For my needs, this is sufficient. Ideally, one of the higher level classes could be modified to take in additional GSS flags. However, that is a fight for another day.
As state here, you can extends the SPNegoScheme class (which extends GGSSchemeBase) and override the method generateGSSToken which calls createGSSContext. You have to create a new createGSSContext method and add 'gssContext.requestCredDeleg(true);'.
Then you can create your HttpClient with a builder like this:
HttpClientBuilder builder = HttpClientBuilder.create();
Lookup<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create().register(AuthSchemes.SPNEGO, new YourOwnSPNegoSchemeFactory()).build();
builder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
YourOwnSPNegoSchemaFactory is a class which extends SPNegoSchemaFactory and is capable of creating and instantiating your own extension of SPNegoSchema.

Java GSS-API Service Ticket not saved in Credentials Cache using Java

I have created 2 demo Kerberos Clients using the GSS-API.
One in Python3, the second in Java.
Both clients seem to be broadly equivalent, and both "work" in that I get a service ticket that is accepted by my Java GSS-API Service Principal.
However on testing I noticed that the Python client saves the service ticket in the kerberos credentials cache, whereas the Java client does not seem to save the ticket.
I use "klist" to view the contents of the credential cache.
My clients are running on a Lubuntu 17.04 Virtual Machine, using FreeIPA as the Kerberos environment. I am using OpenJDK 8 u131.
Question 1: Does the Java GSS-API not save service tickets to the credentials cache? Or can I change my code so it does so?
Question 2: Is there any downside to the fact that the service ticket is not saved to the cache?
My assumption is that cached service tickets reduce interaction with the KDC, but comments on How to save Kerberos Service Ticket using a Windows Java client? suggest that is not the case, but this Microsoft technote says "The client does not need to go back to the KDC each time it wants access to this particular server".
Question 3: The cached service tickets from the python client vanish after some minutes - long before the expiry date. What causes them to vanish?
Python code
#!/usr/bin/python3.5
import gssapi
from io import BytesIO
server_name = 'HTTP/app-srv.acme.com#ACME.COM'
service_name = gssapi.Name(server_name)
client_ctx = gssapi.SecurityContext(name=service_name, usage='initiate')
initial_client_token = client_ctx.step()
Java Code
System.setProperty("java.security.krb5.conf","/etc/krb5.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
GSSManager manager = GSSManager.getInstance();
GSSName clientName;
GSSContext context = null;
//try catch removed for brevity
GSSName serverName =
manager.createName("HTTP/app-srv.acme.com#ACME.COM", null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
//use default credentials
context = manager.createContext(serverName,
krb5Oid,
null,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(false);
context.requestConf(false);
context.requestInteg(true);
byte[] token = new byte[0];
token = context.initSecContext(token, 0, token.length);
Edit:
While the original question focusses on the use of the Java GSS-API to build a Java Kerberos Client, GSS is not a must. I am open to other Kerberos approaches that work on Java. Right now I am experimenting with Apache Kerby kerb-client.
So far Java GSS-API seems to have 2 problems:
1) It uses the credentials cache to get the TGT (Ok), but not to cache service-tickets (Not Ok).
2) It cannot access credential caches of type KEYRING. (Confirmed by behaviour, debugging the Java runtime security classes, and by comments in that code. For the Lubuntu / FreeIPA combination I am using KEYRING was the out-of-the-box default. This won't apply to Windows, and may not apply to other Linux Kerberos combinations.
Edit 2:
The question I should have asked is:
How do I stop my KDC from being hammered for repeated SGT requests because Java GSS is not using the credentials cache.
I leave my original answer in place at the bottom, because if largely focusses on the original question.
After another round of deep debugging and testing, I have found an acceptable solution to the root problem.
Using Java GSS API with JAAS, as opposed to "pure" GSS without JAAS in my original solution makes a big difference!
Yes, existing Service Tickets (SGTs) that may be in the credentials cache are not being loaded,
nor are any newly acquired SGTs written back to the cache, however the KDC is not be constantly hammered (the real problem).
Both pure GSS, and GSS with JAAS use a client principal subject. The subject has an in-memory privateCredentials set,
which is used to store TGTs and SGTs.
The key difference is:
"pure GSS": the subject + privateCredentials is created within the GSSContext, and lives only as long as the GSSContext lives.
GSS with JAAS: the subject is created by JAAS, outside the GSSContext, and thus can live for the life of the application,
spanning many GSSContexts during the life of the application.
The first GSSContext established will query the subject's privateCredentials for a SGT, not find one,
then request a SGT from the KDC.
The SGT is added to the subject's privateCredentials, and as the subject lives longer than the GSSContext,
it is available, as is the SGT, when following GSSContexts are created. These will find the SGT in the subject's privateCredentials, and do not need to hit the KDC for a new SGT.
So seen in the light of my particular Java Fat Client, opened once and likely to run for hours, everything is ok.
The first GSSContext created will hit the KDC for a SGT which will then be used by all following GSSContexts created until the client is closed.
The credentials cache is not being used, but that does not hurt.
In the light of a much shorter lived client, reopened many many times, and perhaps in parallel,
then use / non-use of the credentials cache might be a more serious issue.
private void initJAASandGSS() {
LoginContext loginContext = null;
TextCallbackHandler cbHandler = new TextCallbackHandler();
try {
loginContext = new LoginContext("wSOXClientGSSJAASLogin", cbHandler);
loginContext.login();
mySubject = loginContext.getSubject();
} catch (LoginException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
gssManager = GSSManager.getInstance();
try {
//TODO: LAMB: This name should be got from config / built from config / serviceIdentifier
serverName = gssManager.createName("HTTP/app-srv.acme.com#ACME.COM", null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
} catch (GSSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String getGSSwJAASServiceToken() {
byte[] token = null;
String encodedToken = null;
token = Subject.doAs(mySubject, new PrivilegedAction<byte[]>(){
public byte[] run(){
try{
System.setProperty("javax.security.auth.useSubjectCredsOnly","true");
GSSContext context = gssManager.createContext(serverName,
krb5Oid,
null,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(false);
context.requestConf(false);
context.requestInteg(true);
byte[] ret = new byte[0];
ret = context.initSecContext(ret, 0, ret.length);
context.dispose();
return ret;
} catch(Exception e){
Log.log(Log.ERROR, e);
throw new otms.util.OTMSRuntimeException("Start Client (Kerberos) failed, cause: " + e.getMessage());
}
}
});
encodedToken = Base64.getEncoder().encodeToString(token);
return encodedToken;
}
End Edit 2: Original answer below:
Question 1: Does the Java GSS-API not save service tickets to the credentials cache? Or can I change my code so it does so?
Edit: Root Cause Analysis.
After many hours debugging the sun.security.* classes, I now understand what GSS and Java Security code is doing / not doing - at least in Java 8 u 131.
In this example we have a credential cache, of a type Java GSS can access, containing a valid Ticket Granting Ticket (TGT) and a valid Service Ticket (SGT).
1) When the client principal Subject is created, the TGT is loaded from the cache (Credentials.acquireTGTFromCache()), and stored in the privateCredentials set of the Subject. --> (OK)
Only the TGT is loaded, SGTs are NOT loaded and saved to the Subject privateCredentials. -->(NOT OK)
2) Later, deep in the GSSContext.initSecContext() process, the security code actually tries to retrieve a Service Ticket from the privateCredentials of the Subject. The relevant code is Krb5Context.initSecContext() / KrbUtils.getTicket() / SubjectComber.find()/findAux(). However as SGTs were never loaded in step 1) an SGT will not be found! Therefore a new SGT is requested from the KDC and used.
This is repeated for each Service request.
Just for fun, and strictly as a proof-of-concept hack, I added a few lines of code between the login, and the initSecContext() to parse the credentials cache, extract the credentials, convert to Krb Credentials, and add them to the Subject’s private credentials.
This done, in step 2) the existing SGT is found and used. No new SGT is requested from the KDC.
I will not post the code for this hack as it calls sun internal classes that we should not be calling, and I don’t wish to inspire anybody else to do so. Nor do I intend to use this hack as a solution.
—> The root cause problem is not that the service ticket are not SAVED to the cache; but rather
a) that SGTs are not LOADED from the credential cache to the Subject of the client principal
and
b) that there is no public API or configuration settings to do so.
This affects GSS-API both with and without JAAS.
So where does this leave me?
i) Use Java GSS-API / GSS-API with JAAS “as is”, with each SGT Request hitting the KDC —> Not good.
ii) As suggested by Samson in the comments below, use Java GSS-API only for initial login of the application, then for all further calls use an alternative security mechanism for subsequent calls (a kind of self-built kerberos-light) using tokens or cookies.
iii) Consider alternatives to GSS-API such as Apache Kerby kerb-client. This has implications outside the scope of this answer, and may well prove to be jumping from the proverbial frying pan to the fire.
I have submitted a Java Feature Request to Oracle, suggesting that SGTs should be retrieved from the cache and stored in the Subject credentials (as already the case for TGTs).
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180144
Question 2: Is there any downside to the fact that the service ticket is not saved to the cache?
Using the credentials cache for Service Tickets reduces interaction between the client and the KDC. The corollary to this is that where service tickets are not cached, each request will require interaction with the KDC, which could lead to the KDC being hammered.

Authentication with public/private key signatures: What's a good message (digest)?

I'm build an application with a client/server infrastructure and would like to implement an authentication mechanism using the public/private key method.
Let's assume that a client owns the private key and the server only has the public key. During authentication the client signs a message with the private key, sends it to the server where it's validated with the public key. If validation succeeds the client is authenticated.
Here's some JUnit test code where I made myself familiar with the concepts:
#Test
public void testSignature() throws Exception {
final String message = "Hello world is a stupid message to be signed";
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
final Signature privSig = Signature.getInstance("SHA1withRSA");
privSig.initSign(keyPair.getPrivate());
privSig.update(message.getBytes());
byte[] signature = privSig.sign();
final Signature pubSig = Signature.getInstance("SHA1withRSA");
pubSig.initVerify(keyPair.getPublic());
pubSig.update(message.getBytes());
assertTrue(pubSig.verify(signature));
}
Of course in order for this to work both server and clients must be in possession of the plain message (digest).
Now my question is: What is a good message (digest) to be used for the signature? For example can this be a static, hardcoded string (which is used for all clients) or would this impose some kind of security issue on this concept? If a static string is bad would it be a good idea to negotiate some random string before authentication? This random string could be used as a "session" key for example and invalidated after some time.
The static String would be bad since its susceptible to a repeat attack (The signed string would be the same every time).
Although, you seem to be reinventing what has been done before. Using certificates is the trusted way. See this example for more info on it: Java HTTPS client certificate authentication
If you want to implement it yourself you'll probably need to read up on how SSL works and mimic that. Any other solution is likely to have some flaws in it, unless it was very bespoke (e.g. your client would keep a list of 1000's of shared static strings which it never reused, and the server had that same list and kept track of what had been used. or alternatively, keep track of a shared number which increments, as suggested in the comments below.)

TLS with SNI in Java clients

There is an ongoing discussion on the security and trust working group for NHIN Direct regarding the IP-to-domain mapping problem that is created with traditional SSL. If an HISP (as defined by NHIN Direct) wants to host thousands of NHIN Direct "Health Domains" for providers, then it will an "artificially inflated cost" to have to purchase an IP for each of those domains.
Because Apache and OpenSSL have recently released TLS with support for the SNI extension, it is possible to use SNI as a solution to this problem on the server side. However, if we decide that we will allow server implementations of the NHINDirect transport layer to support TLS+SNI, then we must require that all clients support SNI too. OpenSSL based clients should do this by default and one could always us stunnel to implement an TLS+SNI aware client to proxy if your given programming language SSL implementation does not support SNI. It appears that native Java applications using OpenJDK do not yet support SNI, but I cannot get a straight answer out of that project. I know that there are OpenSSL Java libraries available but I have no idea if that would be considered viable.
Can you give me a "state of the art" summary of where TLS+SNI support is for Java clients? I need a Java implementers perspective on this.
JavaSE 7 has SNI Support in JSSE.
http://docs.oracle.com/javase/7/docs/technotes/guides/security/enhancements-7.html
Note, there seems to be a problem with it, as you can read here:
SSL handshake alert: unrecognized_name error since upgrade to Java 1.7.0
it is also possible to patch with some lines the orig Sun JDK (bootclasspath) to get Server SNI working.
Class: sun.security.ssl.ServerHandshaker
Add Field
/** Use for SNI */
private ServerNameExtension serverNameExtension = null;
Patch Method clientHello (add these lines)
/* Use for SNI */
this.serverNameExtension = (ServerNameExtension)mesg.extensions.get(ExtensionType.EXT_SERVER_NAME);
Patch Method setupPrivateKeyAndChain (change)
if (this.conn != null) { alias = km.chooseServerAlias(algorithm , null, this.conn);
} else { alias = km.chooseEngineServerAlias(algorithm, null, this.engine); }
to
final Principal[] principals = (this.serverNameExtension == null) ? null : this.serverNameExtension.getHostnamePrincipals();
if (this.conn != null) { alias = km.chooseServerAlias(algorithm , principals, this.conn);
} else { alias = km.chooseEngineServerAlias(algorithm, principals, this.engine); }
Add to Class sun.security.ssl.ServerNameExtension
static final class ServerNamePrincipal implements Principal {
private final String name;
ServerNamePrincipal(final String name) { this.name = name; }
#Override public String getName() { return this.name; }
#Override public String toString() { return this.name; }
}
public Principal[] getHostnamePrincipals() {
final List<Principal> principals = new LinkedList<>();
for(final ServerName name : this.names) {
if(name.type == NAME_HOST_NAME) { principals.add(new ServerNamePrincipal(name.hostname)); }
}
return principals.toArray(new Principal[principals.size()]);
}
I'm working on the same project as ftrotter.
Note the requirement of support for thousands of domains. I don't think that SANs are going to cut the mustard for two reasons. First, the size of the certificate is going to get enormous, which will likely cause performance problems at a minimum. Second, these domains are going to come and go frequently, particularly in the early days of NHIN Direct. The operational burden of having to update the certificate every time a domain comes or goes, is going to be unacceptable, IMHO.
At ftrotter's request, I did some googling around on the subject of java, TLS and SNI, and other ways to implement what amounts to a named-based virtual hosting situation, with one certificate per virtual host. Here's what I've come up with:
JSSE (Java Secure Socket Extension) supports TLS, and has "partial support" for TLS+SNI. I have no idea what partial support means in this context. The commentary I'm seeing indicates that the support that exists is not adequate for doing named-based virtual hosts, which is basically what we need.
I've found one article that claims the JDK7 version of JSSE will support TLS+SNI (dated 11/20/2008), and I've found one that claims it won't (dated 2/27/2009). Neither is particularly authoritative.
Some of the folks working on OpenJDK 7 discussed the issues around adding SNI support to JSSE back in Feb-Mar 2009, including posting a source patch. (thread starts here:
http://www.mail-archive.com/security-dev#openjdk.java.net/msg00612.html). OpenJDK7 isn't going to be released any time before about September 2010. I have no idea when the Java 7 platform will be released.
There is nothing substantive on java.sun.com at all, so I really don't know what Sun's plans are at all.
There is apparently a different way to accomplish name-based virtual hosts which is apparently widely compatible, using a single certificate per hosting server which contains multiple common names and multiple subject alt names. See http://wiki.cacert.org/VhostTaskForce and Serve different certs for same Tomcat application via connectors?
This approach would create really large certificates (due to all those CNs and SANs) if you have lots of virtual hosts. One of the folks at NHIN Direct's recent face-to-face meeting was talking about wanting to support thousands of virtual hosts. My guess is that this will break a lot of implementations. In addition, having to update the certificate each time you add or remove a virtual host sounds like a ridiculous operational burden.
In summary, the current Java state of the art for name-based virtual hosting with separate certificates per virtual host appears to be "no can do". In addition, it's not clear when or if it will be added.
Does anyone agree or disagree? Does anyone know if the OpenJDK project has any intention of "backporting" SNI support for Java 6?

Categories