1 /*
2  * Copyright (c) 2020, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * SunJSSE does not support dynamic system properties, no way to re-use
26  * system properties in samevm/agentvm mode.
27  * For extra debugging output, add -Djavax.net.debug=ssl:handshake into the
28  * run directive below.
29  */
30 
31 /*
32  * @test
33  * @bug 8166596
34  * @summary TLS support for the EdDSA signature algorithm
35  * @library /javax/net/ssl/templates /test/lib
36  * @run main/othervm TLSWithEdDSA
37  */
38 
39 import java.io.ByteArrayInputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.OutputStream;
43 import java.net.Socket;
44 import java.net.SocketException;
45 import java.nio.charset.Charset;
46 import java.security.GeneralSecurityException;
47 import java.security.KeyFactory;
48 import java.security.KeyStore;
49 import java.security.KeyStoreException;
50 import java.security.Principal;
51 import java.security.PrivateKey;
52 import java.security.PublicKey;
53 import java.security.cert.Certificate;
54 import java.security.cert.CertificateException;
55 import java.security.cert.CertificateFactory;
56 import java.security.cert.PKIXBuilderParameters;
57 import java.security.cert.X509CertSelector;
58 import java.security.cert.X509Certificate;
59 import java.security.interfaces.ECKey;
60 import java.security.interfaces.EdECKey;
61 import java.security.spec.PKCS8EncodedKeySpec;
62 import java.util.*;
63 import javax.net.ssl.CertPathTrustManagerParameters;
64 import javax.net.ssl.KeyManager;
65 import javax.net.ssl.KeyManagerFactory;
66 import javax.net.ssl.SSLContext;
67 import javax.net.ssl.SSLPeerUnverifiedException;
68 import javax.net.ssl.SSLServerSocket;
69 import javax.net.ssl.SSLSession;
70 import javax.net.ssl.SSLSocket;
71 import javax.net.ssl.TrustManagerFactory;
72 import javax.net.ssl.X509ExtendedKeyManager;
73 import javax.net.ssl.X509KeyManager;
74 import jdk.test.lib.security.SecurityUtils;
75 
76 public class TLSWithEdDSA extends SSLSocketTemplate {
77     private static final String PASSWD = "passphrase";
78     private static final String DEF_TRUST_ANCHORS = "CA_DSA_1024:CA_DSA_2048:" +
79             "CA_ECDSA_SECP256R1:CA_ECDSA_SECP384R1:CA_ECDSA_SECP521R1:" +
80             "CA_ED25519:CA_ED448:CA_RSA_2048";
81     private static final String DEF_ALL_EE = "EE_ECDSA_SECP256R1:" +
82             "EE_ECDSA_SECP384R1:EE_ECDSA_SECP521R1:EE_RSA_2048:" +
83             "EE_EC_RSA_SECP256R1:EE_DSA_2048:EE_DSA_1024:EE_ED25519:EE_ED448";
84     private static final List<String> TEST_PROTOS = List.of(
85             "TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1");
86 
87     private static CertificateFactory certFac;
88     private static final Map<ParamType, String> clientParameters =
89             new HashMap<>();
90     private static final Map<ParamType, String> serverParameters =
91             new HashMap<>();
92 
93     private final SessionChecker clientChecker;
94     private final SessionChecker serverChecker;
95     private final Class<? extends Throwable> clientException;
96     private final Class<? extends Throwable> serverException;
97 
98     interface SessionChecker {
99         public void check(SSLSocket socket);
100     }
101 
102     /**
103      * Checks to make sure the end-entity certificate presented by the
104      * peer uses and Ed25519 key.
105      */
106     final static SessionChecker isPeerEd25519 = new SessionChecker() {
107         @Override
108         public void check(SSLSocket sock) {
109             try {
110                 SSLSession session = sock.getSession();
111                 System.out.println("Peer certificate check for Ed25519:\n" +
112                         sessionDump(session));
113                 Certificate[] serverCertChain = session.getPeerCertificates();
114                 X509Certificate tlsCert = (X509Certificate)serverCertChain[0];
115                 keyCheck(tlsCert.getPublicKey(), "EdDSA", "Ed25519");
116             } catch (SSLPeerUnverifiedException sslpe) {
117                 throw new RuntimeException(sslpe);
118             }
119         }
120     };
121 
122     /**
123      * Checks to make sure the end-entity certificate presented by the
124      * peer uses and Ed448 key.
125      */
126     final static SessionChecker isPeerEd448 = new SessionChecker() {
127         @Override
128         public void check(SSLSocket sock) {
129             try {
130                 SSLSession session = sock.getSession();
131                 System.out.println("Peer certificate check for Ed448:\n" +
132                         sessionDump(session));
133                 Certificate[] serverCertChain = session.getPeerCertificates();
134                 X509Certificate tlsCert = (X509Certificate)serverCertChain[0];
135                 keyCheck(tlsCert.getPublicKey(), "EdDSA", "Ed448");
136             } catch (SSLPeerUnverifiedException sslpe) {
137                 throw new RuntimeException(sslpe);
138             }
139         }
140     };
141 
142     /**
143      * Checks to make sure the end-entity certificate presented by the
144      * peer uses an EC secp521r1 key.
145      */
main(String[] args)146     final static SessionChecker isPeerP521 = new SessionChecker() {
147         @Override
148         public void check(SSLSocket sock) {
149             try {
150                 SSLSession session = sock.getSession();
151                 System.out.println("Peer certificate check for secp521r1:\n" +
152                         sessionDump(session));
153                 Certificate[] serverCertChain = session.getPeerCertificates();
154                 X509Certificate tlsCert = (X509Certificate)serverCertChain[0];
155                 keyCheck(tlsCert.getPublicKey(), "EC", "secp521r1");
156             } catch (SSLPeerUnverifiedException sslpe) {
157                 throw new RuntimeException(sslpe);
158             }
159         }
160     };
161 
162     /**
163      * Returns a String summary of an SSLSession object
164      *
165      * @param sess the SSLSession object to be dumped
166      *
167      * @return a String representation of the test-relevant portions of the
168      *      SSLSession object.
169      */
170     private static String sessionDump(SSLSession sess) {
171         StringBuilder sb = new StringBuilder();
172         sb.append("----- Session Info -----\n");
Server(String tlsProtocol, KeyType keyType, String cipher, CountDownLatch latch)173         sb.append("Protocol: ").append(sess.getProtocol()).append("\n");
174         sb.append("Cipher Suite: ").append(sess.getCipherSuite());
175         Certificate[] localCerts = sess.getLocalCertificates();
176         if (localCerts != null) {
177             sb.append("\nLocal Certs:");
178             int i = 0;
179             for (Certificate cert : localCerts) {
180                 sb.append(String.format("\n   [%d]: %s", i++,
start()181                         ((X509Certificate)cert).getSubjectX500Principal()));
182             }
183         }
184         try {
185             Certificate[] peerCerts = sess.getPeerCertificates();
186             if (peerCerts != null) {
187                 sb.append("\nPeer Certs:");
188                 int i = 0;
189                 for (Certificate cert : peerCerts) {
190                     sb.append(String.format("\n   [%d]: %s", i++,
191                             ((X509Certificate)cert).getSubjectX500Principal()));
192                 }
193             }
194         } catch (SSLPeerUnverifiedException sslex) {
195             throw new RuntimeException(sslex);
196         }
197 
198         return sb.toString();
199     }
200 
201     /**
202      * Checks to make sure the public key conforms to the expected key type
203      * and (where applicable) curve.
204      *
doServerSide()205      * @param pubKey the public key to be checked
206      * @param expPkType the expected key type (RSA/DSA/EC/EdDSA)
207      * @param expCurveName if an EC/EdDSA key, the expected curve
208      */
209     private static void keyCheck(PublicKey pubKey, String expPkType,
210             String expCurveName) {
211         String curveName = null;
212         String pubKeyAlg = pubKey.getAlgorithm();
213         if (!expPkType.equalsIgnoreCase(pubKeyAlg)) {
214             throw new RuntimeException("Expected " + expPkType + " key, got " +
215                     pubKeyAlg);
216         }
217 
218         // Check the curve type
219         if (expCurveName != null) {
220             switch (pubKeyAlg) {
221                 case "EdDSA":
222                     curveName = ((EdECKey)pubKey).getParams().getName().
223                             toLowerCase();
224                     if (!expCurveName.equalsIgnoreCase(curveName)) {
225                         throw new RuntimeException("Expected " + expCurveName +
226                                 " curve, " + "got " + curveName);
227                     }
228                     break;
229                 case "EC":
230                     curveName = ((ECKey)pubKey).getParams().toString().
231                             toLowerCase();
232                     if (!curveName.contains(expCurveName.toLowerCase())) {
233                         throw new RuntimeException("Expected " + expCurveName +
234                                 " curve, " + "got " + curveName);
235                     }
236                     break;
237                 default:
238                     throw new IllegalArgumentException(
239                             "Unsupported key type: " + pubKeyAlg);
240             }
241         }
242         System.out.format("Found key: %s / %s\n", pubKeyAlg,
243                 curveName != null ? curveName : "");
244     }
245 
246     TLSWithEdDSA(SessionChecker cliChk, Class<? extends Throwable> cliExpExc,
247             SessionChecker servChk, Class<? extends Throwable> servExpExc) {
248         super();
run()249         clientChecker = cliChk;
250         clientException = cliExpExc;
251         serverChecker = servChk;
252         serverException = servExpExc;
253     }
254 
255     /**
256      * Creates an SSLContext for use with the client side of this test.  This
257      * uses parameters held in the static client parameters map.
258      *
259      * @return an initialized SSLContext for use with the client.
260      *
261      * @throws Exception if any downstream errors occur during key store
262      *      creation, key/trust manager factory creation or context
263      *      initialization.
264      */
265     @Override
266     protected SSLContext createClientSSLContext() throws Exception {
267         KeyStore clientKeyStore = createKeyStore(
268                 clientParameters.getOrDefault(ParamType.KSENTRIES, ""),
269                 PASSWD.toCharArray());
Client(String tlsProtocol, KeyType keyType, String cipher, int serverPort)270         KeyStore clientTrustStore = createTrustStore(
271                 clientParameters.getOrDefault(ParamType.TSENTRIES,
272                         DEF_TRUST_ANCHORS));
273         return createCtxCommon(clientKeyStore,
274                 clientParameters.get(ParamType.CERTALIAS), PASSWD.toCharArray(),
275                 clientTrustStore, "jdk.tls.client.SignatureSchemes",
276                 clientParameters.get(ParamType.SIGALGS));
277     }
doClientSide()278 
279     /**
280      * Creates an SSLContext for use with the server side of this test.  This
281      * uses parameters held in the static server parameters map.
282      *
283      * @return an initialized SSLContext for use with the server.
284      *
285      * @throws Exception if any downstream errors occur during key store
286      *      creation, key/trust manager factory creation or context
287      *      initialization.
288      */
289     @Override
290     protected SSLContext createServerSSLContext() throws Exception {
291         KeyStore serverKeyStore = createKeyStore(
292                 serverParameters.getOrDefault(ParamType.KSENTRIES, ""),
293                 PASSWD.toCharArray());
294         KeyStore serverTrustStore = createTrustStore(
295                 serverParameters.getOrDefault(ParamType.TSENTRIES,
296                         DEF_TRUST_ANCHORS));
297         return createCtxCommon(serverKeyStore,
298                 serverParameters.get(ParamType.CERTALIAS), PASSWD.toCharArray(),
299                 serverTrustStore, "jdk.tls.server.SignatureSchemes",
300                 serverParameters.get(ParamType.SIGALGS));
301     }
302 
303     /**
304      * Create a trust store containing any CA certificates designated as
305      * trust anchors.
306      *
307      * @return the trust store populated with the root CA certificate.
308      *
309      * @throws GeneralSecurityException if any certificates cannot be added to
310      *      the key store.
311      */
312     private static KeyStore createTrustStore(String certEnumNames)
313             throws GeneralSecurityException {
314         KeyStore.Builder keyStoreBuilder =
315                 KeyStore.Builder.newInstance("PKCS12", null,
getSSLContext(String tlsProtocol, String trustedCertStr, String keyCertStr, String privateKey, String keyType)316                         new KeyStore.PasswordProtection(PASSWD.toCharArray()));
317         KeyStore ks = keyStoreBuilder.getKeyStore();
318         for (String certName : certEnumNames.split(":")) {
319             try {
320                 SSLSocketTemplate.Cert cert =
321                         SSLSocketTemplate.Cert.valueOf(certName);
322                 ks.setCertificateEntry(certName, pem2Cert(cert.certStr));
323             } catch (IllegalArgumentException iae) {
324                 System.out.println("Unable to find Cert enum entry for " +
325                         certName + ", skipping");
326             }
327         }
328         return ks;
329     }
330 
331     /**
332      * Create a key store containing any end-entity private keys/certs
333      * specified in the parameters.
334      *
335      * @param certEnumNames a colon-delimited list of String values that are
336      *      the names of the SSLSocketTemplate.Cert enumeration entries.
337      * @param pass the desired password for the resulting KeyStore object.
338      *
339      * @return a populated, loaded KeyStore ready for use.
340      *
341      * @throws GeneralSecurityException if any issues occur while setting
342      *      the private key or certificate entries.
343      */
344     private static KeyStore createKeyStore(String certEnumNames, char[] pass)
345             throws GeneralSecurityException {
346         KeyStore.Builder keyStoreBuilder =
347                 KeyStore.Builder.newInstance("PKCS12", null,
348                         new KeyStore.PasswordProtection(pass));
349         KeyStore ks = keyStoreBuilder.getKeyStore();
350         if (certEnumNames != null && !certEnumNames.isEmpty()) {
351             for (String certName : certEnumNames.split(":")) {
352                 try {
353                     SSLSocketTemplate.Cert cert =
354                             SSLSocketTemplate.Cert.valueOf(certName);
355                     ks.setKeyEntry(certName,
356                             pem2PrivKey(cert.privKeyStr, cert.keyAlgo), pass,
357                             new Certificate[] { pem2Cert(cert.certStr) });
358                 } catch (IllegalArgumentException iae) {
359                     System.out.println("Unable to find Cert enum entry for " +
360                             certName + ", skipping");
361                 }
362             }
363         }
364 
365         return ks;
366     }
367 
368     /**
369      * Covert a PEM-encoded certificate into a X509Certificate object.
370      *
371      * @param certPem the PEM encoding for the certificate.
372      *
373      * @return the corresponding X509Certificate object for the provided PEM.
374      *
375      * @throws CertificateException if any decoding errors occur.
376      */
377     private static X509Certificate pem2Cert(String certPem)
378             throws CertificateException {
379         return (X509Certificate)certFac.generateCertificate(
380                 new ByteArrayInputStream(certPem.getBytes(
381                         Charset.forName("UTF-8"))));
382     }
383 
384     /**
385      * Covert a PEM-encoded PKCS8 private key into a PrivateKey object.
386      *
387      * @param keyPem the PEM encoding for the certificate.
388      * @param keyAlg the algorithm for the private key contained in the PKCS8
389      *  `   encoding.
390      *
391      * @return the corresponding PrivateKey object for the provided PEM.
392      *
393      * @throws GeneralSecurityException if any decoding errors occur.
394      */
395     private static PrivateKey pem2PrivKey(String keyPem, String keyAlg)
396             throws GeneralSecurityException {
397         PKCS8EncodedKeySpec p8Spec = new PKCS8EncodedKeySpec(
398                 Base64.getMimeDecoder().decode(keyPem));
399         KeyFactory keyFac = KeyFactory.getInstance(keyAlg);
400         return keyFac.generatePrivate(p8Spec);
401     }
402 
403     /**
404      * Create an SSLContext for use with the client or server sides of this
405      * test.
406      *
407      * @param keys the key store object for this SSLContext.
408      * @param alias optional alias specifier to exclusively use that alias for
409      *      TLS connections.
410      * @param pass the key store password
411      * @param trust the trust store object
412      * @param sigAlgProp the signature algorithm property name to set
413      *      (reserved for future use pending the fix for JDK-8255867)
414      * @param sigAlgVal the property value to be applied.
415      *
416      * @return an initialized SSLContext object.
417      *
418      * @throws IOException if any IOExceptions during manager factory creation
419      *      take place
420      * @throws GeneralSecurityException any other failure during SSLContext
421      *      creation/initialization
422      */
423     private static SSLContext createCtxCommon(KeyStore keys, String alias,
424             char[] pass, KeyStore trust, String sigAlgProp, String sigAlgVal)
425             throws IOException, GeneralSecurityException {
426         SSLContext ctx;
427         if (sigAlgVal != null && !sigAlgVal.isEmpty()) {
428             System.setProperty(sigAlgProp, sigAlgVal);
429         }
430 
431         // If an alias is specified use our local AliasKeyManager
432         KeyManager[] kms = (alias != null && !alias.isEmpty()) ?
433                 new KeyManager[] { new AliasKeyManager(keys, pass, alias) } :
434                 createKeyManagerFactory(keys, pass).getKeyManagers();
435 
436         ctx = SSLContext.getInstance("TLS");
437         ctx.init(kms, createTrustManagerFactory(trust).getTrustManagers(),
438                 null);
439         return ctx;
440     }
441 
442     /**
443      * Creates a KeyManagerFactory for use during SSLContext initialization.
444      *
445      * @param ks the KeyStore forming the base of the KeyManagerFactory
446      * @param passwd the password to use for the key store
447      *
448      * @return the initialized KeyManagerFactory
449      *
450      * @throws IOException any IOExceptions during key manager factory
451      *      initialization.
452      * @throws GeneralSecurityException if any failures during instantiation
453      *      take place.
454      */
455     private static KeyManagerFactory createKeyManagerFactory(KeyStore ks,
456             char[] passwd) throws IOException, GeneralSecurityException {
457         KeyManagerFactory kmf;
458         kmf = KeyManagerFactory.getInstance("SunX509");
459         kmf.init(ks, passwd);
460 
461         KeyManager[] kmgrs = kmf.getKeyManagers();
462         X509ExtendedKeyManager xkm = (X509ExtendedKeyManager)kmgrs[0];
463         return kmf;
464     }
465 
466     /**
467      * Creates a TrustManagerFactory for use during SSLContext initialization.
468      *
469      * @param trustStrore the KeyStore forming the base of the
470      *      TrustManagerFactory
471      *
472      * @return the initialized TrustManagerFactory
473      *
474      * @throws IOException any IOExceptions during trust manager factory
475      *      initialization.
476      * @throws GeneralSecurityException if any failures during instantiation
477      *      take place.
478      */
479     private static TrustManagerFactory createTrustManagerFactory(
480             KeyStore trustStore) throws IOException, GeneralSecurityException {
481         TrustManagerFactory tmf;
482         PKIXBuilderParameters pkixParams =
483                 new PKIXBuilderParameters(trustStore, new X509CertSelector());
484         pkixParams.setRevocationEnabled(false);
485         tmf = TrustManagerFactory.getInstance("PKIX");
486         tmf.init(new CertPathTrustManagerParameters(pkixParams));
487         return tmf;
488     }
489 
490     /*
491      * Configure the client side socket.
492      */
493     @Override
494     protected void configureClientSocket(SSLSocket socket) {
495         String pVal;
496         if ((pVal = clientParameters.get(ParamType.PROTOS)) != null) {
497             socket.setEnabledProtocols(pVal.split(":"));
498         }
499 
500         if ((pVal = clientParameters.get(ParamType.CIPHERS)) != null) {
501             socket.setEnabledCipherSuites(pVal.split(":"));
502         }
503     }
504 
505     /*
506      * Configure the server side socket.
507      */
508     @Override
509     protected void configureServerSocket(SSLServerSocket socket) {
510         String pVal;
511         try {
512             socket.setReuseAddress(true);
513             if ((pVal = serverParameters.get(ParamType.PROTOS)) != null) {
514                 socket.setEnabledProtocols(pVal.split(":"));
515             }
516 
517             if ((pVal = serverParameters.get(ParamType.CIPHERS)) != null) {
518                 socket.setEnabledCipherSuites(pVal.split(":"));
519             }
520 
521             pVal = serverParameters.get(ParamType.CLIAUTH);
522             socket.setWantClientAuth("WANT".equalsIgnoreCase(pVal));
523             socket.setNeedClientAuth("NEED".equalsIgnoreCase(pVal));
524         } catch (SocketException se) {
525             throw new RuntimeException(se);
526         }
527     }
528 
529 
530     @Override
531     protected void runServerApplication(SSLSocket socket) throws Exception {
532         InputStream sslIS = socket.getInputStream();
533         OutputStream sslOS = socket.getOutputStream();
534 
535         sslIS.read();
536         sslOS.write(85);
537         sslOS.flush();
538 
539         if (serverChecker != null) {
540             serverChecker.check(socket);
541         }
542     }
543 
544     @Override
545     protected void runClientApplication(SSLSocket socket) throws Exception {
546         InputStream sslIS = socket.getInputStream();
547         OutputStream sslOS = socket.getOutputStream();
548 
549         sslOS.write(280);
550         sslOS.flush();
551         sslIS.read();
552 
553         if (clientChecker != null) {
554             clientChecker.check(socket);
555         }
556     }
557 
558     public static void main(String[] args) throws Exception {
559         SecurityUtils.removeFromDisabledTlsAlgs("TLSv1.1", "TLSv1");
560         certFac = CertificateFactory.getInstance("X.509");
561         String testFormat;
562 
563         System.out.println("===== Test KeyManager alias retrieval =====");
564         testKeyManager(DEF_ALL_EE, "EdDSA",
565                 new String[] {"ee_ed25519", "ee_ed448"});
566 
567         testFormat =
568                 "===== Basic Ed25519 Server-side Authentication: %s =====\n";
569         serverParameters.put(ParamType.KSENTRIES, "EE_ED25519:EE_RSA_2048");
570         runtest(testFormat, isPeerEd25519, null, null, null);
571 
572         testFormat =
573                 "===== Basic Ed448 Server-side Authentication: %s =====\n";
574         serverParameters.put(ParamType.KSENTRIES, "EE_ED448:EE_RSA_2048");
575         runtest(testFormat, isPeerEd448, null, null, null);
576 
577         testFormat = "===== EC favored over EdDSA by default: %s =====\n";
578         serverParameters.put(ParamType.KSENTRIES,
579                 "EE_ED25519:EE_ECDSA_SECP521R1");
580         runtest(testFormat, isPeerP521, null, null, null);
581 
582         testFormat = "===== Override EC favoring by alias: %s =====\n";
583         serverParameters.put(ParamType.CERTALIAS, "EE_ED25519");
584         runtest(testFormat, isPeerEd25519, null, null, null);
585         serverParameters.remove(ParamType.CERTALIAS);
586 
587         testFormat = "===== EdDSA Client Authentication: %s =====\n";
588         serverParameters.put(ParamType.KSENTRIES, "EE_RSA_2048");
589         serverParameters.put(ParamType.CLIAUTH, "NEED");
590         clientParameters.put(ParamType.KSENTRIES, "EE_ED25519");
591         runtest(testFormat, null, null, isPeerEd25519, null);
592     }
593 
594     private static void testKeyManager(String keyStoreSpec, String keyType,
595             String[] expAliases)
596             throws GeneralSecurityException, IOException {
597         char[] passChar = PASSWD.toCharArray();
598 
599         // Create the KeyManager factory and resulting KeyManager
600         KeyManagerFactory kmf = createKeyManagerFactory(
601                 createKeyStore(keyStoreSpec, passChar), passChar);
602         KeyManager[] kMgrs = kmf.getKeyManagers();
603         X509KeyManager xkm = (X509KeyManager)kMgrs[0];
604 
605         String[] cliEdDSAAlises = xkm.getClientAliases(keyType, null);
606         System.out.format("Client Aliases (%s): ", keyType);
607         for (String alias : cliEdDSAAlises) {
608             System.out.print(alias + " ");
609         }
610         System.out.println();
611 
612         String[] servEdDSAAliases = xkm.getServerAliases(keyType, null);
613         System.out.format("Server Aliases (%s): ", keyType);
614             for (String alias : servEdDSAAliases) {
615             System.out.print(alias + " ");
616         }
617         System.out.println();
618 
619         if (!Arrays.equals(cliEdDSAAlises, expAliases)) {
620             throw new RuntimeException("Client alias mismatch");
621         } else if (!Arrays.equals(servEdDSAAliases, expAliases)) {
622             throw new RuntimeException("Server alias mismatch");
623         }
624     }
625 
626     private static void runtest(String testNameFmt, SessionChecker cliChk,
627             Class<? extends Throwable> cliExpExc, SessionChecker servChk,
628             Class<? extends Throwable> servExpExc) {
629         TEST_PROTOS.forEach(protocol -> {
630             clientParameters.put(ParamType.PROTOS, protocol);
631             TLSWithEdDSA testObj = new TLSWithEdDSA(cliChk, cliExpExc, servChk,
632                         servExpExc);
633             System.out.format(testNameFmt, protocol);
634             try {
635                 testObj.run();
636                 if (testObj.clientException != null ||
637                         testObj.serverException != null) {
638                     throw new RuntimeException("Expected exception from " +
639                             "either client or server but was missed");
640                 }
641             } catch (Exception exc) {
642                 if (testObj.clientException == null &&
643                         testObj.serverException == null) {
644                     throw new RuntimeException(
645                             "Expected test failure did not occur");
646                 } else if (testObj.clientException != null &&
647                         !testObj.clientException.isAssignableFrom(exc.getClass())) {
648                     throw new RuntimeException("Unexpected client exception " +
649                             "detected: Expected " +
650                             testObj.clientException.getName() +
651                             ", got " + exc.getClass().getName());
652 
653                 }  else if (testObj.serverException != null &&
654                         !testObj.serverException.isAssignableFrom(exc.getClass())) {
655                     throw new RuntimeException("Unexpected client exception " +
656                             "detected: Expected " +
657                             testObj.serverException.getName() +
658                             ", got " + exc.getClass().getName());
659                 }
660             }
661             System.out.println();
662         });
663     }
664 
665     /**
666      * A Custom KeyManager that allows the user to specify a key/certificate
667      * by alias to be used for any TLS authentication actions.
668      */
669     static class AliasKeyManager implements X509KeyManager {
670         private final String alias;
671         private final KeyStore keystore;
672         private final char[] pass;
673 
674         public AliasKeyManager(KeyStore keystore, char[] pass, String alias) {
675             this.keystore = Objects.requireNonNull(keystore);
676             this.alias = Objects.requireNonNull(alias);
677             this.pass = Objects.requireNonNull(pass);
678         }
679 
680         @Override
681         public PrivateKey getPrivateKey(String alias) {
682             try {
683                 return (PrivateKey)keystore.getKey(alias, pass);
684             } catch (GeneralSecurityException exc) {
685                 throw new RuntimeException(exc);
686             }
687         }
688 
689         @Override
690         public X509Certificate[] getCertificateChain(String alias) {
691             try {
692                 Certificate[] certAr = keystore.getCertificateChain(alias);
693                 return (certAr != null) ? Arrays.copyOf(certAr, certAr.length,
694                         X509Certificate[].class) : null;
695             } catch (KeyStoreException ke) {
696                 throw new RuntimeException(ke);
697             }
698         }
699 
700         @Override
701         public String chooseClientAlias(String[] keyType, Principal[] issuers,
702                 Socket socket) {
703             // Blindly return the one selected alias.
704             return alias;
705         }
706 
707         @Override
708         public String chooseServerAlias(String keyType, Principal[] issuers,
709                 Socket socket) {
710             // Blindly return the one selected alias.
711             return alias;
712         }
713 
714         @Override
715         public String[] getClientAliases(String keyType, Principal[] issuers) {
716             // There can be only one!
717             return new String[] { alias };
718         }
719 
720         @Override
721         public String[] getServerAliases(String keyType, Principal[] issuers) {
722             // There can be only one!
723             return new String[] { alias };
724         }
725     }
726 
727     static enum ParamType {
728         PROTOS,
729         CIPHERS,
730         SIGALGS,
731         CLIAUTH,
732         KSENTRIES,
733         TSENTRIES,
734         CERTALIAS
735     }
736 }