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