1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26// SunJSSE does not support dynamic system properties, no way to re-use
27// system properties in samevm/agentvm mode.
28
29/*
30 * @test
31 * @bug 8139565
32 * @summary Restrict certificates with DSA keys less than 1024 bits
33 *
34 * @run main/othervm DisabledShortDSAKeys PKIX TLSv1.2
35 * @run main/othervm DisabledShortDSAKeys SunX509 TLSv1.2
36 * @run main/othervm DisabledShortDSAKeys PKIX TLSv1.1
37 * @run main/othervm DisabledShortDSAKeys SunX509 TLSv1.1
38 * @run main/othervm DisabledShortDSAKeys PKIX TLSv1
39 * @run main/othervm DisabledShortDSAKeys SunX509 TLSv1
40 * @run main/othervm DisabledShortDSAKeys PKIX SSLv3
41 * @run main/othervm DisabledShortDSAKeys SunX509 SSLv3
42 */
43
44import java.net.*;
45import java.util.*;
46import java.io.*;
47import javax.net.ssl.*;
48import java.security.Security;
49import java.security.KeyStore;
50import java.security.KeyFactory;
51import java.security.cert.Certificate;
52import java.security.cert.CertificateFactory;
53import java.security.spec.*;
54import java.security.interfaces.*;
55import java.util.Base64;
56
57
58public class DisabledShortDSAKeys {
59
60    /*
61     * =============================================================
62     * Set the various variables needed for the tests, then
63     * specify what tests to run on each side.
64     */
65
66    /*
67     * Should we run the client or server in a separate thread?
68     * Both sides can throw exceptions, but do you have a preference
69     * as to which side should be the main thread.
70     */
71    static boolean separateServerThread = true;
72
73    /*
74     * Where do we find the keystores?
75     */
76    // Certificates and key used in the test.
77    static String trustedCertStr =
78        "-----BEGIN CERTIFICATE-----\n" +
79        "MIIDDjCCAs2gAwIBAgIJAO5/hbm1ByJOMAkGByqGSM44BAMwHzELMAkGA1UEBhMC\n" +
80        "VVMxEDAOBgNVBAoTB0V4YW1wbGUwHhcNMTYwMjE2MDQzNTQ2WhcNMzcwMTI2MDQz\n" +
81        "NTQ2WjAfMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXhhbXBsZTCCAbgwggEsBgcq\n" +
82        "hkjOOAQBMIIBHwKBgQC4aSK8nBYdWJtuBkz6yoDyjZnNuGFSpDmx1ggKpLpcnPuw\n" +
83        "YKAbUhqdYhZtaIqQ4aO0T1ZS/HuOM0zvddnMUidFNX3RUvDkvdD/JYOnjqzCm+xW\n" +
84        "U0NFuPHZdapQY5KFk3ugkqZpHLY1StZbu0qugZOZjbBOMwB7cHAbMDuVpEr8DQIV\n" +
85        "AOi+ig+h3okFbWEE9MztiI2+DqNrAoGBAKh2EZbuWU9NoHglhVzfDUoz8CeyW6W6\n" +
86        "rUZuIOQsjWaYOeRPWX0UVAGq9ykIOfamEpurKt4H8ge/pHaL9iazJjonMHOXG12A\n" +
87        "0lALsMDGv22zVaJzXjOBvdPzc87opr0LIVgHASKOcDYjsICKNYPlS2cL3MJoD+bj\n" +
88        "NAR67b90VBbEA4GFAAKBgQCGrkRp2tdj2mZF7Qz0tO6p3xSysbEfN6QZxOJYPTvM\n" +
89        "yIYfLV9Yoy7XaRd/mCpJo/dqmsZMzowtyi+u+enuVpOLKiq/lyCktL+xUzZAjLT+\n" +
90        "9dafHlS1wR3pDSa1spo9xTEi4Ff/DQDHcdGalBxSXX/UdRtSecIYAp5/fkt3QZ5v\n" +
91        "0aOBkTCBjjAdBgNVHQ4EFgQUX4qbP5PgBx1J8BJ8qEgfoKVLSnQwTwYDVR0jBEgw\n" +
92        "RoAUX4qbP5PgBx1J8BJ8qEgfoKVLSnShI6QhMB8xCzAJBgNVBAYTAlVTMRAwDgYD\n" +
93        "VQQKEwdFeGFtcGxlggkA7n+FubUHIk4wDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8E\n" +
94        "BAMCAgQwCQYHKoZIzjgEAwMwADAtAhUAkr5bINXyy/McAx6qwhb6r0/QJUgCFFUP\n" +
95        "CZokA4/NqJIgq8ThpTQAE8SB\n" +
96        "-----END CERTIFICATE-----";
97
98    static String targetCertStr =
99        "-----BEGIN CERTIFICATE-----\n" +
100        "MIICUjCCAhGgAwIBAgIJAIiDrs/4W8rtMAkGByqGSM44BAMwHzELMAkGA1UEBhMC\n" +
101        "VVMxEDAOBgNVBAoTB0V4YW1wbGUwHhcNMTYwMjE2MDQzNTQ2WhcNMzUxMTAzMDQz\n" +
102        "NTQ2WjA5MQswCQYDVQQGEwJVUzEQMA4GA1UECgwHRXhhbXBsZTEYMBYGA1UEAwwP\n" +
103        "d3d3LmV4YW1wbGUuY29tMIHwMIGoBgcqhkjOOAQBMIGcAkEAs6A0p3TysTtVXGSv\n" +
104        "ThR/8GHpbL49KyWRJBMIlmLc5jl/wxJgnL1t07p4YTOEa6ecyTFos04Z8n2GARmp\n" +
105        "zYlUywIVAJLDcf4JXhZbguRFSQdWwWhZkh+LAkBLCzh3Xvpmc/5CDqU+QHqDcuSk\n" +
106        "5B8+ZHaHRi2KQ00ejilpF2qZpW5JdHe4m3Pggh0MIuaAGX+leM4JKlnObj14A0MA\n" +
107        "AkAYb+DYlFgStFhF1ip7rFzY8K6i/3ellkXI2umI/XVwxUQTHSlk5nFOep5Dfzm9\n" +
108        "pADJwuSe1qGHsHB5LpMZPVpto4GEMIGBMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgPo\n" +
109        "MB0GA1UdDgQWBBT8nsFyccF4q1dtpWE1dkNK5UiXtTAfBgNVHSMEGDAWgBRfips/\n" +
110        "k+AHHUnwEnyoSB+gpUtKdDAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIG\n" +
111        "CCsGAQUFBwMDMAkGByqGSM44BAMDMAAwLQIUIcIlxpIwaZXdpMC+U076unR1Mp8C\n" +
112        "FQCD/NE8O0xwq57nwFfp7tUvUHYMMA==\n" +
113        "-----END CERTIFICATE-----";
114
115    // Private key in the format of PKCS#8, key size is 512 bits.
116    static String targetPrivateKey =
117        "MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEAs6A0p3TysTtVXGSvThR/8GHpbL49KyWR\n" +
118        "JBMIlmLc5jl/wxJgnL1t07p4YTOEa6ecyTFos04Z8n2GARmpzYlUywIVAJLDcf4J\n" +
119        "XhZbguRFSQdWwWhZkh+LAkBLCzh3Xvpmc/5CDqU+QHqDcuSk5B8+ZHaHRi2KQ00e\n" +
120        "jilpF2qZpW5JdHe4m3Pggh0MIuaAGX+leM4JKlnObj14BBYCFHB2Wek2g5hpNj5y\n" +
121        "RQfCc6CFO0dv";
122
123    static char passphrase[] = "passphrase".toCharArray();
124
125    /*
126     * Is the server ready to serve?
127     */
128    volatile static boolean serverReady = false;
129
130    /*
131     * Turn on SSL debugging?
132     */
133    static boolean debug = false;
134
135    /*
136     * Define the server side of the test.
137     *
138     * If the server prematurely exits, serverReady will be set to true
139     * to avoid infinite hangs.
140     */
141    void doServerSide() throws Exception {
142        SSLContext context = generateSSLContext(null, targetCertStr,
143                                            targetPrivateKey);
144        SSLServerSocketFactory sslssf = context.getServerSocketFactory();
145        SSLServerSocket sslServerSocket =
146            (SSLServerSocket)sslssf.createServerSocket(serverPort);
147        serverPort = sslServerSocket.getLocalPort();
148
149        /*
150         * Signal Client, we're ready for his connect.
151         */
152        serverReady = true;
153
154        try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
155            try (InputStream sslIS = sslSocket.getInputStream()) {
156                sslIS.read();
157            }
158
159            throw new Exception(
160                    "DSA keys shorter than 1024 bits should be disabled");
161        } catch (SSLHandshakeException sslhe) {
162            // the expected exception, ignore
163        }
164    }
165
166    /*
167     * Define the client side of the test.
168     *
169     * If the server prematurely exits, serverReady will be set to true
170     * to avoid infinite hangs.
171     */
172    void doClientSide() throws Exception {
173
174        /*
175         * Wait for server to get started.
176         */
177        while (!serverReady) {
178            Thread.sleep(50);
179        }
180
181        SSLContext context = generateSSLContext(trustedCertStr, null, null);
182        SSLSocketFactory sslsf = context.getSocketFactory();
183
184        try (SSLSocket sslSocket =
185            (SSLSocket)sslsf.createSocket("localhost", serverPort)) {
186
187            // only enable the target protocol
188            sslSocket.setEnabledProtocols(new String[] {enabledProtocol});
189
190            // enable a block cipher
191            sslSocket.setEnabledCipherSuites(
192                new String[] {"TLS_DHE_DSS_WITH_AES_128_CBC_SHA"});
193
194            try (OutputStream sslOS = sslSocket.getOutputStream()) {
195                sslOS.write('B');
196                sslOS.flush();
197            }
198
199            throw new Exception(
200                    "DSA keys shorter than 1024 bits should be disabled");
201        } catch (SSLHandshakeException sslhe) {
202            // the expected exception, ignore
203        }
204    }
205
206    /*
207     * =============================================================
208     * The remainder is just support stuff
209     */
210    private static String tmAlgorithm;        // trust manager
211    private static String enabledProtocol;    // the target protocol
212
213    private static void parseArguments(String[] args) {
214        tmAlgorithm = args[0];
215        enabledProtocol = args[1];
216    }
217
218    private static SSLContext generateSSLContext(String trustedCertStr,
219            String keyCertStr, String keySpecStr) throws Exception {
220
221        // generate certificate from cert string
222        CertificateFactory cf = CertificateFactory.getInstance("X.509");
223
224        // create a key store
225        KeyStore ks = KeyStore.getInstance("JKS");
226        ks.load(null, null);
227
228        // import the trused cert
229        Certificate trusedCert = null;
230        ByteArrayInputStream is = null;
231        if (trustedCertStr != null) {
232            is = new ByteArrayInputStream(trustedCertStr.getBytes());
233            trusedCert = cf.generateCertificate(is);
234            is.close();
235
236            ks.setCertificateEntry("DSA Export Signer", trusedCert);
237        }
238
239        if (keyCertStr != null) {
240            // generate the private key.
241            PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
242                                Base64.getMimeDecoder().decode(keySpecStr));
243            KeyFactory kf = KeyFactory.getInstance("DSA");
244            DSAPrivateKey priKey =
245                    (DSAPrivateKey)kf.generatePrivate(priKeySpec);
246
247            // generate certificate chain
248            is = new ByteArrayInputStream(keyCertStr.getBytes());
249            Certificate keyCert = cf.generateCertificate(is);
250            is.close();
251
252            Certificate[] chain = null;
253            if (trusedCert != null) {
254                chain = new Certificate[2];
255                chain[0] = keyCert;
256                chain[1] = trusedCert;
257            } else {
258                chain = new Certificate[1];
259                chain[0] = keyCert;
260            }
261
262            // import the key entry.
263            ks.setKeyEntry("Whatever", priKey, passphrase, chain);
264        }
265
266        // create SSL context
267        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
268        tmf.init(ks);
269
270        SSLContext ctx = SSLContext.getInstance("TLS");
271        if (keyCertStr != null && !keyCertStr.isEmpty()) {
272            KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
273            kmf.init(ks, passphrase);
274
275            ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
276            ks = null;
277        } else {
278            ctx.init(null, tmf.getTrustManagers(), null);
279        }
280
281        return ctx;
282    }
283
284
285    // use any free port by default
286    volatile int serverPort = 0;
287
288    volatile Exception serverException = null;
289    volatile Exception clientException = null;
290
291    public static void main(String[] args) throws Exception {
292        if (debug)
293            System.setProperty("javax.net.debug", "all");
294
295        /*
296         * Get the customized arguments.
297         */
298        parseArguments(args);
299
300        /*
301         * Start the tests.
302         */
303        new DisabledShortDSAKeys();
304    }
305
306    Thread clientThread = null;
307    Thread serverThread = null;
308
309    /*
310     * Primary constructor, used to drive remainder of the test.
311     *
312     * Fork off the other side, then do your work.
313     */
314    DisabledShortDSAKeys() throws Exception {
315        Exception startException = null;
316        try {
317            if (separateServerThread) {
318                startServer(true);
319                startClient(false);
320            } else {
321                startClient(true);
322                startServer(false);
323            }
324        } catch (Exception e) {
325            startException = e;
326        }
327
328        /*
329         * Wait for other side to close down.
330         */
331        if (separateServerThread) {
332            if (serverThread != null) {
333                serverThread.join();
334            }
335        } else {
336            if (clientThread != null) {
337                clientThread.join();
338            }
339        }
340
341        /*
342         * When we get here, the test is pretty much over.
343         * Which side threw the error?
344         */
345        Exception local;
346        Exception remote;
347
348        if (separateServerThread) {
349            remote = serverException;
350            local = clientException;
351        } else {
352            remote = clientException;
353            local = serverException;
354        }
355
356        Exception exception = null;
357
358        /*
359         * Check various exception conditions.
360         */
361        if ((local != null) && (remote != null)) {
362            // If both failed, return the curthread's exception.
363            local.initCause(remote);
364            exception = local;
365        } else if (local != null) {
366            exception = local;
367        } else if (remote != null) {
368            exception = remote;
369        } else if (startException != null) {
370            exception = startException;
371        }
372
373        /*
374         * If there was an exception *AND* a startException,
375         * output it.
376         */
377        if (exception != null) {
378            if (exception != startException && startException != null) {
379                exception.addSuppressed(startException);
380            }
381            throw exception;
382        }
383
384        // Fall-through: no exception to throw!
385    }
386
387    void startServer(boolean newThread) throws Exception {
388        if (newThread) {
389            serverThread = new Thread() {
390                public void run() {
391                    try {
392                        doServerSide();
393                    } catch (Exception e) {
394                        /*
395                         * Our server thread just died.
396                         *
397                         * Release the client, if not active already...
398                         */
399                        System.err.println("Server died...");
400                        serverReady = true;
401                        serverException = e;
402                    }
403                }
404            };
405            serverThread.start();
406        } else {
407            try {
408                doServerSide();
409            } catch (Exception e) {
410                serverException = e;
411            } finally {
412                serverReady = true;
413            }
414        }
415    }
416
417    void startClient(boolean newThread) throws Exception {
418        if (newThread) {
419            clientThread = new Thread() {
420                public void run() {
421                    try {
422                        doClientSide();
423                    } catch (Exception e) {
424                        /*
425                         * Our client thread just died.
426                         */
427                        System.err.println("Client died...");
428                        clientException = e;
429                    }
430                }
431            };
432            clientThread.start();
433        } else {
434            try {
435                doClientSide();
436            } catch (Exception e) {
437                clientException = e;
438            }
439        }
440    }
441}
442