Create Ssl Connection Using Certificate

How to generate a self-signed SSL certificate using OpenSSL?

You can do that in one command:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365

You can also add -nodes (short for "no DES") if you don't want to protect your private key with a passphrase. Otherwise it will prompt you for "at least a 4 character" password.

The days parameter (365) you can replace with any number to affect the expiration date. It will then prompt you for things like "Country Name", but you can just hit Enter and accept the defaults.

Add -subj '/CN=localhost' to suppress questions about the contents of the certificate (replace localhost with your desired domain).

Self-signed certificates are not validated with any third party unless you import them to the browsers previously. If you need more security, you should use a certificate signed by a certificate authority (CA).

SSL connection using certificate

I remember doing several things like installing keytool in eclipse, importing certificates into eclipse

Also importing manually in cmd to the cacerts file in the JDK folder under lib/security
http://magicmonster.com/kb/prg/java/ssl/pkix_path_building_failed.html

Lastly adding this parameter in the run config for my maven project -Djavax.net.debug=all in eclipse for debugging

Create SSL connection using certificate

If this is a self signed certificate to be used in server trust authentication, you should do the following:

  1. Convert the .CRT encoded certificate into a .DER encoded certificate. On the terminal type:

    $: openssl x509 -in certificate.crt -outform der -out "com.server.trust_cert.der"

    (choose your own meaningful name)

    Put the .DER encoded certificate into the bundle.

  2. Implement the method connection:didReceiveAuthenticationChallenge: as follows.
    Important: always check for errors and bail out and let the authentication fail if anything is wrong!!

    Test it thoroughly!



- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
{
do
{
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
if (serverTrust == nil)
break; // failed

SecTrustResultType trustResult;
OSStatus status = SecTrustEvaluate(serverTrust, &trustResult);
if (!(errSecSuccess == status))
break; // fatal error in trust evaluation -> failed

if (!((trustResult == kSecTrustResultProceed)
|| (trustResult == kSecTrustResultUnspecified)))
{
break; // see "Certificate, Key, and Trust Services Reference"
// for explanation of result codes.
}

SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
if (serverCertificate == nil)
break; // failed

CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate);
if (serverCertificateData == nil)
break; // failed

const UInt8* const data = CFDataGetBytePtr(serverCertificateData);
const CFIndex size = CFDataGetLength(serverCertificateData);
NSData* server_cert = [NSData dataWithBytes:data length:(NSUInteger)size];
CFRelease(serverCertificateData);

NSString* file = [[NSBundle mainBundle] pathForResource:@"com.server.trust_cert"
ofType:@"der"];
NSData* my_cert = [NSData dataWithContentsOfFile:file];

if (server_cert == nil || my_cert == nil)
break; // failed

const BOOL equal = [server_cert isEqualToData:my_cert];
if (!equal)
break; // failed

// Athentication succeeded:
return [[challenge sender] useCredential:[NSURLCredential credentialForTrust:serverTrust]
forAuthenticationChallenge:challenge];
} while (0);

// Authentication failed:
return [[challenge sender] cancelAuthenticationChallenge:challenge];
}
}

Note:

A possible improvement of the above technique is to use "public key pinning".

Must Reads:

HTTPS Server Trust Evaluation (Official Apple Documentation, Technical Note TN2232)

Certificate, Key, and Trust Services Reference (Official Apple Reference Documentation)

SSL connection with client-side certificate

The certificates in SSL/TLS are only used for authentication, the encryption itself is done by shared keys negotiated during the handshake.

If you want to use certificates, you'll always need at least the SSL/TLS server to have a certificate (which may be the TCP client). You can indeed swap the role of the client and server when making the connection. That is, the SSL/TLS server doesn't have to be the TCP server, but can be the TCP client. See definition in the specification glossary:

  client
The application entity that initiates a TLS connection to a
server. This may or may not imply that the client initiated the
underlying transport connection. The primary operational
difference between the server and client is that the server is
generally authenticated, while the client is only optionally
authenticated.

However, doing so can lead to difficulties. Just like a server in a traditional SSL/TLS connection can't detect whether the request have been through a MITM (it's solely the client's responsibility to check the server certificate, without client-certificate authentication), making the TCP client be the SSL/TLS server makes it hard for the TCP client to know that it's talking to the TCP server it intended: the server could in fact be a MITM. You'd need to consider whether this suits your needs.

In Python, you should be able to choose the orientation of your SSL/TLS socket using the server_side parameter of ssl.wrap_socket.

The server would still have the private key for the certificate the
client uses.

This doesn't make sense at all. Private keys should be kept private by the party to which the certificate was issued.

Perhaps you're after a Pre-Shared Key mechanism instead.

Can I create my own root certificate to use for an SSL connection with Go? Is this safe?

A self-signed certificate is a special case of deploying your own PKI (pushed to the extreme where you only have one certificate in that PKI).

Both can be used to prevent MITM attacks, provided that the client can verify the server certificate using trust anchors (certificates) it knows in advance (and has been configured with them in a trusted manner).

You can create your own CA and configure your client to use its root CA certificate or, if you only have a very limited set of servers, you can usually configure clients to trust a specific certificate directly (self-signed or not).

You may be interested in these questions for links to tools:

  • Solutions to sign certificates
  • Recommendations for a Certificate Management tool for Linux
  • OpenSSL's CA.pl (useful, but may lack general management interfaces)

Once you've understood the basic principles, the technical aspects are not necessarily difficult, but part of the real difficulty is in the administration of your CA.

Java HTTPS client certificate authentication

Finally managed to solve all the issues, so I'll answer my own question. These are the settings/files I've used to manage to get my particular problem(s) solved;

The client's keystore is a PKCS#12 format file containing

  1. The client's public certificate (in this instance signed by a self-signed CA)
  2. The client's private key

To generate it I used OpenSSL's pkcs12 command, for example;

openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "Whatever"

Tip: make sure you get the latest OpenSSL, not version 0.9.8h because that seems to suffer from a bug which doesn't allow you to properly generate PKCS#12 files.

This PKCS#12 file will be used by the Java client to present the client certificate to the server when the server has explicitly requested the client to authenticate. See the Wikipedia article on TLS for an overview of how the protocol for client certificate authentication actually works (also explains why we need the client's private key here).

The client's truststore is a straight forward JKS format file containing the root or intermediate CA certificates. These CA certificates will determine which endpoints you will be allowed to communicate with, in this case it will allow your client to connect to whichever server presents a certificate which was signed by one of the truststore's CA's.

To generate it you can use the standard Java keytool, for example;

keytool -genkey -dname "cn=CLIENT" -alias truststorekey -keyalg RSA -keystore ./client-truststore.jks -keypass whatever -storepass whatever
keytool -import -keystore ./client-truststore.jks -file myca.crt -alias myca

Using this truststore, your client will try to do a complete SSL handshake with all servers who present a certificate signed by the CA identified by myca.crt.

The files above are strictly for the client only. When you want to set-up a server as well, the server needs its own key- and truststore files. A great walk-through for setting up a fully working example for both a Java client and server (using Tomcat) can be found on this website.

Issues/Remarks/Tips

  1. Client certificate authentication can only be enforced by the server.
  2. (Important!) When the server requests a client certificate (as part of the TLS handshake), it will also provide a list of trusted CA's as part of the certificate request. When the client certificate you wish to present for authentication is not signed by one of these CA's, it won't be presented at all (in my opinion, this is weird behaviour, but I'm sure there's a reason for it). This was the main cause of my issues, as the other party had not configured their server properly to accept my self-signed client certificate and we assumed that the problem was at my end for not properly providing the client certificate in the request.
  3. Get Wireshark. It has great SSL/HTTPS packet analysis and will be a tremendous help debugging and finding the problem. It's similar to -Djavax.net.debug=ssl but is more structured and (arguably) easier to interpret if you're uncomfortable with the Java SSL debug output.
  4. It's perfectly possible to use the Apache httpclient library. If you want to use httpclient, just replace the destination URL with the HTTPS equivalent and add the following JVM arguments (which are the same for any other client, regardless of the library you want to use to send/receive data over HTTP/HTTPS):

    -Djavax.net.debug=ssl
    -Djavax.net.ssl.keyStoreType=pkcs12
    -Djavax.net.ssl.keyStore=client.p12
    -Djavax.net.ssl.keyStorePassword=whatever
    -Djavax.net.ssl.trustStoreType=jks
    -Djavax.net.ssl.trustStore=client-truststore.jks
    -Djavax.net.ssl.trustStorePassword=whatever

SSL Client Authentication with certificate using c#

Some time ago I've created this POC for client authentication with certificate in .Net Core. It uses idunno.Authentication package that is now build-in in .Net Core. My POC probably is bit outdated now, but it can be a good starting point for you.

First create an extension method to add certificate to HttpClientHandler:

public static class HttpClientHandlerExtensions
{
public static HttpClientHandler AddClientCertificate(this HttpClientHandler handler, X509Certificate2 certificate)
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(certificate);

return handler;
}
}

Then another extension method to add certificate to IHttpClientBuilder

    public static IHttpClientBuilder AddClientCertificate(this IHttpClientBuilder httpClientBuilder, X509Certificate2 certificate)
{
httpClientBuilder.ConfigureHttpMessageHandlerBuilder(builder =>
{
if (builder.PrimaryHandler is HttpClientHandler handler)
{
handler.AddClientCertificate(certificate);
}
else
{
throw new InvalidOperationException($"Only {typeof(HttpClientHandler).FullName} handler type is supported. Actual type: {builder.PrimaryHandler.GetType().FullName}");
}
});

return httpClientBuilder;
}

Then load the certificate and register HttpClient in HttpClientFactory

        var cert = CertificateFinder.FindBySubject("your-subject");
services
.AddHttpClient("ClientWithCertificate", client => { client.BaseAddress = new Uri(ServerUrl); })
.AddClientCertificate(cert);

Now when you will use client created by factory it will automatically send your certificate with the request;

public async Task SendRequest()
{
var client = _httpClientFactory.CreateClient("ClientWithCertificate");
....
}

How can I use different certificates on specific connections?

Create an SSLSocket factory yourself, and set it on the HttpsURLConnection before connecting.

...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...

You'll want to create one SSLSocketFactory and keep it around. Here's a sketch of how to initialize it:

/* Load the keyStore that includes self-signed cert as a "trusted" entry. */
KeyStore keyStore = ...
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();

If you need help creating the key store, please comment.


Here's an example of loading the key store:

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, trustStorePassword);
trustStore.close();

To create the key store with a PEM format certificate, you can write your own code using CertificateFactory, or just import it with keytool from the JDK (keytool won't work for a "key entry", but is just fine for a "trusted entry").

keytool -import -file selfsigned.pem -alias server -keystore server.jks


Related Topics



Leave a reply



Submit