How can I get the Client Certificate in Netty Handler to identify user? - ssl

I am successfully running Netty with 2-way SSL (see Set up Netty with 2-way SSL Handsake (client and server certificate)).
However, in some of my handlers, I need to know about the user who is using the application. I find that I can't figure out how to get information like the user certificate DN in my handlers.
I would think it would be available in the ChannelHandlerContext somewhere but it is not. Any suggestions?
I know the SSLEngine has access to it somewhere, but I don't see anything about obtaining access in the SSLEngine public API. I know it has access in the handshake operation.... but how do I get it?

The SSLEngine can be fetched through the Pipline/ChannelHandlerContext
ChannelHandlerContext ctx = ...
SslHandler sslhandler = (SslHandler) ctx.channel().pipeline().get("ssl");
sslhandler.engine().getSession().getPeerCertificateChain()[0].getSubjectDN());
This allows you to get the certificates in the Handler Objects. Pay attention, that the SSL-Handshake needs to be finished when you do this. Otherwise you will get a
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
exception. To avoid this, you can listen for a userEvent (in our case HandshakeCompletionEvent) in the handler, which could look the following:
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
logger.info("userEventTriggered: {0}, Class: {1}", evt.toString(), evt.getClass());
if (evt instanceof HandshakeCompletionEvent) {
fetchCertificate(ctx);
}
}

SSLEngine.getSession().getPeerCertificateChain(). The zeroth entry is the peer's own certificate.

I used the following codes to get the client certificate and certificate's issuer. I hope it helps.
SslHandler sslHandler = (SslHandler) ctx.channel().pipeline().get("ssl");
X509Certificate issuer = convert(sslHandler.engine().getSession().getPeerCertificateChain()[sslHandler.engine().getSession().getPeerCertificateChain().length -1]);
System.out.println("issuer: " + issuer);
public static java.security.cert.X509Certificate convert(javax.security.cert.X509Certificate cert) {
try {
byte[] encoded = cert.getEncoded();
ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
java.security.cert.CertificateFactory cf
= java.security.cert.CertificateFactory.getInstance("X.509");
return (java.security.cert.X509Certificate)cf.generateCertificate(bis);
} catch (java.security.cert.CertificateEncodingException e) {
} catch (javax.security.cert.CertificateEncodingException e) {
} catch (java.security.cert.CertificateException e) {
}
return null;
}

Related

Host name check in Custom Trust Manager

We have a java client that allows both secure and non-secure connections to LDAP hosts.
It comes as part of a software suite which
has its own server component.
We are good with non-secure connections but need to switch to secure only.
The trusted public certificates are maintained (root+intermediate+host are copy pasted into one PEM file) in a
central location with the server component external to the clients.
The custom trust manager downloads the externally held trusted certificates on demand
and builds the trusted certificate chain. This way, I guess, it avoids pre-saving the trusted certicate chain in each client.
Our LDAP hosts are load balanced and that setup has not gone well with the trust manager. When we investigated, we found two questionable lines
in the code.
An environment variable to by-pass the host name verification.
if ("T".equals(System.getenv("IGNORE_HOSTNAME_CHECK"))) return true;
It seems like doing something similar to below which I have seen elsewhere.
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
Host name check relies on CN value of subject alone.
if (this.tgtHostname.equalsIgnoreCase(leafCn)) return true;
I have skimmed through some RFCs related to TLS and have come across SNI, SAN:DNSName and MITM warnings
but my rudimentary knowledge is not enough to make a case one way or the other.
Any advice on improvements (or against the use of it altogether) around commented out lines labelled H1 and H2 will be greatly valued.
I intend to pass them on to the right entity later.
The cut-down version of checkServerTrusted() of the custom trust manager is pasted below.
public void checkServerTrusted(X509Certificate[] certsRcvdFromTgt, String authType) throws CertificateException
{
// Some stuff
// Verify that the last certificate in the chain corresponds to the tgt server we want to access.
checkLastCertificate(certsRcvdFromTgt[certsRcvdFromTgt.length - 1]);
// Some more stuff
}
private boolean checkLastCertificate(X509Certificate leafCert) throws CertificateException
{
// need some advice here ... (H1)
if ("T".equals(System.getenv("IGNORE_HOSTNAME_CHECK"))) return true;
try
{
String leafCn = null;
X500Principal subject = leafCert.getSubjectX500Principal();
String dn = subject.getName();
LdapName ldapDN = new LdapName(dn);
for (Rdn rdn : ldapDN.getRdns())
{
if (rdn.getType().equalsIgnoreCase("cn"))
{
leafCn = rdn.getValue().toString();
break;
}
}
// need some advice here ... (H2)
if (this.tgtHostname.equalsIgnoreCase(leafCn)) return true;
}
catch (InvalidNameException e){/*error handling*/}
throw new CertificateException("Failed to verify that the last certificate in the chain is for target " + this.tgtHostname);
}

Intermittent peer not authenticated - when trusting all the certificates

One more to the list of the mysterious "peer not authenticated".
I have an apache httpclient using 4.2 lib. I have explicitly set to trust all certificates in the code.
I have a Tomcat server (JRE 1.7U45), serving the requests on Linux. The server has a self signed certificate.
Client side code:
private DefaultHttpClient getHttpsClient() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
final SSLSocketFactory sf;
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
}
} }, new SecureRandom());
sf = new SSLSocketFactory(sslContext,
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme(url.getScheme(), url.getPort(), sf));
ClientConnectionManager cm = new BasicClientConnectionManager(
registry);
return new MyDefaultHttpClient(cm);
} catch (Exception e) {
return new MyDefaultHttpClient();
}
}
This error is only seen intermittently on "Solaris 5.10" (32 bit JRE 1.7.0u45) clients talking to the server.
Sometime, the request on the same box go thru fine, but at other times, this just throws "Peer Not Authenticate"
I have other flavors of OS clients, where the call is going thru just fine.
Would any of have any suggestions/pointers to look into this issue?
More Update:
Ran the ssl debug on the server and we see that intermittently, it throws
http-bio-8443-exec-7, handling exception: javax.net.ssl.SSLHandshakeException: Invalid Padding length: 105
http-bio-8443-exec-7, IOException in getSession(): javax.net.ssl.SSLHandshakeException: Invalid Padding length: 105
This was due the following bug in JRE 1.7 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8013059
Also, the apache httpclient 4.2 added to the confusion, where it masking the actual exception thrown instead throwing the generic "Peer not authenticated"
In the server.xml of tom-cat, for connector element, add the cipher attribute with a list of non-DH ciphers
E.g.
ciphers="SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"
This solved the issue.
Hope this is useful to someone.
Thanks

Read SSL Certificate Details on WP8

I want to read certificate details (e.g. expiration date or CN) for security reasons.
Usually there are some properties in network classes available, that allow to check the certificate. This is missing in WP8 implementations.
Also I tried to create an SslStream but also there is no way to get any certificate detail like the RemoteCertificate on .net 4.5.
var sslStream = new SslStream(new NetworkStream(e.ConnectSocket));
The SslStream is missing everything relating security. So it looks like also BountyCastle and other libraries cannot be able to get the certificate, because the underlying framework doesn't support it.
So my questions are:
Can I read the CN or other Certificate details on WP8 using other approaches.?
If not, how can you create then seriously secure apps (line banking) on WP8 using techniques like SSL Pinning or client side certificate validation and is there any reason why this is not supported in WP8?
Regards
Holger
I issued a user voice request to Microsoft .NET team asking them to provide a solution for reading server SSL certificate details from portable class libraries (targeting also WP8). You can vote it here: http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4784983-support-server-ssl-certificate-chain-inspection-in
On Windows Phone 8.1 this can be done with HttpClient, as well as with StreamSocket (as Mike suggested).
Example for certificate validation with StreamSocket can be found here (Scenario5_Certificate in source code).
Certificate validation with HttpClient can be done by handling the ERROR_INTERNET_INVALID_CA exception, validating the server certificate using the HttpTransportInformation class, creating new instance of HttpBaseProtocolFilter class and specifying the errors to ignore.
Note that not all the errors are ignorable. You will receive an exception if you'll try to add Success, Revoked,
InvalidSignature, InvalidCertificateAuthorityPolicy, BasicConstraintsError, UnknownCriticalExtension or OtherErrors enum values.
I'm adding a sample code that bypasses certificate errors using HttpClient:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Security.Cryptography.Certificates;
using Windows.Web.Http;
using Windows.Web.Http.Filters;
namespace Example.App
{
public class HttpsHandler
{
private const int ERROR_INTERNET_INVALID_CA = -2147012851; // 0x80072f0d
public static async void HttpsWithCertificateValidation()
{
Uri resourceUri;
if (!Uri.TryCreate("https://www.pcwebshop.co.uk/", UriKind.Absolute, out resourceUri))
return;
IReadOnlyList<ChainValidationResult> serverErrors = await DoGet(null, resourceUri);
if (serverErrors != null)
{
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
foreach (ChainValidationResult value in serverErrors)
{
try {
filter.IgnorableServerCertificateErrors.Add(value);
} catch (Exception ex) {
// Note: the following values can't be ignorable:
// Success Revoked InvalidSignature InvalidCertificateAuthorityPolicy
// BasicConstraintsError UnknownCriticalExtension OtherErrors
Debug.WriteLine(value + " can't be ignorable");
}
}
await DoGet(filter, resourceUri);
}
}
private static async Task<IReadOnlyList<ChainValidationResult>> DoGet(HttpBaseProtocolFilter filter, Uri resourceUri)
{
HttpClient httpClient;
if (filter != null)
httpClient = new HttpClient(filter);
else
httpClient = new HttpClient();
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, resourceUri);
bool hadCertificateException = false;
HttpResponseMessage response;
String responseBody;
try {
response = await httpClient.SendRequestAsync(requestMessage);
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
} catch (Exception ex) {
hadCertificateException = ex.HResult == ERROR_INTERNET_INVALID_CA;
}
return hadCertificateException ? requestMessage.TransportInformation.ServerCertificateErrors : null;
}
}
}
After trying open source libs like bouncyCastle, supersocket or webSocket4net I tested an evaluation of a commercial lib named ELDOS SecureBlackbox. This test was successfull. Here is a code snipped, that gets the X509Certificates with all details:
public void OpenSSL()
{
var c = new TElSimpleSSLClient();
c.OnCertificateValidate += new TSBCertificateValidateEvent(OnCertificateValidate);
c.Address = "myhostname.com";
c.Port = 443;
c.Open();
c.Close(false);
}
private void OnCertificateValidate(object sender, TElX509Certificate x509certificate, ref TSBBoolean validate)
{
validate = true;
}
The validation is getting all certificates... if validate is set to true, the next certificate will be shown. That means the callback is called forreach certificate there.
Regards
Holger
For WP8, you can use the StreamSocket class, which has an UpgradeToSslAsync() method that will do the TLS handshake for you as an async operation. Once that completes, you can use the .Information.ServerCertificate property to check that you got the server certificate you were expecting.

HTTP Client with https

What is the best way to process HTTP GET Method with SSL using HTTP Components HTTPClient 4 Project?
what is the best way to parametrized certification info? properties file? reload method to Daemon Service?
HttpClient httpClient = new DefaultHttpClient();
String url = "https://xxx.190.2.45/index.jsp";
HttpGet get = new HttpGet(url);
try {
//TODO
HTTPHelper.addSSLSupport(httpClient);
HttpResponse response = httpClient.execute(get);
BasicResponseHandler responseHandler = new BasicResponseHandler();
String responseString = responseHandler.handleResponse(response);
} catch (ClientProtocolException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
You'll need to enable the SSL support, see the tutorial for more information
I'm under the impression that you're using a self-signed certificate for the server. What you probably should do is look at getting openssl, generate yourself a CA & server certificate. Put the CA certificate (not the private key) in a "trust store" and configure the socket factory.
If you need more detail on how to do this, just comment on this and I'll flesh out some more. I've had great success with simple local projects!

Clojure and SSL/x.509 certs quetion

I need to write a simple program for work that does the following:
read a config file
connect to a bunch of servers
establish a ssl socket
pull info form the server's x509 cert, expire date and hostname for now
email a report when its done
items 3 and 4 are things that I have had bad luck researching/googleing and I do not know java well, at all since 1.2 around 2001
A verbose but throughout guide about the inners of Java Cryptographic Extension is found at Oracles website as well: http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html
I found a code snipit that tells me what I need to know about java at http://www.exampledepot.com/egs/javax.net.ssl/GetCert.html
here it is:
try {
// Create the client socket
int port = 443;
String hostname = "hostname";
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket(hostname, port);
// Connect to the server
socket.startHandshake();
// Retrieve the server's certificate chain
java.security.cert.Certificate[] serverCerts =
socket.getSession().getPeerCertificates();
// Close the socket
socket.close();
} catch (SSLPeerUnverifiedException e) {
} catch (IOException e) {
} catch (java.security.cert.CertificateEncodingException e) {
}