1 /* 2 * Copyright (c) 2015, 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 import javax.net.ssl.KeyManagerFactory; 25 import javax.net.ssl.SNIHostName; 26 import javax.net.ssl.SNIMatcher; 27 import javax.net.ssl.SNIServerName; 28 import javax.net.ssl.SSLContext; 29 import javax.net.ssl.SSLEngine; 30 import javax.net.ssl.SSLSession; 31 import javax.net.ssl.SSLEngineResult; 32 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 33 import javax.net.ssl.SSLException; 34 import javax.net.ssl.SSLParameters; 35 import javax.net.ssl.TrustManagerFactory; 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.IOException; 39 import java.nio.ByteBuffer; 40 import java.security.KeyManagementException; 41 import java.security.KeyStore; 42 import java.security.KeyStoreException; 43 import java.security.NoSuchAlgorithmException; 44 import java.security.UnrecoverableKeyException; 45 import java.security.cert.CertificateException; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.HashMap; 49 import java.util.LinkedList; 50 import java.util.List; 51 import java.util.Map; 52 53 /** 54 * Basic class to inherit SSLEngine test cases from it. Tests apply for 55 * the TLS or DTLS security protocols and their versions. 56 */ 57 abstract public class SSLEngineTestCase { 58 59 public enum Ciphers { 60 61 /** 62 * Ciphers supported by the tested SSLEngine without those with 63 * kerberos authentication. 64 */ 65 SUPPORTED_NON_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_CIPHERS, 66 "Supported non kerberos"), 67 /** 68 * Ciphers supported by the tested SSLEngine without those with 69 * kerberos authentication and without those with SHA256 ans SHA384. 70 */ 71 SUPPORTED_NON_KRB_NON_SHA_CIPHERS( 72 SSLEngineTestCase.SUPPORTED_NON_KRB_NON_SHA_CIPHERS, 73 "Supported non kerberos non SHA256 and SHA384"), 74 /** 75 * Ciphers supported by the tested SSLEngine with kerberos 76 * authentication. 77 */ 78 SUPPORTED_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_KRB_CIPHERS, 79 "Supported kerberos"), 80 /** 81 * Ciphers enabled by default for the tested SSLEngine without kerberos 82 * and anon. 83 */ 84 ENABLED_NON_KRB_NOT_ANON_CIPHERS( 85 SSLEngineTestCase.ENABLED_NON_KRB_NOT_ANON_CIPHERS, 86 "Enabled by default non kerberos not anonymous"), 87 /** 88 * Ciphers unsupported by the tested SSLEngine. 89 */ 90 UNSUPPORTED_CIPHERS(SSLEngineTestCase.UNSUPPORTED_CIPHERS, 91 "Unsupported"); 92 93 Ciphers(String[] ciphers, String description) { 94 this.ciphers = ciphers; 95 this.description = description; 96 } 97 98 final String[] ciphers; 99 final String description; 100 } 101 102 /** 103 * Enumeration used to distinguish handshake mode in 104 * {@link SSLEngineTestCase#doHandshake(javax.net.ssl.SSLEngine, 105 * javax.net.ssl.SSLEngine, int, SSLEngineTestCase.HandshakeMode, boolean) 106 * SSLEngineTestCase.doHandshake} method. 107 */ 108 public enum HandshakeMode { 109 110 /** 111 * Initial handshake done for the first time: both engines call 112 * {@link SSLEngine#beginHandshake()} method. 113 */ 114 INITIAL_HANDSHAKE, 115 /** 116 * Repeated handshake done by client: client engine calls 117 * {@link SSLEngine#beginHandshake()} method. 118 */ 119 REHANDSHAKE_BEGIN_CLIENT, 120 /** 121 * Repeated handshake done by server: server engine calls 122 * {@link SSLEngine#beginHandshake()} method. 123 */ 124 REHANDSHAKE_BEGIN_SERVER; 125 } 126 /** 127 * Security protocol to be tested: "TLS" or "DTLS" or their versions, 128 * e.g. "TLSv1", "TLSv1.1", "TLSv1.2", "DTLSv1.0", "DTLSv1.2". 129 */ 130 public static final String TESTED_SECURITY_PROTOCOL 131 = System.getProperty("test.security.protocol", "TLS"); 132 /** 133 * Test mode: "norm", "norm_sni" or "krb". 134 * Modes "norm" and "norm_sni" are used to run 135 * with all supported non-kerberos ciphers. 136 * Mode "krb" is used to run with kerberos ciphers. 137 */ 138 public static final String TEST_MODE 139 = System.getProperty("test.mode", "norm"); 140 141 private static final String FS = System.getProperty("file.separator", "/"); 142 private static final String PATH_TO_STORES = ".." + FS + "etc"; 143 private static final String KEY_STORE_FILE = "keystore"; 144 private static final String TRUST_STORE_FILE = "truststore"; 145 private static final String PASSWD = "passphrase"; 146 147 private static final String KEY_FILE_NAME 148 = System.getProperty("test.src", ".") + FS + PATH_TO_STORES 149 + FS + KEY_STORE_FILE; 150 private static final String TRUST_FILE_NAME 151 = System.getProperty("test.src", ".") + FS + PATH_TO_STORES 152 + FS + TRUST_STORE_FILE; 153 154 // Need an enhancement to use none-static mutable global variables. 155 private static ByteBuffer net; 156 private static boolean doUnwrapForNotHandshakingStatus; 157 private static boolean endHandshakeLoop = false; 158 159 private static final int MAX_HANDSHAKE_LOOPS = 100; 160 private static final String EXCHANGE_MSG_SENT = "Hello, peer!"; 161 private static final String TEST_SRC = System.getProperty("test.src", "."); 162 private static final String KTAB_FILENAME = "krb5.keytab.data"; 163 private static final String KRB_REALM = "TEST.REALM"; 164 private static final String KRBTGT_PRINCIPAL = "krbtgt/" + KRB_REALM; 165 private static final String KRB_USER = "USER"; 166 private static final String KRB_USER_PASSWORD = "password"; 167 private static final String KRB_USER_PRINCIPAL = KRB_USER + "@" + KRB_REALM; 168 private static final String KRB5_CONF_FILENAME = "krb5.conf"; 169 private static final String PATH_TO_COMMON = ".." + FS + "TLSCommon"; 170 private static final String JAAS_CONF_FILE = PATH_TO_COMMON 171 + FS + "jaas.conf"; 172 private static final int DELAY = 1000; 173 private static final String HOST = "localhost"; 174 private static final String SERVER_NAME = "service.localhost"; 175 private static final String SNI_PATTERN = ".*"; 176 177 private static final String[] SUPPORTED_NON_KRB_CIPHERS; 178 179 static { 180 try { 181 String[] allSupportedCiphers = getContext() 182 .createSSLEngine().getSupportedCipherSuites(); 183 List<String> supportedCiphersList = new LinkedList<>(); 184 for (String cipher : allSupportedCiphers) { 185 if (!cipher.contains("KRB5") 186 && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { 187 188 supportedCiphersList.add(cipher); 189 } 190 } 191 SUPPORTED_NON_KRB_CIPHERS = 192 supportedCiphersList.toArray(new String[0]); 193 } catch (Exception ex) { 194 throw new Error("Unexpected issue", ex); 195 } 196 } 197 198 private static final String[] SUPPORTED_NON_KRB_NON_SHA_CIPHERS; 199 200 static { 201 try { 202 String[] allSupportedCiphers = getContext() 203 .createSSLEngine().getSupportedCipherSuites(); 204 List<String> supportedCiphersList = new LinkedList<>(); 205 for (String cipher : allSupportedCiphers) { 206 if (!cipher.contains("KRB5") 207 && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV") 208 && !cipher.endsWith("_SHA256") 209 && !cipher.endsWith("_SHA384")) { 210 supportedCiphersList.add(cipher); 211 } 212 } 213 SUPPORTED_NON_KRB_NON_SHA_CIPHERS 214 = supportedCiphersList.toArray(new String[0]); 215 } catch (Exception ex) { 216 throw new Error("Unexpected issue", ex); 217 } 218 } 219 220 private static final String[] SUPPORTED_KRB_CIPHERS; 221 222 static { 223 try { 224 String[] allSupportedCiphers = getContext() 225 .createSSLEngine().getSupportedCipherSuites(); 226 List<String> supportedCiphersList = new LinkedList<>(); 227 for (String cipher : allSupportedCiphers) { 228 if (cipher.contains("KRB5") 229 && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { 230 supportedCiphersList.add(cipher); 231 } 232 } 233 SUPPORTED_KRB_CIPHERS = supportedCiphersList.toArray(new String[0]); 234 } catch (Exception ex) { 235 throw new Error("Unexpected issue", ex); 236 } 237 } 238 239 private static final String[] ENABLED_NON_KRB_NOT_ANON_CIPHERS; 240 241 static { 242 try { 243 SSLEngine temporary = getContext().createSSLEngine(); 244 temporary.setUseClientMode(true); 245 String[] enabledCiphers = temporary.getEnabledCipherSuites(); 246 List<String> enabledCiphersList = new LinkedList<>(); 247 for (String cipher : enabledCiphers) { 248 if (!cipher.contains("anon") && !cipher.contains("KRB5") 249 && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { 250 enabledCiphersList.add(cipher); 251 } 252 } 253 ENABLED_NON_KRB_NOT_ANON_CIPHERS = 254 enabledCiphersList.toArray(new String[0]); 255 } catch (Exception ex) { 256 throw new Error("Unexpected issue", ex); 257 } 258 } 259 260 private static final String[] UNSUPPORTED_CIPHERS = { 261 "SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA", 262 "SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA", 263 "SSL_DHE_DSS_WITH_RC4_128_SHA", 264 "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", 265 "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA", 266 "SSL_DH_DSS_WITH_DES_CBC_SHA", 267 "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", 268 "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA", 269 "SSL_DH_RSA_WITH_DES_CBC_SHA", 270 "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA", 271 "SSL_FORTEZZA_DMS_WITH_NULL_SHA", 272 "SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA", 273 "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA", 274 "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", 275 "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", 276 "SSL_RSA_FIPS_WITH_DES_CBC_SHA", 277 "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", 278 "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", 279 "TLS_KRB5_WITH_IDEA_CBC_MD5", 280 "TLS_KRB5_WITH_IDEA_CBC_SHA", 281 "SSL_RSA_WITH_IDEA_CBC_SHA", 282 "TLS_DH_RSA_WITH_AES_128_GCM_SHA256", 283 "TLS_DH_RSA_WITH_AES_256_GCM_SHA384", 284 "TLS_DH_DSS_WITH_AES_128_GCM_SHA256", 285 "TLS_DH_DSS_WITH_AES_256_GCM_SHA384" 286 }; 287 288 private final int maxPacketSize; 289 290 /** 291 * Constructs test case with the given MFLN maxMacketSize. 292 * 293 * @param maxPacketSize - MLFN extension max packet size. 294 */ 295 public SSLEngineTestCase(int maxPacketSize) { 296 this.maxPacketSize = maxPacketSize; 297 } 298 299 /** 300 * Constructs test case with {@code maxPacketSize = 0}. 301 */ 302 public SSLEngineTestCase() { 303 this.maxPacketSize = 0; 304 } 305 306 /** 307 * Wraps data with the specified engine. 308 * 309 * @param engine - SSLEngine that wraps data. 310 * @param wrapper - Set wrapper id, e.g. "server" of "client". 311 * Used for logging only. 312 * @param maxPacketSize - Max packet size to check that MFLN extension 313 * works or zero for no check. 314 * @param app - Buffer with data to wrap. 315 * @return - Buffer with wrapped data. 316 * @throws SSLException - thrown on engine errors. 317 */ 318 public static ByteBuffer doWrap(SSLEngine engine, String wrapper, 319 int maxPacketSize, ByteBuffer app) 320 throws SSLException { 321 return doWrap(engine, wrapper, maxPacketSize, 322 app, SSLEngineResult.Status.OK, null); 323 } 324 325 /** 326 * Wraps data with the specified engine. 327 * 328 * @param engine - SSLEngine that wraps data. 329 * @param wrapper - Set wrapper id, e.g. "server" of "client". 330 * Used for logging only. 331 * @param maxPacketSize - Max packet size to check that MFLN extension 332 * works or zero for no check. 333 * @param app - Buffer with data to wrap. 334 * @param result - Array which first element will be used to 335 * output wrap result object. 336 * @return - Buffer with wrapped data. 337 * @throws SSLException - thrown on engine errors. 338 */ 339 public static ByteBuffer doWrap(SSLEngine engine, String wrapper, 340 int maxPacketSize, ByteBuffer app, 341 SSLEngineResult[] result) 342 throws SSLException { 343 return doWrap(engine, wrapper, maxPacketSize, 344 app, SSLEngineResult.Status.OK, result); 345 } 346 347 /** 348 * Wraps data with the specified engine. 349 * 350 * @param engine - SSLEngine that wraps data. 351 * @param wrapper - Set wrapper id, e.g. "server" of "client". 352 * Used for logging only. 353 * @param maxPacketSize - Max packet size to check that MFLN extension 354 * works or zero for no check. 355 * @param app - Buffer with data to wrap. 356 * @param wantedStatus - Specifies expected result status of wrapping. 357 * @return - Buffer with wrapped data. 358 * @throws SSLException - thrown on engine errors. 359 */ 360 public static ByteBuffer doWrap(SSLEngine engine, String wrapper, 361 int maxPacketSize, ByteBuffer app, 362 SSLEngineResult.Status wantedStatus) 363 throws SSLException { 364 return doWrap(engine, wrapper, maxPacketSize, 365 app, wantedStatus, null); 366 } 367 368 /** 369 * Wraps data with the specified engine. 370 * 371 * @param engine - SSLEngine that wraps data. 372 * @param wrapper - Set wrapper id, e.g. "server" of "client". 373 * Used for logging only. 374 * @param maxPacketSize - Max packet size to check that MFLN extension 375 * works or zero for no check. 376 * @param app - Buffer with data to wrap. 377 * @param wantedStatus - Specifies expected result status of wrapping. 378 * @param result - Array which first element will be used to output 379 * wrap result object. 380 * @return - Buffer with wrapped data. 381 * @throws SSLException - thrown on engine errors. 382 */ 383 public static ByteBuffer doWrap(SSLEngine engine, String wrapper, 384 int maxPacketSize, ByteBuffer app, 385 SSLEngineResult.Status wantedStatus, 386 SSLEngineResult[] result) 387 throws SSLException { 388 ByteBuffer net = ByteBuffer.allocate(engine.getSession() 389 .getPacketBufferSize()); 390 SSLEngineResult r = engine.wrap(app, net); 391 net.flip(); 392 int length = net.remaining(); 393 System.out.println(wrapper + " wrapped " + length + " bytes."); 394 System.out.println(wrapper + " handshake status is " 395 + engine.getHandshakeStatus()); 396 if (maxPacketSize < length && maxPacketSize != 0) { 397 throw new AssertionError("Handshake wrapped net buffer length " 398 + length + " exceeds maximum packet size " 399 + maxPacketSize); 400 } 401 checkResult(r, wantedStatus); 402 if (result != null && result.length > 0) { 403 result[0] = r; 404 } 405 return net; 406 } 407 408 /** 409 * Unwraps data with the specified engine. 410 * 411 * @param engine - SSLEngine that unwraps data. 412 * @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for 413 * logging only. 414 * @param net - Buffer with data to unwrap. 415 * @return - Buffer with unwrapped data. 416 * @throws SSLException - thrown on engine errors. 417 */ 418 public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper, 419 ByteBuffer net) throws SSLException { 420 return doUnWrap(engine, unwrapper, 421 net, SSLEngineResult.Status.OK, null); 422 } 423 424 /** 425 * Unwraps data with the specified engine. 426 * 427 * @param engine - SSLEngine that unwraps data. 428 * @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for 429 * logging only. 430 * @param net - Buffer with data to unwrap. 431 * @param result - Array which first element will be used to output wrap 432 * result object. 433 * @return - Buffer with unwrapped data. 434 * @throws SSLException - thrown on engine errors. 435 */ 436 public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper, 437 ByteBuffer net, SSLEngineResult[] result) throws SSLException { 438 return doUnWrap(engine, unwrapper, 439 net, SSLEngineResult.Status.OK, result); 440 } 441 442 /** 443 * Unwraps data with the specified engine. 444 * 445 * @param engine - SSLEngine that unwraps data. 446 * @param unwrapper - Set unwrapper id, e.g. "server" of "client". 447 * Used for logging only. 448 * @param net - Buffer with data to unwrap. 449 * @param wantedStatus - Specifies expected result status of wrapping. 450 * @return - Buffer with unwrapped data. 451 * @throws SSLException - thrown on engine errors. 452 */ 453 public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper, 454 ByteBuffer net, 455 SSLEngineResult.Status wantedStatus) throws SSLException { 456 return doUnWrap(engine, unwrapper, net, wantedStatus, null); 457 } 458 459 /** 460 * Unwraps data with the specified engine. 461 * 462 * @param engine - SSLEngine that unwraps data. 463 * @param unwrapper - Set unwrapper id, e.g. "server" of "client". 464 * Used for logging only. 465 * @param net - Buffer with data to unwrap. 466 * @param wantedStatus - Specifies expected result status of wrapping. 467 * @param result - Array which first element will be used to output 468 * wrap result object. 469 * @return - Buffer with unwrapped data. 470 * @throws SSLException - thrown on engine errors. 471 */ 472 public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper, 473 ByteBuffer net, SSLEngineResult.Status wantedStatus, 474 SSLEngineResult[] result) throws SSLException { 475 476 ByteBuffer app = ByteBuffer.allocate( 477 engine.getSession().getApplicationBufferSize()); 478 int length = net.remaining(); 479 System.out.println(unwrapper + " unwrapping " + length + " bytes..."); 480 SSLEngineResult r = engine.unwrap(net, app); 481 app.flip(); 482 System.out.println(unwrapper + " handshake status is " 483 + engine.getHandshakeStatus()); 484 checkResult(r, wantedStatus); 485 if (result != null && result.length > 0) { 486 result[0] = r; 487 } 488 return app; 489 } 490 491 /** 492 * Does the handshake of the two specified engines according to the 493 * {@code mode} specified. 494 * 495 * @param clientEngine - Client SSLEngine. 496 * @param serverEngine - Server SSLEngine. 497 * @param maxPacketSize - Maximum packet size for MFLN of zero for no limit. 498 * @param mode - Handshake mode according to 499 * {@link HandshakeMode} enum. 500 * @throws SSLException - thrown on engine errors. 501 */ 502 public static void doHandshake(SSLEngine clientEngine, 503 SSLEngine serverEngine, 504 int maxPacketSize, HandshakeMode mode) throws SSLException { 505 506 doHandshake(clientEngine, serverEngine, maxPacketSize, mode, false); 507 } 508 509 /** 510 * Does the handshake of the two specified engines according to the 511 * {@code mode} specified. 512 * 513 * @param clientEngine - Client SSLEngine. 514 * @param serverEngine - Server SSLEngine. 515 * @param maxPacketSize - Maximum packet size for MFLN of zero 516 * for no limit. 517 * @param mode - Handshake mode according to 518 * {@link HandshakeMode} enum. 519 * @param enableReplicatedPacks - Set {@code true} to enable replicated 520 * packet sending. 521 * @throws SSLException - thrown on engine errors. 522 */ 523 public static void doHandshake(SSLEngine clientEngine, 524 SSLEngine serverEngine, int maxPacketSize, 525 HandshakeMode mode, 526 boolean enableReplicatedPacks) throws SSLException { 527 528 System.out.println("============================================="); 529 System.out.println("Starting handshake " + mode.name()); 530 int loop = 0; 531 if (maxPacketSize < 0) { 532 throw new Error("Test issue: maxPacketSize is less than zero!"); 533 } 534 SSLParameters params = clientEngine.getSSLParameters(); 535 params.setMaximumPacketSize(maxPacketSize); 536 clientEngine.setSSLParameters(params); 537 params = serverEngine.getSSLParameters(); 538 params.setMaximumPacketSize(maxPacketSize); 539 serverEngine.setSSLParameters(params); 540 SSLEngine firstEngine; 541 SSLEngine secondEngine; 542 switch (mode) { 543 case INITIAL_HANDSHAKE: 544 firstEngine = clientEngine; 545 secondEngine = serverEngine; 546 doUnwrapForNotHandshakingStatus = false; 547 clientEngine.beginHandshake(); 548 serverEngine.beginHandshake(); 549 break; 550 case REHANDSHAKE_BEGIN_CLIENT: 551 firstEngine = clientEngine; 552 secondEngine = serverEngine; 553 doUnwrapForNotHandshakingStatus = true; 554 clientEngine.beginHandshake(); 555 break; 556 case REHANDSHAKE_BEGIN_SERVER: 557 firstEngine = serverEngine; 558 secondEngine = clientEngine; 559 doUnwrapForNotHandshakingStatus = true; 560 serverEngine.beginHandshake(); 561 break; 562 default: 563 throw new Error("Test issue: unknown handshake mode"); 564 } 565 endHandshakeLoop = false; 566 while (!endHandshakeLoop) { 567 if (++loop > MAX_HANDSHAKE_LOOPS) { 568 throw new Error("Too much loops for handshaking"); 569 } 570 System.out.println("============================================"); 571 System.out.println("Handshake loop " + loop + ": round 1"); 572 System.out.println("=========================="); 573 handshakeProcess(firstEngine, secondEngine, maxPacketSize, 574 enableReplicatedPacks); 575 if (endHandshakeLoop) { 576 break; 577 } 578 System.out.println("Handshake loop " + loop + ": round 2"); 579 System.out.println("=========================="); 580 handshakeProcess(secondEngine, firstEngine, maxPacketSize, 581 enableReplicatedPacks); 582 } 583 } 584 585 /** 586 * Routine to send application data from one SSLEngine to another. 587 * 588 * @param fromEngine - Sending engine. 589 * @param toEngine - Receiving engine. 590 * @return - Result of unwrap method of the receiving engine. 591 * @throws SSLException - thrown on engine errors. 592 */ 593 public static SSLEngineResult sendApplicationData(SSLEngine fromEngine, 594 SSLEngine toEngine) 595 throws SSLException { 596 String sender = null; 597 String reciever = null; 598 String excMsgSent = EXCHANGE_MSG_SENT; 599 if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) { 600 sender = "Client"; 601 reciever = "Server"; 602 excMsgSent += " Client."; 603 } else if (toEngine.getUseClientMode() && 604 !fromEngine.getUseClientMode()) { 605 sender = "Server"; 606 reciever = "Client"; 607 excMsgSent += " Server."; 608 } else { 609 throw new Error("Test issue: both engines are in the same mode"); 610 } 611 System.out.println("============================================="); 612 System.out.println("Trying to send application data from " + sender 613 + " to " + reciever); 614 ByteBuffer clientAppSent 615 = ByteBuffer.wrap(excMsgSent.getBytes()); 616 net = doWrap(fromEngine, sender, 0, clientAppSent); 617 SSLEngineResult[] r = new SSLEngineResult[1]; 618 ByteBuffer serverAppRecv = doUnWrap(toEngine, reciever, net, r); 619 byte[] serverAppRecvTrunc = Arrays.copyOf(serverAppRecv.array(), 620 serverAppRecv.limit()); 621 String msgRecv = new String(serverAppRecvTrunc); 622 if (!msgRecv.equals(excMsgSent)) { 623 throw new AssertionError(sender + " to " + reciever 624 + ": application data" 625 + " has been altered while sending." 626 + " Message sent: " + "\"" + excMsgSent + "\"." 627 + " Message recieved: " + "\"" + msgRecv + "\"."); 628 } 629 System.out.println("Successful sending application data from " + sender 630 + " to " + reciever); 631 return r[0]; 632 } 633 634 /** 635 * Close engines by sending "close outbound" message from one SSLEngine to 636 * another. 637 * 638 * @param fromEngine - Sending engine. 639 * @param toEngine - Receiving engine. 640 * @throws SSLException - thrown on engine errors. 641 */ 642 public static void closeEngines(SSLEngine fromEngine, 643 SSLEngine toEngine) throws SSLException { 644 String from = null; 645 String to = null; 646 ByteBuffer app; 647 if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) { 648 from = "Client"; 649 to = "Server"; 650 } else if (toEngine.getUseClientMode() && 651 !fromEngine.getUseClientMode()) { 652 from = "Server"; 653 to = "Client"; 654 } else { 655 throw new Error("Both engines are in the same mode"); 656 } 657 System.out.println("============================================="); 658 System.out.println( 659 "Trying to close engines from " + from + " to " + to); 660 // Sending close outbound request to peer 661 fromEngine.closeOutbound(); 662 app = ByteBuffer.allocate( 663 fromEngine.getSession().getApplicationBufferSize()); 664 net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED); 665 doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED); 666 app = ByteBuffer.allocate( 667 fromEngine.getSession().getApplicationBufferSize()); 668 net = doWrap(toEngine, to, 0, app, SSLEngineResult.Status.CLOSED); 669 doUnWrap(fromEngine, from, net, SSLEngineResult.Status.CLOSED); 670 if (!toEngine.isInboundDone()) { 671 throw new AssertionError(from + " sent close request to " + to 672 + ", but " + to + "did not close inbound."); 673 } 674 // Executing close inbound 675 fromEngine.closeInbound(); 676 app = ByteBuffer.allocate( 677 fromEngine.getSession().getApplicationBufferSize()); 678 net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED); 679 doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED); 680 if (!toEngine.isOutboundDone()) { 681 throw new AssertionError(from + "sent close request to " + to 682 + ", but " + to + "did not close outbound."); 683 } 684 System.out.println("Successful closing from " + from + " to " + to); 685 } 686 687 /** 688 * Runs the same test case for all given {@code ciphers}. Method counts all 689 * failures and throws {@code AssertionError} if one or more tests fail. 690 * 691 * @param ciphers - Ciphers that should be tested. 692 */ 693 public void runTests(Ciphers ciphers) { 694 int total = ciphers.ciphers.length; 695 int failed = testSomeCiphers(ciphers); 696 if (failed > 0) { 697 throw new AssertionError("" + failed + " of " + total 698 + " tests failed!"); 699 } 700 System.out.println("All tests passed!"); 701 } 702 703 /** 704 * Runs test cases for ciphers defined by the test mode. 705 */ 706 public void runTests() { 707 switch (TEST_MODE) { 708 case "norm": 709 case "norm_sni": 710 switch (TESTED_SECURITY_PROTOCOL) { 711 case "DTLSv1.0": 712 case "TLSv1": 713 case "TLSv1.1": 714 runTests(Ciphers.SUPPORTED_NON_KRB_NON_SHA_CIPHERS); 715 break; 716 default: 717 runTests(Ciphers.SUPPORTED_NON_KRB_CIPHERS); 718 } 719 break; 720 case "krb": 721 runTests(Ciphers.SUPPORTED_KRB_CIPHERS); 722 break; 723 default: 724 throw new Error( 725 "Test error: unexpected test mode: " + TEST_MODE); 726 } 727 } 728 729 /** 730 * Returns maxPacketSize value used for MFLN extension testing 731 * 732 * @return - MLFN extension max packet size. 733 */ 734 public int getMaxPacketSize() { 735 return maxPacketSize; 736 } 737 738 /** 739 * Checks that status of result {@code r} is {@code wantedStatus}. 740 * 741 * @param r - Result. 742 * @param wantedStatus - Wanted status of the result. 743 * @throws AssertionError - if status or {@code r} is not 744 * {@code wantedStatus}. 745 */ 746 public static void checkResult(SSLEngineResult r, 747 SSLEngineResult.Status wantedStatus) { 748 SSLEngineResult.Status rs = r.getStatus(); 749 if (!rs.equals(wantedStatus)) { 750 throw new AssertionError("Unexpected status " + rs.name() 751 + ", should be " + wantedStatus.name()); 752 } 753 } 754 755 /** 756 * Returns SSLContext with TESTED_SECURITY_PROTOCOL protocol and 757 * sets up keys. 758 * 759 * @return - SSLContext with a protocol specified by 760 * TESTED_SECURITY_PROTOCOL. 761 */ 762 public static SSLContext getContext() { 763 try { 764 java.security.Security.setProperty( 765 "jdk.tls.disabledAlgorithms", ""); 766 java.security.Security.setProperty( 767 "jdk.certpath.disabledAlgorithms", ""); 768 KeyStore ks = KeyStore.getInstance("JKS"); 769 KeyStore ts = KeyStore.getInstance("JKS"); 770 char[] passphrase = PASSWD.toCharArray(); 771 try (FileInputStream keyFileStream = 772 new FileInputStream(KEY_FILE_NAME)) { 773 ks.load(keyFileStream, passphrase); 774 } 775 try (FileInputStream trustFileStream = 776 new FileInputStream(TRUST_FILE_NAME)) { 777 ts.load(trustFileStream, passphrase); 778 } 779 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 780 kmf.init(ks, passphrase); 781 TrustManagerFactory tmf = 782 TrustManagerFactory.getInstance("SunX509"); 783 tmf.init(ts); 784 SSLContext sslCtx = 785 SSLContext.getInstance(TESTED_SECURITY_PROTOCOL); 786 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 787 return sslCtx; 788 } catch (KeyStoreException | IOException | NoSuchAlgorithmException | 789 CertificateException | UnrecoverableKeyException | 790 KeyManagementException ex) { 791 throw new Error("Unexpected exception", ex); 792 } 793 } 794 795 /** 796 * Sets up and starts kerberos KDC server. 797 */ 798 public static void setUpAndStartKDC() { 799 String servicePrincipal = "host/" + SERVER_NAME + "@" + KRB_REALM; 800 Map<String, String> principals = new HashMap<>(); 801 principals.put(KRB_USER_PRINCIPAL, KRB_USER_PASSWORD); 802 principals.put(KRBTGT_PRINCIPAL, null); 803 principals.put(servicePrincipal, null); 804 System.setProperty("java.security.krb5.conf", KRB5_CONF_FILENAME); 805 startKDC(KRB_REALM, principals, KTAB_FILENAME); 806 System.setProperty("java.security.auth.login.config", 807 TEST_SRC + FS + JAAS_CONF_FILE); 808 System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); 809 } 810 811 /** 812 * Sets up and starts kerberos KDC server if 813 * SSLEngineTestCase.TEST_MODE is "krb". 814 */ 815 public static void setUpAndStartKDCIfNeeded() { 816 if (TEST_MODE.equals("krb")) { 817 setUpAndStartKDC(); 818 } 819 } 820 821 /** 822 * Returns client ssl engine. 823 * 824 * @param context - SSLContext to get SSLEngine from. 825 * @param useSNI - flag used to enable or disable using SNI extension. 826 * Needed for Kerberos. 827 */ 828 public static SSLEngine getClientSSLEngine( 829 SSLContext context, boolean useSNI) { 830 831 SSLEngine clientEngine = context.createSSLEngine(HOST, 80); 832 clientEngine.setUseClientMode(true); 833 if (useSNI) { 834 SNIHostName serverName = new SNIHostName(SERVER_NAME); 835 List<SNIServerName> serverNames = new ArrayList<>(); 836 serverNames.add(serverName); 837 SSLParameters params = clientEngine.getSSLParameters(); 838 params.setServerNames(serverNames); 839 clientEngine.setSSLParameters(params); 840 } 841 return clientEngine; 842 } 843 844 /** 845 * Returns server ssl engine. 846 * 847 * @param context - SSLContext to get SSLEngine from. 848 * @param useSNI - flag used to enable or disable using SNI extension. 849 * Needed for Kerberos. 850 */ 851 public static SSLEngine getServerSSLEngine( 852 SSLContext context, boolean useSNI) { 853 854 SSLEngine serverEngine = context.createSSLEngine(); 855 serverEngine.setUseClientMode(false); 856 if (useSNI) { 857 SNIMatcher matcher = SNIHostName.createSNIMatcher(SNI_PATTERN); 858 List<SNIMatcher> matchers = new ArrayList<>(); 859 matchers.add(matcher); 860 SSLParameters params = serverEngine.getSSLParameters(); 861 params.setSNIMatchers(matchers); 862 serverEngine.setSSLParameters(params); 863 } 864 return serverEngine; 865 } 866 867 /** 868 * Runs the test case for one cipher suite. 869 * 870 * @param cipher - Cipher suite name. 871 * @throws SSLException - If tests fails. 872 */ 873 abstract protected void testOneCipher(String cipher) 874 throws SSLException; 875 876 /** 877 * Iterates through an array of ciphers and runs the same test case for 878 * every entry. 879 * 880 * @param ciphers - Array of cipher names. 881 * @return - Number of tests failed. 882 */ 883 protected int testSomeCiphers(Ciphers ciphers) { 884 int failedNum = 0; 885 String description = ciphers.description; 886 System.out.println("==============================================="); 887 System.out.println(description + " ciphers testing"); 888 System.out.println("==========================================="); 889 for (String cs : ciphers.ciphers) { 890 System.out.println("---------------------------------------"); 891 System.out.println("Testing cipher suite " + cs); 892 System.out.println("---------------------------------------"); 893 Throwable error = null; 894 895 // Reset global mutable static variables 896 net = null; 897 doUnwrapForNotHandshakingStatus = false; 898 endHandshakeLoop = false; 899 900 try { 901 testOneCipher(cs); 902 } catch (Throwable t) { 903 error = t; 904 } 905 switch (ciphers) { 906 case SUPPORTED_NON_KRB_CIPHERS: 907 case SUPPORTED_NON_KRB_NON_SHA_CIPHERS: 908 case SUPPORTED_KRB_CIPHERS: 909 case ENABLED_NON_KRB_NOT_ANON_CIPHERS: 910 if (error != null) { 911 System.out.println("Test Failed: " + cs); 912 System.err.println("Test Exception for " + cs); 913 error.printStackTrace(); 914 failedNum++; 915 } else { 916 System.out.println("Test Passed: " + cs); 917 } 918 break; 919 case UNSUPPORTED_CIPHERS: 920 if (error == null) { 921 System.out.println("Test Failed: " + cs); 922 System.err.println("Test for " + cs + 923 " should have thrown " + 924 "IllegalArgumentException, but it has not!"); 925 failedNum++; 926 } else if (!(error instanceof IllegalArgumentException)) { 927 System.out.println("Test Failed: " + cs); 928 System.err.println("Test Exception for " + cs); 929 error.printStackTrace(); 930 failedNum++; 931 } else { 932 System.out.println("Test Passed: " + cs); 933 } 934 break; 935 default: 936 throw new Error("Test issue: unexpected ciphers: " 937 + ciphers.name()); 938 } 939 } 940 941 return failedNum; 942 } 943 944 /** 945 * Method used for the handshake routine. 946 * 947 * @param wrapingEngine - Engine that is expected to wrap data. 948 * @param unwrapingEngine - Engine that is expected to unwrap data. 949 * @param maxPacketSize - Maximum packet size for MFLN of zero 950 * for no limit. 951 * @param enableReplicatedPacks - Set {@code true} to enable replicated 952 * packet sending. 953 * @throws SSLException - thrown on engine errors. 954 */ 955 private static void handshakeProcess(SSLEngine wrapingEngine, 956 SSLEngine unwrapingEngine, 957 int maxPacketSize, 958 boolean enableReplicatedPacks) throws SSLException { 959 960 HandshakeStatus wrapingHSStatus = wrapingEngine.getHandshakeStatus(); 961 HandshakeStatus unwrapingHSStatus = 962 unwrapingEngine.getHandshakeStatus(); 963 SSLEngineResult r; 964 String wrapper, unwrapper; 965 if (wrapingEngine.getUseClientMode() 966 && !unwrapingEngine.getUseClientMode()) { 967 wrapper = "Client"; 968 unwrapper = "Server"; 969 } else if (unwrapingEngine.getUseClientMode() 970 && !wrapingEngine.getUseClientMode()) { 971 wrapper = "Server"; 972 unwrapper = "Client"; 973 } else { 974 throw new Error("Both engines are in the same mode"); 975 } 976 System.out.println( 977 wrapper + " handshake (wrap) status " + wrapingHSStatus); 978 System.out.println( 979 unwrapper + " handshake (unwrap) status " + unwrapingHSStatus); 980 981 ByteBuffer netReplicatedClient = null; 982 ByteBuffer netReplicatedServer = null; 983 switch (wrapingHSStatus) { 984 case NEED_WRAP: 985 if (enableReplicatedPacks) { 986 if (net != null) { 987 net.flip(); 988 if (net.remaining() != 0) { 989 if (wrapingEngine.getUseClientMode()) { 990 netReplicatedServer = net; 991 } else { 992 netReplicatedClient = net; 993 } 994 } 995 } 996 } 997 ByteBuffer app = ByteBuffer.allocate( 998 wrapingEngine.getSession().getApplicationBufferSize()); 999 net = doWrap(wrapingEngine, wrapper, maxPacketSize, app); 1000 wrapingHSStatus = wrapingEngine.getHandshakeStatus(); 1001 // No break, falling into unwrapping. 1002 case NOT_HANDSHAKING: 1003 switch (unwrapingHSStatus) { 1004 case NEED_TASK: 1005 runDelegatedTasks(unwrapingEngine); 1006 case NEED_UNWRAP: 1007 doUnWrap(unwrapingEngine, unwrapper, net); 1008 if (enableReplicatedPacks) { 1009 System.out.println(unwrapper + 1010 " unwrapping replicated packet..."); 1011 if (unwrapingEngine.getHandshakeStatus() 1012 .equals(HandshakeStatus.NEED_TASK)) { 1013 runDelegatedTasks(unwrapingEngine); 1014 } 1015 ByteBuffer netReplicated; 1016 if (unwrapingEngine.getUseClientMode()) { 1017 netReplicated = netReplicatedClient; 1018 } else { 1019 netReplicated = netReplicatedServer; 1020 } 1021 if (netReplicated != null) { 1022 doUnWrap(unwrapingEngine, 1023 unwrapper, netReplicated); 1024 } else { 1025 net.flip(); 1026 doUnWrap(unwrapingEngine, unwrapper, net); 1027 } 1028 } 1029 break; 1030 case NEED_UNWRAP_AGAIN: 1031 break; 1032 case NOT_HANDSHAKING: 1033 if (doUnwrapForNotHandshakingStatus) { 1034 System.out.println("Not handshake status unwrap"); 1035 doUnWrap(unwrapingEngine, unwrapper, net); 1036 doUnwrapForNotHandshakingStatus = false; 1037 break; 1038 } else { 1039 if (wrapingHSStatus == 1040 HandshakeStatus.NOT_HANDSHAKING) { 1041 System.out.println("Handshake is completed"); 1042 endHandshakeLoop = true; 1043 } 1044 } 1045 break; 1046 case NEED_WRAP: 1047 SSLSession session = unwrapingEngine.getSession(); 1048 int bufferSize = session.getApplicationBufferSize(); 1049 ByteBuffer b = ByteBuffer.allocate(bufferSize); 1050 net = doWrap(unwrapingEngine, 1051 unwrapper, maxPacketSize, b); 1052 unwrapingHSStatus = 1053 unwrapingEngine.getHandshakeStatus(); 1054 if ((wrapingHSStatus == 1055 HandshakeStatus.NOT_HANDSHAKING) && 1056 (unwrapingHSStatus == 1057 HandshakeStatus.NOT_HANDSHAKING)) { 1058 1059 System.out.println("Handshake is completed"); 1060 endHandshakeLoop = true; 1061 } 1062 1063 break; 1064 default: 1065 throw new Error( 1066 "Unexpected unwraping engine handshake status " 1067 + unwrapingHSStatus.name()); 1068 } 1069 break; 1070 case NEED_UNWRAP: 1071 break; 1072 case NEED_UNWRAP_AGAIN: 1073 net.flip(); 1074 doUnWrap(wrapingEngine, wrapper, net); 1075 break; 1076 case NEED_TASK: 1077 runDelegatedTasks(wrapingEngine); 1078 break; 1079 default: 1080 throw new Error("Unexpected wraping engine handshake status " 1081 + wrapingHSStatus.name()); 1082 } 1083 } 1084 1085 private static void runDelegatedTasks(SSLEngine engine) { 1086 Runnable runnable; 1087 System.out.println("Running delegated tasks..."); 1088 while ((runnable = engine.getDelegatedTask()) != null) { 1089 runnable.run(); 1090 } 1091 HandshakeStatus hs = engine.getHandshakeStatus(); 1092 if (hs == HandshakeStatus.NEED_TASK) { 1093 throw new Error("Handshake shouldn't need additional tasks."); 1094 } 1095 } 1096 1097 /** 1098 * Start a KDC server: 1099 * - create a KDC instance 1100 * - create Kerberos principals 1101 * - save Kerberos configuration 1102 * - save keys to keytab file 1103 * - no pre-auth is required 1104 */ 1105 private static void startKDC(String realm, Map<String, String> principals, 1106 String ktab) { 1107 try { 1108 KDC kdc = KDC.create(realm, HOST, 0, true); 1109 kdc.setOption(KDC.Option.PREAUTH_REQUIRED, Boolean.FALSE); 1110 if (principals != null) { 1111 principals.entrySet().stream().forEach((entry) -> { 1112 String name = entry.getKey(); 1113 String password = entry.getValue(); 1114 if (password == null || password.isEmpty()) { 1115 System.out.println("KDC: add a principal '" + name 1116 + "' with a random password"); 1117 kdc.addPrincipalRandKey(name); 1118 } else { 1119 System.out.println("KDC: add a principal '" + name 1120 + "' with '" + password + "' password"); 1121 kdc.addPrincipal(name, password.toCharArray()); 1122 } 1123 }); 1124 } 1125 KDC.saveConfig(KRB5_CONF_FILENAME, kdc); 1126 if (ktab != null) { 1127 File ktabFile = new File(ktab); 1128 if (ktabFile.exists()) { 1129 System.out.println("KDC: append keys to an exising " 1130 + "keytab file " + ktab); 1131 kdc.appendKtab(ktab); 1132 } else { 1133 System.out.println("KDC: create a new keytab file " 1134 + ktab); 1135 kdc.writeKtab(ktab); 1136 } 1137 } 1138 System.out.println("KDC: started on " + HOST + ":" + kdc.getPort() 1139 + " with '" + realm + "' realm"); 1140 } catch (Exception e) { 1141 throw new RuntimeException("KDC: unexpected exception", e); 1142 } 1143 } 1144 }