Ken Allen
Ken Allen

Reputation: 51

Java HTTPS with client verification using windows keystore

I'm trying to do some json/https rpc in Java with client certificate authentication. I'm trying to use the windows keystore for the client cert and it's not working - the server returns a decrypt_error ssl alert. I can hit the same server with the same client cert in IE so I know it's not a problem with the cert itself. I can also do it from java if I pull the key from a different source. The server is running Apache 2.2.8 with openssl 0.9.8g. I tried different ciphers in the mod_ssl config but it failed the same with all of them. The protocol always negotiates to TLSv1. I tried several JRE 1.6 versions and they all have this problem. I've seen mention of this error on the internet in a few places, but no solutions.

In particular I saw these:

http://forums.oracle.com/forums/thread.jspa?threadID=1531706

http://www.java-forums.org/java-applets/24508-how-use-windows-keystore-establise-ssl-connection.html

I tried running with javax.net.debug=all but I actually get a different error when I do - which seems odd. Attached is a minimal test case, and the end of the javax.net.debug=ssl and javax.net.debug=all output with stacktraces. I can probably post the full logs if someone wants to see them.

This is really important to my current project so ANY help would be appreciated.

Here's a minimal test case:

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.security.KeyStore;
import java.util.Arrays;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

public class Minimal {
    public static void main(String[] args) throws Exception {
        SSLContext context = SSLContext.getInstance("SSL");
        KeyManagerFactory keyFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        KeyStore keyStore = KeyStore.getInstance("WINDOWS-MY");
        keyStore.load(null, null);
        keyFac.init(keyStore, null);
        TrustManagerFactory trustFac = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        KeyStore trustStore = KeyStore.getInstance("WINDOWS-ROOT");
        trustStore.load(null, null);
        trustFac.init(trustStore);
        context.init(keyFac.getKeyManagers(), trustFac.getTrustManagers(), null);

        HttpsURLConnection conn = (HttpsURLConnection)new URL("https://<redacted>").openConnection();
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        conn.setSSLSocketFactory(context.getSocketFactory());

        int responseCode = conn.getResponseCode();
        System.out.println("RESPONSE: " + responseCode);
        InputStream response = null;
        if(responseCode != 200) {
            response = conn.getErrorStream();
        } else {
            response = conn.getInputStream();
        }
        Reader r = new InputStreamReader(new BufferedInputStream(response));
        char[] buffer = new char[1024];
        int read = 0;
        while((read = r.read(buffer)) != -1) {
            System.out.print(Arrays.copyOf(buffer, read));
        }
        System.out.println("DONE");
    }
}

And the debug=ssl output with the Oracle 1.6u26 32bit JRE on Windows XP SP3:

***
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
main, WRITE: TLSv1 Handshake, length = 4640
SESSION KEYGEN:
PreMaster Secret:
0000: 03 01 1F B7 3D E7 72 6D   23 39 7C B0 0F F0 26 8F  ....=.rm#9....&.
0010: D6 24 FA D2 1C DE 43 94   4C 9C AA EA F1 4A 69 F1  .$....C.L....Ji.
0020: 62 20 5B CA 94 B8 CC 84   13 D5 1B 04 E5 51 A8 B7  b [..........Q..
CONNECTION KEYGEN:
Client Nonce:
0000: 4E 40 59 44 1A 4E 92 52   C3 BB 26 1F 08 A3 14 3F  [email protected]..&....?
0010: EB B9 CA 17 A1 DD B8 1D   89 C3 43 A8 E2 C6 D1 D0  ..........C.....
Server Nonce:
0000: 4E 40 59 44 9C D0 05 53   96 C1 50 3E 24 AA 38 DB  [email protected]>$.8.
0010: AE E7 55 F0 40 14 A4 85   4B BE 46 A5 7C 08 CB 2F  [email protected]..../
Master Secret:
0000: 7F 32 A2 C4 35 8D CA C0   F7 05 B5 0B B0 38 F8 C6  .2..5........8..
0010: 0C DC 7E C1 79 FD 97 08   0A D7 B1 40 6E 73 CB 28  ....y......@ns.(
0020: 84 78 D2 87 A8 88 C8 C7   A0 8C A3 AB 29 6B 6D FC  .x..........)km.
Client MAC write Secret:
0000: 96 90 FF F8 86 E0 AC E6   89 00 57 5A C6 23 94 EE  ..........WZ.#..
0010: AD 20 AB 5A                                        . .Z
Server MAC write Secret:
0000: AD C0 78 DC C8 96 BD E4   27 AD 7C 6D C8 AA C4 96  ..x.....'..m....
0010: E3 03 46 25                                        ..F%
Client write key:
0000: 40 25 7F BD 82 B7 85 6F   74 B2 A4 D1 16 4A FB 9F  @%.....ot....J..
Server write key:
0000: 9F E5 5D 45 73 66 E0 11   9B 14 25 F5 80 A9 EB 2D  ..]Esf....%....-
Client write IV:
0000: 77 1E BE 62 7A EB 56 D9   C4 62 D9 B5 2D 1E 22 97  w..bz.V..b..-.".
Server write IV:
0000: 7B 9F 0B AE 2E DF AF 7B   15 09 08 8C DE 13 0F 82  ................
*** CertificateVerify
main, WRITE: TLSv1 Handshake, length = 288
main, WRITE: TLSv1 Change Cipher Spec, length = 32
*** Finished
verify_data:  { 12, 12, 219, 182, 15, 237, 101, 233, 209, 171, 52, 158 }
***
main, WRITE: TLSv1 Handshake, length = 48
main, READ: TLSv1 Alert, length = 32
main, RECV TLSv1 ALERT:  fatal, decrypt_error
%% Invalidated:  [Session-3, TLS_RSA_WITH_AES_128_CBC_SHA]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: decrypt_error
main, called close()
main, called closeInternal(true)
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: decrypt_error
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:755)
        at com.sun.net.ssl.internal.ssl.AppInputStream.read(AppInputStream.java:75)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
        at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:317)
        at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:687)
        at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:632)
        at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:652)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1195)
        at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:379)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:318)
        at Minimal.main(Minimal.java:33)

And the debug=all output with the Oracle 1.6u26 32bit JRE on Windows XP SP3:

*** CertificateVerify
[write] MD5 and SHA1 hashes:  len = 262
0000: 0F 00 01 02 01 00 54 BD   E6 C5 44 96 71 2B EF FC  ......T...D.q+..
0010: 6E 0B D6 26 79 32 F1 23   AC 35 9C CE FD C6 A5 44  n..&y2.#.5.....D
0020: F7 F1 C9 4C 48 0F AA EC   26 62 8F F8 50 3B FE 55  ...LH...&b..P;.U
0030: 99 07 6D EC F9 42 60 B8   DF 8C 54 94 F6 2C B7 A8  ..m..B`...T..,..
0040: 16 C5 75 18 99 7E 3D 89   29 A2 46 5C 3E 49 33 F5  ..u...=.).F\>I3.
0050: C6 B0 82 B1 1D 74 42 2A   D5 8F E7 6C 13 75 F9 93  .....tB*...l.u..
0060: CD 21 10 D1 52 39 DD 00   95 C5 28 E6 84 66 75 DB  .!..R9....(..fu.
0070: D3 53 A1 F6 CF D1 0B EC   6C 2E F2 32 FB 2E 87 49  .S......l..2...I
0080: 8A 11 E0 EA 2F E4 A3 AF   49 09 86 0B DF 6D 8A BB  ..../...I....m..
0090: 0C 51 1B 9A 16 6D DA EF   F5 C0 25 09 4F 17 35 84  .Q...m....%.O.5.
00A0: DC 15 FE 2A 17 F0 AD 9F   F5 4C 26 AA DE 54 97 97  ...*.....L&..T..
00B0: EB 6F 07 ED 86 0A 62 B2   33 ED 2E DB 98 C0 A9 D3  .o....b.3.......
00C0: 6A B2 1D EE E8 D4 F9 73   F1 EE 76 0D 2E 2A F0 D0  j......s..v..*..
00D0: 32 35 4A F8 4F E6 E3 C5   D3 29 3F AF 27 5E 3E 09  25J.O....)?.'^>.
00E0: 1C 4A E5 4B 0C E2 92 77   91 F1 31 73 18 10 0F 8A  .J.K...w..1s....
00F0: 53 87 54 73 A0 64 92 4E   21 40 25 9E EB D7 9C 68  S.Ts.d.N!@%....h
0100: 75 59 3C 12 A6 AE                                  uY<...
Padded plaintext before ENCRYPTION:  len = 288
0000: 0F 00 01 02 01 00 54 BD   E6 C5 44 96 71 2B EF FC  ......T...D.q+..
0010: 6E 0B D6 26 79 32 F1 23   AC 35 9C CE FD C6 A5 44  n..&y2.#.5.....D
0020: F7 F1 C9 4C 48 0F AA EC   26 62 8F F8 50 3B FE 55  ...LH...&b..P;.U
0030: 99 07 6D EC F9 42 60 B8   DF 8C 54 94 F6 2C B7 A8  ..m..B`...T..,..
0040: 16 C5 75 18 99 7E 3D 89   29 A2 46 5C 3E 49 33 F5  ..u...=.).F\>I3.
0050: C6 B0 82 B1 1D 74 42 2A   D5 8F E7 6C 13 75 F9 93  .....tB*...l.u..
0060: CD 21 10 D1 52 39 DD 00   95 C5 28 E6 84 66 75 DB  .!..R9....(..fu.
0070: D3 53 A1 F6 CF D1 0B EC   6C 2E F2 32 FB 2E 87 49  .S......l..2...I
0080: 8A 11 E0 EA 2F E4 A3 AF   49 09 86 0B DF 6D 8A BB  ..../...I....m..
0090: 0C 51 1B 9A 16 6D DA EF   F5 C0 25 09 4F 17 35 84  .Q...m....%.O.5.
00A0: DC 15 FE 2A 17 F0 AD 9F   F5 4C 26 AA DE 54 97 97  ...*.....L&..T..
00B0: EB 6F 07 ED 86 0A 62 B2   33 ED 2E DB 98 C0 A9 D3  .o....b.3.......
00C0: 6A B2 1D EE E8 D4 F9 73   F1 EE 76 0D 2E 2A F0 D0  j......s..v..*..
00D0: 32 35 4A F8 4F E6 E3 C5   D3 29 3F AF 27 5E 3E 09  25J.O....)?.'^>.
00E0: 1C 4A E5 4B 0C E2 92 77   91 F1 31 73 18 10 0F 8A  .J.K...w..1s....
00F0: 53 87 54 73 A0 64 92 4E   21 40 25 9E EB D7 9C 68  S.Ts.d.N!@%....h
0100: 75 59 3C 12 A6 AE 04 99   63 17 2C 0F 57 FC DD 48  uY<.....c.,.W..H
0110: 06 79 E9 2F 98 C1 B0 89   E1 16 05 05 05 05 05 05  .y./............
main, WRITE: TLSv1 Handshake, length = 288
[Raw write]: length = 293
0000: 16 03 01 01 20 E4 75 8F   10 01 30 6F DB C9 9A 45  .... .u...0o...E
0010: FE 5A 30 38 1D BC 44 DD   9F 4E CF 1A AB 16 75 0B  .Z08..D..N....u.
0020: 94 7C 59 EB 5B 51 D0 72   7F FC 6D 64 BC F1 A4 3C  ..Y.[Q.r..md...<
0030: A1 AD 77 55 82 D9 42 FE   2B 01 E7 E4 C1 03 8D 53  ..wU..B.+......S
0040: 4E B6 4E 3E 9F AA B4 CB   14 12 36 11 FF 14 DD BD  N.N>......6.....
0050: 8F D0 BF 3D 11 58 CE 17   6A 80 6F F8 A8 0C 17 0D  ...=.X..j.o.....
0060: 6C 41 02 AB 96 03 91 60   C9 54 76 44 E2 17 A1 D5  lA.....`.TvD....
0070: 07 7A 26 16 3E 94 88 0C   BC E9 BE E9 91 A7 60 DC  .z&.>.........`.
0080: D5 1C DD 85 DB F8 7A 52   5E 09 F2 38 B9 29 7D 08  ......zR^..8.)..
0090: 14 00 C0 D2 5E 72 4F 85   5A C0 E0 C1 33 58 C0 CD  ....^rO.Z...3X..
00A0: 13 B6 1A AC 9B 86 2A 00   81 55 94 0B 19 81 89 45  ......*..U.....E
00B0: 42 A0 12 E9 4E 15 2C E7   92 A5 6F D5 F7 31 74 42  B...N.,...o..1tB
00C0: 8E 2B 50 2F 46 A6 46 DF   E4 F4 F1 32 FD 40 0D C9  .+P/F.F....2.@..
00D0: 3A 0B 26 F0 2B 0A 58 FD   A3 DC E7 30 3A 98 EB A8  :.&.+.X....0:...
00E0: BB 7C A2 FA DF 7E 9C 61   96 6F F9 A7 02 19 43 91  .......a.o....C.
00F0: 0B 1C C4 4E 73 8F A5 CA   C5 CF D3 71 86 26 A1 EE  ...Ns......q.&..
0100: 2A B1 DE 1B BE 7A E8 1B   04 91 62 DD 9A C9 F2 72  *....z....b....r
0110: D6 A4 AC 13 83 CE 60 28   E7 D4 97 54 1E 31 E2 E3  ......`(...T.1..
0120: 75 3B 5E 57 81                                     u;^W.
Padded plaintext before ENCRYPTION:  len = 32
0000: 01 20 0B 27 DC 48 23 03   1A D7 9A F6 2A BB 8F B8  . .'.H#.....*...
0010: 4D 6E 8A F0 ED 0A 0A 0A   0A 0A 0A 0A 0A 0A 0A 0A  Mn..............
main, WRITE: TLSv1 Change Cipher Spec, length = 32
[Raw write]: length = 37
0000: 14 03 01 00 20 6C 2C F0   99 F6 91 70 68 9B 4C 51  .... l,....ph.LQ
0010: CC 9E 82 87 22 7C 84 FB   FB A6 7F 12 F7 E1 3C 19  ....".........<.
0020: 4E 5E F6 39 A5                                     N^.9.
*** Finished
verify_data:  { 187, 117, 145, 153, 138, 130, 177, 134, 30, 54, 197, 207 }
***
[write] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C BB 75 91 99   8A 82 B1 86 1E 36 C5 CF  .....u.......6..
Padded plaintext before ENCRYPTION:  len = 48
0000: 14 00 00 0C BB 75 91 99   8A 82 B1 86 1E 36 C5 CF  .....u.......6..
0010: 39 22 A0 F8 21 7B 7B 06   B2 AD 63 73 B7 47 74 E2  9"..!.....cs.Gt.
0020: 10 18 AC 34 0B 0B 0B 0B   0B 0B 0B 0B 0B 0B 0B 0B  ...4............
main, WRITE: TLSv1 Handshake, length = 48
main, waiting for close_notify or alert: state 3
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
%% Invalidated:  [Session-3, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT:  fatal, description = unexpected_message
Padded plaintext before ENCRYPTION:  len = 32
0000: 02 0A 09 D5 63 69 0E 9D   6F AF AC 53 23 31 63 94  ....ci..o..S#1c.
0010: 6D 94 94 D4 E4 AB 09 09   09 09 09 09 09 09 09 09  m...............
main, WRITE: TLSv1 Alert, length = 32
main, Exception sending alert: java.net.SocketException: Software caused connection abort: socket write error
main, called closeSocket()
main, called close()
main, called closeInternal(true)
Exception in thread "main" java.net.SocketException: Software caused connection
abort: recv failed
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.read(SocketInputStream.java:129)
        at com.sun.net.ssl.internal.ssl.InputRecord.readFully(InputRecord.java:293)
        at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:331)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:798)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.waitForClose(SSLSocketImpl.java:1493)
        at com.sun.net.ssl.internal.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:103)
        at com.sun.net.ssl.internal.ssl.Handshaker.sendChangeCipherSpec(Handshaker.java:689)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.sendChangeCipherAndFinish(ClientHandshaker.java:985)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverHelloDone(ClientHandshaker.java:904)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:238)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
        at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:893)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:755)
        at com.sun.net.ssl.internal.ssl.AppInputStream.read(AppInputStream.java:75)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
        at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:317)
        at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:687)
        at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:632)
        at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:652)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1195)
        at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:379)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:318)
        at com.scytale.cards.session.Minimal.main(Minimal.java:33)

Upvotes: 2

Views: 10600

Answers (2)

Ken Allen
Ken Allen

Reputation: 51

I rooted around in javaws and found a less terrible solution than starting another process. You need to add deploy.jar from your JRE to the classpath. Note that this uses private sun/oracle apis and is thus subject to change. This is an acceptable risk for my project, it might not be for yours, so keep that in mind.

First, add the following to the initialization of your program:

Class providerClass = Class.forName("com.sun.deploy.security.MSCryptoProvider", true, ClassLoader.getSystemClassLoader());
Provider provider = (Provider)providerClass.newInstance();
Security.insertProviderAt(provider, Security.getProviders().length + 1);
com.sun.deploy.config.Config.getInstance().loadDeployNativeLib();

The rest of the code is the same as in the question but use WIExplorerMy as the keystore name instead of WINDOWS-MY. That should be all you need to do.

Keep in mind also that this just picks the first applicable certificate so you will probably need to implement X509KeyManager, delegating all operations to the one from keyFac.getKeyManagers except for chooseClientAlias.

Upvotes: 3

Derik Wilson
Derik Wilson

Reputation: 1

I was having the exact same issue and I ended up using Java Web Start.

  1. Remove the SSL context logic from your app. You won't need it.

  2. Complete your app and export the executable jar.

  3. Create yourself a self-signed certificate (not for production use over the web of course) using the Java keytool utility:

    prompt> keytool -genkey -keystore mykeystore -alias somealias (you will have to fill out some info)

    prompt> keytool -selfcert -alias somealias -keystore mykeystore

  4. Sign your jar file:

    prompt> jarsigner -keystore mykeystore path_to_jar_file alias_to_sign_with

  5. Finally, set up your jnlp file that your user will launch (via double click or through the browser, or via command line in a shell-and-wait function (executing via javaws) from your host application).

Here is a sample jnlp file:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp codebase="file:///path_to_your_jnlp_file_directory" href="some_jnlpfile.jnlp">
    <information>
        <title>The Title of Your App</title>
        <vendor>The Vendor Name</vendor>
    </information>
    <resources>
        <jar href="the_codebase_relative_path_to_your_jar/somejar.jar"/>
    </resources>
    <application-desc main-class="mypackage.MyClass"/>
    <security>
        <all-permissions/>
    </security>
</jnlp>

Now you can launch your jnlp and allow Java Web Start to handle the SSL functionality, including prompting the user for a certificate and/or PIN.

Upvotes: 0

Related Questions