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 }