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