I have to be able to specify the certificate and key to be used to authenticate a HTTPS connection in a configuration file (not a system keystore, but user configuration) in a backend system, and have tried the following approach:
- First to encode the user certificate and private key as a PKCS#12 file encoded in Base64 in config. (I have exported the info from
openssl pkcs12
command and converted to base64 format, to be able to read it from a hardwiredString
instance) - Read that information from an
InputStream
from aBase64.Decode
and input toKeyStore#load()
method. (Info is correctly read andKeyStore
instance stores actually the user info, this has been checked) - Specify that
KeyStore
as the source of key info, in aSSLContext.Builder
(fromorg.apache.http.conn.ssl
library) (This is what I think is not correctly done) - Issue a
HTTPS
call to access a client authentication server that authenticates client based on client presented certificate.
The code used to test the connection is shown below:
/**
* $Id: $
*
* Copyright (C) 2017 BaseN.
*
* All rights reserved.
*/
package net.basen.testing;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Base64;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
/**
* Tester for https connections.
*/
public class CheckSSLAccess {
/**
* Main program
* @param args
* @throws KeyStoreException
* @throws IOException
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws UnrecoverableKeyException
*/
public static void main( String[] args ) {
String ksContents = /* Keystore contents altered intentionally, but correctly reflect a private key and certificate */
"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";
try {
InputStream is = Base64.getMimeDecoder().wrap( new ByteArrayInputStream( ksContents.getBytes() ) );
KeyStore kst = KeyStore.getInstance( "pkcs12" );
kst.load( is, null );
KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() )
.init( kst, null );
SSLContext.setDefault( SSLContexts.custom()
.loadTrustMaterial(kst)
.loadKeyMaterial( kst, null )
.build() );
} catch( Exception e ) {
e.printStackTrace();
return;
}
try {
CloseableHttpClient cl = HttpClients.createDefault();
/* server address has been also altered to protect access privacy */
HttpGet get = new HttpGet( "https://www.server.com/autheticated_url" );
CloseableHttpResponse resp = cl.execute( get );
System.out.println(resp.getStatusLine());
Header[] headers = resp.getAllHeaders();
for (int i = 0; i < headers.length; i++)
System.out.println(headers[i]);
System.out.println();
InputStream in = resp.getEntity().getContent();
byte[] buffer = new byte[1024];
int n;
while ((n = in.read(buffer)) > 0) {
System.out.write( buffer, 0, n );
}
if (n < 0) {
System.err.println( "n == " + n );
}
} catch( Exception e ) {
e.printStackTrace();
return;
}
}
} /* CheckSSLAccess */
The library issues the following exception, when negotiating SSL
credentials with the server, the same as if no user certificate is being used. I don't know exactly what is missing in this code for the client to select the proper certificate and negotiate correctly with the server.
Sorry, but due to project issues, the certificates shown in the example have been changed to newly generated ones, and also the URL of the server to access to.
javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
at sun.security.ssl.ClientHandshaker.handshakeAlert(ClientHandshaker.java:1438)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2016)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at net.basen.testing.CheckSSLAccess.main(CheckSSLAccess.java:109)
The question is
What should be missing in the SSLContext
initialization for it to select (or to link the SSLContext
to the actual HTTPS call) the KeyStore
credentials in the HTTPS call?
EDIT
After activating the option in question and debug mode, the user certificate is never considered in the ssl handshake, so I'm afraid this question has nothing to do with the referred one. For some reason, the hanshake stops on the server certificate exchange and client certificate is never exchanged:
trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
[...]
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
[...]
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1.1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 for TLSv1.1
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1471524485 bytes = { 109, 133, 165, 223, 114, 170, 197, 226, 115, 115, 12, 46, 74, 145, 95, 17, 242, 119,
57, 167, 76, 6, 147, 14, 32, 63, 62, 177 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256,
[...]
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1,
sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1,
secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA,
SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
***
[write] MD5 and SHA1 hashes: len = 195
0000: 01 00 00 BF 03 03 58 B6 AF 85 6D 85 A5 DF 72 AA ......X...m...r.
[...]
00B0: 03 05 01 04 03 04 01 03 03 03 01 02 03 02 01 02 ................
00C0: 02 01 01 ...
main, WRITE: TLSv1.2 Handshake, length = 195
[Raw write]: length = 200
0000: 16 03 03 00 C3 01 00 00 BF 03 03 58 B6 AF 85 6D ...........X...m
[...]
00B0: 06 03 06 01 05 03 05 01 04 03 04 01 03 03 03 01 ................
00C0: 02 03 02 01 02 02 01 01 ........
[Raw read]: length = 5
0000: 16 03 01 00 31 ....1
[Raw read]: length = 49
0000: 02 00 00 2D 03 01 58 B6 AF 85 5B 50 5E 9A C0 D5 ...-..X...[P^...
0010: 74 38 A3 6F F8 14 C1 F1 0E 35 E0 D8 66 4A 13 23 t8.o.....5..fJ.#
0020: 1E BC 39 EE 70 8D 00 00 2F 00 00 05 FF 01 00 01 ..9.p.../.......
0030: 00 .
main, READ: TLSv1 Handshake, length = 49
*** ServerHello, TLSv1
RandomCookie: GMT: 1471524485 bytes = { 91, 80, 94, 154, 192, 213, 116, 56, 163, 111, 248, 20, 193, 241, 14, 53, 224, 216, 102,
74, 19, 35, 30, 188, 57, 238, 112, 141 }
Session ID: {}
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
** TLS_RSA_WITH_AES_128_CBC_SHA
[read] MD5 and SHA1 hashes: len = 49
0000: 02 00 00 2D 03 01 58 B6 AF 85 5B 50 5E 9A C0 D5 ...-..X...[P^...
0010: 74 38 A3 6F F8 14 C1 F1 0E 35 E0 D8 66 4A 13 23 t8.o.....5..fJ.#
0020: 1E BC 39 EE 70 8D 00 00 2F 00 00 05 FF 01 00 01 ..9.p.../.......
0030: 00 .
[Raw read]: length = 5
0000: 16 03 01 08 5F ...._
[Raw read]: length = 2143
0000: 0B 00 08 5B 00 08 58 00 04 AC 30 82 04 A8 30 82 ...[..X...0...0.
0010: 03 90 A0 03 02 01 02 02 01 13 30 0D 06 09 2A 86 ..........0...*.
0020: 48 86 F7 0D 01 01 05 05 00 30 81 92 31 0B 30 09 H........0..1.0.
[...]
0810: 3F 7F 8E 25 F0 6C 0E 10 75 D9 D6 C6 51 77 AE 1D ?..%.l..u...Qw..
0820: 96 0A E9 72 1B 65 2F C5 E4 96 20 67 0E 75 76 E0 ...r.e/... g.uv.
0830: D5 42 27 4B 81 22 15 C4 F9 7E A2 78 24 91 A4 A2 .B'K.".....x$...
0840: A7 96 1A B4 D0 D7 88 94 B5 36 B7 B3 19 78 37 E1 .........6...x7.
0850: C2 2E 28 61 D4 E0 4B 42 20 27 6C 99 40 A2 8E ..(a..KB 'l.@..
main, READ: TLSv1 Handshake, length = 2143
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: EMAILADDRESS=admin@actility.com, CN=rms.poc1.actility.com, OU=Actility, O=Actility, ST=FRANCE, C=FR
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 2048 bits
modulus:
2292398721841540[...]592209023
public exponent: 65537
Validity: [From: Thu Apr 03 15:25:07 CEST 2014,
To: Wed Mar 29 15:25:07 CEST 2034]
Issuer: EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR
SerialNumber: [ 13]
Certificate Extensions: 4
[1]: ObjectId: 2.16.840.1.113730.1.13 Criticality=false
Extension unknown: DER encoded OCTET string =
0000: 04 1F 16 1D 4F 70 65 6E 53 53 4C 20 47 65 6E 65 ....OpenSSL Gene
0010: 72 61 74 65 64 20 43 65 72 74 69 66 69 63 61 74 rated Certificat
0020: 65 e
[2]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
[EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR]
SerialNumber: [ 9d5d3f4c 2ee0bcd9]
]
[3]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]
[4]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 0B 82 48 98 6B E2 66 02 2E 30 7E 3C 0E 55 18 9E ..H.k.f..0.<.U..
0010: DF 78 BC E9 .x..
]
]
]
Algorithm: [SHA1withRSA]
Signature:
0000: C7 E2 0C 87 AE AF 97 E8 DD 9C 9F B5 1E C0 48 AA ..............H.
[...]
00E0: 10 AB 42 B8 C2 F9 42 C8 BC 60 99 12 FE A1 CE 9F ..B...B..`......
00F0: 2C 9A 66 DF AE 03 88 61 9A 24 0B 1A D2 89 22 EA ,.f....a.$....".
]
chain [1] = [
[
Version: V1
Subject: EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 2048 bits
modulus:
2828950521283455293132606[...]047773313891689356150673882060816455169139045423472889
public exponent: 65537
Validity: [From: Thu Apr 03 15:23:19 CEST 2014,
To: Wed Mar 29 15:23:19 CEST 2034]
Issuer: EMAILADDRESS=admin@actility.com, CN=m2m.actility.com, OU=Actility, O=Actility, L=Paris, ST=FRANCE, C=FR
SerialNumber: [ 9d5d3f4c 2ee0bcd9]
]
Algorithm: [SHA1withRSA]
Signature:
0000: A7 FB 5B 7F 90 9D 31 85 CC CA 1D 19 C5 B6 34 2A ..[...1.......4*
[...]
00F0: E1 C2 2E 28 61 D4 E0 4B 42 20 27 6C 99 40 A2 8E ...(a..KB 'l.@..
]
***
%% Invalidated: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT: fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 2E .......
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building
failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1506)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
[....]
java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 26 more