1 /*
   2  * Copyright (c) 1999, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.ssl;
  27 
  28 import java.net.Socket;
  29 
  30 import java.io.*;
  31 import java.util.*;
  32 import java.security.*;
  33 import java.security.cert.*;
  34 import java.security.cert.Certificate;
  35 
  36 import javax.net.ssl.*;
  37 
  38 import sun.security.provider.certpath.AlgorithmChecker;
  39 import sun.security.action.GetPropertyAction;
  40 
  41 public abstract class SSLContextImpl extends SSLContextSpi {
  42 
  43     private static final Debug debug = Debug.getInstance("ssl");
  44 
  45     private final EphemeralKeyManager ephemeralKeyManager;
  46     private final SSLSessionContextImpl clientCache;
  47     private final SSLSessionContextImpl serverCache;
  48 
  49     private boolean isInitialized;
  50 
  51     private X509ExtendedKeyManager keyManager;
  52     private X509TrustManager trustManager;
  53     private SecureRandom secureRandom;
  54 
  55     SSLContextImpl() {
  56         ephemeralKeyManager = new EphemeralKeyManager();
  57         clientCache = new SSLSessionContextImpl();
  58         serverCache = new SSLSessionContextImpl();
  59     }
  60 
  61     @Override
  62     protected void engineInit(KeyManager[] km, TrustManager[] tm,
  63                                 SecureRandom sr) throws KeyManagementException {
  64         isInitialized = false;
  65         keyManager = chooseKeyManager(km);
  66 
  67         if (tm == null) {
  68             try {
  69                 TrustManagerFactory tmf = TrustManagerFactory.getInstance(
  70                         TrustManagerFactory.getDefaultAlgorithm());
  71                 tmf.init((KeyStore)null);
  72                 tm = tmf.getTrustManagers();
  73             } catch (Exception e) {
  74                 // eat
  75             }
  76         }
  77         trustManager = chooseTrustManager(tm);
  78 
  79         if (sr == null) {
  80             secureRandom = JsseJce.getSecureRandom();
  81         } else {
  82             if (SunJSSE.isFIPS() &&
  83                         (sr.getProvider() != SunJSSE.cryptoProvider)) {
  84                 throw new KeyManagementException
  85                     ("FIPS mode: SecureRandom must be from provider "
  86                     + SunJSSE.cryptoProvider.getName());
  87             }
  88             secureRandom = sr;
  89         }
  90 
  91         /*
  92          * The initial delay of seeding the random number generator
  93          * could be long enough to cause the initial handshake on our
  94          * first connection to timeout and fail. Make sure it is
  95          * primed and ready by getting some initial output from it.
  96          */
  97         if (debug != null && Debug.isOn("sslctx")) {
  98             System.out.println("trigger seeding of SecureRandom");
  99         }
 100         secureRandom.nextInt();
 101         if (debug != null && Debug.isOn("sslctx")) {
 102             System.out.println("done seeding SecureRandom");
 103         }
 104         isInitialized = true;
 105     }
 106 
 107     private X509TrustManager chooseTrustManager(TrustManager[] tm)
 108             throws KeyManagementException {
 109         // We only use the first instance of X509TrustManager passed to us.
 110         for (int i = 0; tm != null && i < tm.length; i++) {
 111             if (tm[i] instanceof X509TrustManager) {
 112                 if (SunJSSE.isFIPS() &&
 113                         !(tm[i] instanceof X509TrustManagerImpl)) {
 114                     throw new KeyManagementException
 115                         ("FIPS mode: only SunJSSE TrustManagers may be used");
 116                 }
 117 
 118                 if (tm[i] instanceof X509ExtendedTrustManager) {
 119                     return (X509TrustManager)tm[i];
 120                 } else {
 121                     return new AbstractTrustManagerWrapper(
 122                                         (X509TrustManager)tm[i]);
 123                 }
 124             }
 125         }
 126 
 127         // nothing found, return a dummy X509TrustManager.
 128         return DummyX509TrustManager.INSTANCE;
 129     }
 130 
 131     private X509ExtendedKeyManager chooseKeyManager(KeyManager[] kms)
 132             throws KeyManagementException {
 133         for (int i = 0; kms != null && i < kms.length; i++) {
 134             KeyManager km = kms[i];
 135             if (!(km instanceof X509KeyManager)) {
 136                 continue;
 137             }
 138             if (SunJSSE.isFIPS()) {
 139                 // In FIPS mode, require that one of SunJSSE's own keymanagers
 140                 // is used. Otherwise, we cannot be sure that only keys from
 141                 // the FIPS token are used.
 142                 if ((km instanceof X509KeyManagerImpl)
 143                             || (km instanceof SunX509KeyManagerImpl)) {
 144                     return (X509ExtendedKeyManager)km;
 145                 } else {
 146                     // throw exception, we don't want to silently use the
 147                     // dummy keymanager without telling the user.
 148                     throw new KeyManagementException
 149                         ("FIPS mode: only SunJSSE KeyManagers may be used");
 150                 }
 151             }
 152             if (km instanceof X509ExtendedKeyManager) {
 153                 return (X509ExtendedKeyManager)km;
 154             }
 155             if (debug != null && Debug.isOn("sslctx")) {
 156                 System.out.println(
 157                     "X509KeyManager passed to " +
 158                     "SSLContext.init():  need an " +
 159                     "X509ExtendedKeyManager for SSLEngine use");
 160             }
 161             return new AbstractKeyManagerWrapper((X509KeyManager)km);
 162         }
 163 
 164         // nothing found, return a dummy X509ExtendedKeyManager
 165         return DummyX509KeyManager.INSTANCE;
 166     }
 167 
 168     @Override
 169     protected SSLSocketFactory engineGetSocketFactory() {
 170         if (!isInitialized) {
 171             throw new IllegalStateException(
 172                 "SSLContextImpl is not initialized");
 173         }
 174        return new SSLSocketFactoryImpl(this);
 175     }
 176 
 177     @Override
 178     protected SSLServerSocketFactory engineGetServerSocketFactory() {
 179         if (!isInitialized) {
 180             throw new IllegalStateException("SSLContext is not initialized");
 181         }
 182         return new SSLServerSocketFactoryImpl(this);
 183     }
 184     abstract SSLEngine createSSLEngineImpl();
 185     abstract SSLEngine createSSLEngineImpl(String host, int port);
 186 
 187     @Override
 188     protected SSLEngine engineCreateSSLEngine() {
 189         if (!isInitialized) {
 190             throw new IllegalStateException(
 191                 "SSLContextImpl is not initialized");
 192         }
 193         return createSSLEngineImpl();
 194     }
 195 
 196     @Override
 197     protected SSLEngine engineCreateSSLEngine(String host, int port) {
 198         if (!isInitialized) {
 199             throw new IllegalStateException(
 200                 "SSLContextImpl is not initialized");
 201         }
 202         return createSSLEngineImpl(host, port);
 203     }
 204 
 205     @Override
 206     protected SSLSessionContext engineGetClientSessionContext() {
 207         return clientCache;
 208     }
 209 
 210     @Override
 211     protected SSLSessionContext engineGetServerSessionContext() {
 212         return serverCache;
 213     }
 214 
 215     SecureRandom getSecureRandom() {
 216         return secureRandom;
 217     }
 218 
 219     X509ExtendedKeyManager getX509KeyManager() {
 220         return keyManager;
 221     }
 222 
 223     X509TrustManager getX509TrustManager() {
 224         return trustManager;
 225     }
 226 
 227     EphemeralKeyManager getEphemeralKeyManager() {
 228         return ephemeralKeyManager;
 229     }
 230 
 231 
 232     // Get supported ProtocolList.
 233     abstract ProtocolList getSuportedProtocolList();
 234 
 235     // Get default ProtocolList for server mode.
 236     abstract ProtocolList getServerDefaultProtocolList();
 237 
 238     // Get default ProtocolList for client mode.
 239     abstract ProtocolList getClientDefaultProtocolList();
 240 
 241     // Get supported CipherSuiteList.
 242     abstract CipherSuiteList getSupportedCipherSuiteList();
 243 
 244     // Get default CipherSuiteList for server mode.
 245     abstract CipherSuiteList getServerDefaultCipherSuiteList();
 246 
 247     // Get default CipherSuiteList for client mode.
 248     abstract CipherSuiteList getClientDefaultCipherSuiteList();
 249 
 250     // Get default ProtocolList.
 251     ProtocolList getDefaultProtocolList(boolean roleIsServer) {
 252         return roleIsServer ? getServerDefaultProtocolList()
 253                 : getClientDefaultProtocolList();
 254     }
 255 
 256     // Get default CipherSuiteList.
 257     CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) {
 258         return roleIsServer ? getServerDefaultCipherSuiteList()
 259                 : getClientDefaultCipherSuiteList();
 260     }
 261 
 262     /**
 263      * Return whether a protocol list is the original default enabled
 264      * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
 265      */
 266     boolean isDefaultProtocolList(ProtocolList protocols) {
 267         return (protocols == getServerDefaultProtocolList()) ||
 268                 (protocols == getClientDefaultProtocolList());
 269     }
 270 
 271     /**
 272      * Return whether a protocol list is the original default enabled
 273      * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
 274      */
 275     boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
 276         return (cipherSuites == getServerDefaultCipherSuiteList()) ||
 277                 (cipherSuites == getClientDefaultCipherSuiteList());
 278     }
 279 
 280     /*
 281      * Return the list of all available CipherSuites with a priority of
 282      * minPriority or above.
 283      */
 284     private static CipherSuiteList getApplicableCipherSuiteList(
 285             ProtocolList protocols, boolean onlyEnabled) {
 286 
 287         int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY;
 288         if (onlyEnabled) {
 289             minPriority = CipherSuite.DEFAULT_SUITES_PRIORITY;
 290         }
 291 
 292         Collection<CipherSuite> allowedCipherSuites =
 293                                     CipherSuite.allowedCipherSuites();
 294 
 295         TreeSet<CipherSuite> suites = new TreeSet<>();
 296         if (!(protocols.collection().isEmpty()) &&
 297                 protocols.min.v != ProtocolVersion.NONE.v) {
 298             for (CipherSuite suite : allowedCipherSuites) {
 299                 if (!suite.allowed || suite.priority < minPriority) {
 300                     continue;
 301                 }
 302 
 303                 if (suite.isAvailable() &&
 304                         suite.obsoleted > protocols.min.v &&
 305                         suite.supported <= protocols.max.v) {
 306                     if (SSLAlgorithmConstraints.DEFAULT.permits(
 307                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 308                             suite.name, null)) {
 309                         suites.add(suite);
 310                     }
 311                 } else if (debug != null &&
 312                         Debug.isOn("sslctx") && Debug.isOn("verbose")) {
 313                     if (suite.obsoleted <= protocols.min.v) {
 314                         System.out.println(
 315                             "Ignoring obsoleted cipher suite: " + suite);
 316                     } else if (suite.supported > protocols.max.v) {
 317                         System.out.println(
 318                             "Ignoring unsupported cipher suite: " + suite);
 319                     } else {
 320                         System.out.println(
 321                             "Ignoring unavailable cipher suite: " + suite);
 322                     }
 323                 }
 324             }
 325         }
 326 
 327         return new CipherSuiteList(suites);
 328     }
 329 
 330     private static String[] getAvailableProtocols(
 331             ProtocolVersion[] protocolCandidates) {
 332 
 333         List<String> availableProtocols = Collections.<String>emptyList();
 334         if (protocolCandidates !=  null && protocolCandidates.length != 0) {
 335             availableProtocols = new ArrayList<>(protocolCandidates.length);
 336             for (ProtocolVersion p : protocolCandidates) {
 337                 if (ProtocolVersion.availableProtocols.contains(p)) {
 338                     availableProtocols.add(p.name);
 339                 }
 340             }
 341         }
 342 
 343         return availableProtocols.toArray(new String[0]);
 344     }
 345 
 346     /*
 347      * The SSLContext implementation for TLS/SSL algorithm
 348      *
 349      * SSL/TLS protocols specify the forward compatibility and version
 350      * roll-back attack protections, however, a number of SSL/TLS server
 351      * vendors did not implement these aspects properly, and some current
 352      * SSL/TLS servers may refuse to talk to a TLS 1.1 or later client.
 353      *
 354      * Considering above interoperability issues, SunJSSE will not set
 355      * TLS 1.1 and TLS 1.2 as the enabled protocols for client by default.
 356      *
 357      * For SSL/TLS servers, there is no such interoperability issues as
 358      * SSL/TLS clients. In SunJSSE, TLS 1.1 or later version will be the
 359      * enabled protocols for server by default.
 360      *
 361      * We may change the behavior when popular TLS/SSL vendors support TLS
 362      * forward compatibility properly.
 363      *
 364      * SSLv2Hello is no longer necessary.  This interoperability option was
 365      * put in place in the late 90's when SSLv3/TLS1.0 were relatively new
 366      * and there were a fair number of SSLv2-only servers deployed.  Because
 367      * of the security issues in SSLv2, it is rarely (if ever) used, as
 368      * deployments should now be using SSLv3 and TLSv1.
 369      *
 370      * Considering the issues of SSLv2Hello, we should not enable SSLv2Hello
 371      * by default. Applications still can use it by enabling SSLv2Hello with
 372      * the series of setEnabledProtocols APIs.
 373      */
 374 
 375     /*
 376      * The base abstract SSLContext implementation for the Transport Layer
 377      * Security (TLS) protocols.
 378      *
 379      * This abstract class encapsulates supported and the default server
 380      * SSL/TLS parameters.
 381      *
 382      * @see SSLContext
 383      */
 384     private abstract static class AbstractTLSContext extends SSLContextImpl {
 385         private static final ProtocolList supportedProtocolList;
 386         private static final ProtocolList serverDefaultProtocolList;
 387 
 388         private static final CipherSuiteList supportedCipherSuiteList;
 389         private static final CipherSuiteList serverDefaultCipherSuiteList;
 390 
 391         static {
 392             if (SunJSSE.isFIPS()) {
 393                 supportedProtocolList = new ProtocolList(new String[] {
 394                     ProtocolVersion.TLS10.name,
 395                     ProtocolVersion.TLS11.name,
 396                     ProtocolVersion.TLS12.name
 397                 });
 398 
 399                 serverDefaultProtocolList = new ProtocolList(
 400                         getAvailableProtocols(new ProtocolVersion[] {
 401                     ProtocolVersion.TLS10,
 402                     ProtocolVersion.TLS11,
 403                     ProtocolVersion.TLS12
 404                 }));
 405             } else {
 406                 supportedProtocolList = new ProtocolList(new String[] {
 407                     ProtocolVersion.SSL20Hello.name,
 408                     ProtocolVersion.SSL30.name,
 409                     ProtocolVersion.TLS10.name,
 410                     ProtocolVersion.TLS11.name,
 411                     ProtocolVersion.TLS12.name
 412                 });
 413 
 414                 serverDefaultProtocolList = new ProtocolList(
 415                         getAvailableProtocols(new ProtocolVersion[] {
 416                     ProtocolVersion.SSL20Hello,
 417                     ProtocolVersion.SSL30,
 418                     ProtocolVersion.TLS10,
 419                     ProtocolVersion.TLS11,
 420                     ProtocolVersion.TLS12
 421                 }));
 422             }
 423 
 424             supportedCipherSuiteList = getApplicableCipherSuiteList(
 425                     supportedProtocolList, false);          // all supported
 426             serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
 427                     serverDefaultProtocolList, true);       // enabled only
 428         }
 429 
 430         @Override
 431         ProtocolList getSuportedProtocolList() {
 432             return supportedProtocolList;
 433         }
 434 
 435         @Override
 436         CipherSuiteList getSupportedCipherSuiteList() {
 437             return supportedCipherSuiteList;
 438         }
 439 
 440         @Override
 441         ProtocolList getServerDefaultProtocolList() {
 442             return serverDefaultProtocolList;
 443         }
 444 
 445         @Override
 446         CipherSuiteList getServerDefaultCipherSuiteList() {
 447             return serverDefaultCipherSuiteList;
 448         }
 449 
 450         @Override
 451         SSLEngine createSSLEngineImpl() {
 452             return new SSLEngineImpl(this);
 453         }
 454 
 455         @Override
 456         SSLEngine createSSLEngineImpl(String host, int port) {
 457             return new SSLEngineImpl(this, host, port);
 458         }
 459     }
 460 
 461     /*
 462      * The SSLContext implementation for SSLv3 and TLS10 algorithm
 463      *
 464      * @see SSLContext
 465      */
 466     public static final class TLS10Context extends AbstractTLSContext {
 467         private static final ProtocolList clientDefaultProtocolList;
 468         private static final CipherSuiteList clientDefaultCipherSuiteList;
 469 
 470         static {
 471             if (SunJSSE.isFIPS()) {
 472                 clientDefaultProtocolList = new ProtocolList(
 473                         getAvailableProtocols(new ProtocolVersion[] {
 474                     ProtocolVersion.TLS10
 475                 }));
 476             } else {
 477                 clientDefaultProtocolList = new ProtocolList(
 478                         getAvailableProtocols(new ProtocolVersion[] {
 479                     ProtocolVersion.SSL30,
 480                     ProtocolVersion.TLS10
 481                 }));
 482             }
 483 
 484             clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
 485                     clientDefaultProtocolList, true);       // enabled only
 486         }
 487 
 488         @Override
 489         ProtocolList getClientDefaultProtocolList() {
 490             return clientDefaultProtocolList;
 491         }
 492 
 493         @Override
 494         CipherSuiteList getClientDefaultCipherSuiteList() {
 495             return clientDefaultCipherSuiteList;
 496         }
 497     }
 498 
 499     /*
 500      * The SSLContext implementation for TLS11 algorithm
 501      *
 502      * @see SSLContext
 503      */
 504     public static final class TLS11Context extends AbstractTLSContext {
 505         private static final ProtocolList clientDefaultProtocolList;
 506         private static final CipherSuiteList clientDefaultCipherSuiteList;
 507 
 508         static {
 509             if (SunJSSE.isFIPS()) {
 510                 clientDefaultProtocolList = new ProtocolList(
 511                         getAvailableProtocols(new ProtocolVersion[] {
 512                     ProtocolVersion.TLS10,
 513                     ProtocolVersion.TLS11
 514                 }));
 515             } else {
 516                 clientDefaultProtocolList = new ProtocolList(
 517                         getAvailableProtocols(new ProtocolVersion[] {
 518                     ProtocolVersion.SSL30,
 519                     ProtocolVersion.TLS10,
 520                     ProtocolVersion.TLS11
 521                 }));
 522             }
 523 
 524             clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
 525                     clientDefaultProtocolList, true);       // enabled only
 526         }
 527 
 528         @Override
 529         ProtocolList getClientDefaultProtocolList() {
 530             return clientDefaultProtocolList;
 531         }
 532 
 533         @Override
 534         CipherSuiteList getClientDefaultCipherSuiteList() {
 535             return clientDefaultCipherSuiteList;
 536         }
 537     }
 538 
 539     /*
 540      * The SSLContext implementation for TLS12 algorithm
 541      *
 542      * @see SSLContext
 543      */
 544     public static final class TLS12Context extends AbstractTLSContext {
 545         private static final ProtocolList clientDefaultProtocolList;
 546         private static final CipherSuiteList clientDefaultCipherSuiteList;
 547 
 548         static {
 549             if (SunJSSE.isFIPS()) {
 550                 clientDefaultProtocolList = new ProtocolList(
 551                         getAvailableProtocols(new ProtocolVersion[] {
 552                     ProtocolVersion.TLS10,
 553                     ProtocolVersion.TLS11,
 554                     ProtocolVersion.TLS12
 555                 }));
 556             } else {
 557                 clientDefaultProtocolList = new ProtocolList(
 558                         getAvailableProtocols(new ProtocolVersion[] {
 559                     ProtocolVersion.SSL30,
 560                     ProtocolVersion.TLS10,
 561                     ProtocolVersion.TLS11,
 562                     ProtocolVersion.TLS12
 563                 }));
 564             }
 565 
 566             clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
 567                     clientDefaultProtocolList, true);       // enabled only
 568         }
 569 
 570         @Override
 571         ProtocolList getClientDefaultProtocolList() {
 572             return clientDefaultProtocolList;
 573         }
 574 
 575         @Override
 576         CipherSuiteList getClientDefaultCipherSuiteList() {
 577             return clientDefaultCipherSuiteList;
 578         }
 579     }
 580 
 581     /*
 582      * The interface for the customized SSL/(D)TLS SSLContext.
 583      *
 584      * @see SSLContext
 585      */
 586     private static class CustomizedSSLProtocols {
 587         private static final String PROPERTY_NAME = "jdk.tls.client.protocols";
 588         static IllegalArgumentException reservedException = null;
 589         static ArrayList<ProtocolVersion>
 590                 customizedProtocols = new ArrayList<>();
 591 
 592         // Don't want a java.lang.LinkageError for illegal system property.
 593         //
 594         // Please don't throw exception in this static block.  Otherwise,
 595         // java.lang.LinkageError may be thrown during the instantiation of
 596         // the provider service. Instead, please handle the initialization
 597         // exception in the caller's constructor.
 598         static {
 599             String property = AccessController.doPrivileged(
 600                     new GetPropertyAction(PROPERTY_NAME));
 601             if (property != null && property.length() != 0) {
 602                 // remove double quote marks from beginning/end of the property
 603                 if (property.length() > 1 && property.charAt(0) == '"' &&
 604                         property.charAt(property.length() - 1) == '"') {
 605                     property = property.substring(1, property.length() - 1);
 606                 }
 607             }
 608 
 609             if (property != null && property.length() != 0) {
 610                 String[] protocols = property.split(",");
 611                 for (int i = 0; i < protocols.length; i++) {
 612                     protocols[i] = protocols[i].trim();
 613                     // Is it a supported protocol name?
 614                     try {
 615                         ProtocolVersion pro =
 616                                 ProtocolVersion.valueOf(protocols[i]);
 617 
 618                         if (SunJSSE.isFIPS() &&
 619                                 ((pro.v == ProtocolVersion.SSL30.v) ||
 620                                         (pro.v == ProtocolVersion.SSL20Hello.v))) {
 621                             reservedException = new IllegalArgumentException(
 622                                     PROPERTY_NAME + ": " + pro +
 623                                             " is not FIPS compliant");
 624 
 625                             break;
 626                         }
 627 
 628                         // ignore duplicated protocols
 629                         if (!customizedProtocols.contains(pro)) {
 630                             customizedProtocols.add(pro);
 631                         }
 632                     } catch (IllegalArgumentException iae) {
 633                         reservedException = new IllegalArgumentException(
 634                                 PROPERTY_NAME + ": " + protocols[i] +
 635                                         " is not a standard SSL protocol name", iae);
 636                     }
 637                 }
 638             }
 639         }
 640     }
 641 
 642     /*
 643      * The SSLContext implementation for customized TLS protocols
 644      *
 645      * @see SSLContext
 646      */
 647     private static class CustomizedTLSContext extends AbstractTLSContext {
 648 
 649         private static final ProtocolList clientDefaultProtocolList;
 650         private static final CipherSuiteList clientDefaultCipherSuiteList;
 651 
 652         private static IllegalArgumentException reservedException = null;
 653 
 654         // Don't want a java.lang.LinkageError for illegal system property.
 655         //
 656         // Please don't throw exception in this static block.  Otherwise,
 657         // java.lang.LinkageError may be thrown during the instantiation of
 658         // the provider service. Instead, let's handle the initialization
 659         // exception in constructor.
 660         static {
 661             reservedException = CustomizedSSLProtocols.reservedException;
 662             if (reservedException == null) {
 663                 ArrayList<ProtocolVersion>
 664                         customizedTLSProtocols = new ArrayList<>();
 665                 for (ProtocolVersion protocol :
 666                         CustomizedSSLProtocols.customizedProtocols) {
 667                         customizedTLSProtocols.add(protocol);
 668                 }
 669 
 670                 // candidates for available protocols
 671                 ProtocolVersion[] candidates;
 672                 if (customizedTLSProtocols.isEmpty()) {
 673                     // Use the default enabled client protocols if no
 674                     // customized TLS protocols.
 675                     if (SunJSSE.isFIPS()) {
 676                         candidates = new ProtocolVersion[] {
 677                                 ProtocolVersion.TLS10,
 678                                 ProtocolVersion.TLS11,
 679                                 ProtocolVersion.TLS12
 680                         };
 681                     } else {
 682                         candidates = new ProtocolVersion[] {
 683                                 ProtocolVersion.SSL30,
 684                                 ProtocolVersion.TLS10,
 685                                 ProtocolVersion.TLS11,
 686                                 ProtocolVersion.TLS12
 687                         };
 688                     }
 689                 } else {
 690                     // Use the customized TLS protocols.
 691                     candidates =
 692                             new ProtocolVersion[customizedTLSProtocols.size()];
 693                     candidates = customizedTLSProtocols.toArray(candidates);
 694                 }
 695 
 696                 clientDefaultProtocolList = new ProtocolList(
 697                         getAvailableProtocols(candidates));
 698                 clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
 699                         clientDefaultProtocolList, true);   // enabled only
 700             } else {
 701                 clientDefaultProtocolList = null;       // unlikely to be used
 702                 clientDefaultCipherSuiteList = null;    // unlikely to be used
 703             }
 704         }
 705 
 706         protected CustomizedTLSContext() {
 707             if (reservedException != null) {
 708                 throw reservedException;
 709             }
 710         }
 711 
 712         @Override
 713         ProtocolList getClientDefaultProtocolList() {
 714             return clientDefaultProtocolList;
 715         }
 716 
 717         @Override
 718         CipherSuiteList getClientDefaultCipherSuiteList() {
 719             return clientDefaultCipherSuiteList;
 720         }
 721     }
 722 
 723     /*
 724      * The SSLContext implementation for default "TLS" algorithm
 725      *
 726      * @see SSLContext
 727      */
 728     public static final class TLSContext extends CustomizedTLSContext {
 729         // use the default constructor and methods
 730     }
 731 
 732     // lazy initialization holder class idiom for static default parameters
 733     //
 734     // See Effective Java Second Edition: Item 71.
 735     private static final class DefaultManagersHolder {
 736         private static final String NONE = "NONE";
 737         private static final String P11KEYSTORE = "PKCS11";
 738 
 739         private static final TrustManager[] trustManagers;
 740         private static final KeyManager[] keyManagers;
 741 
 742         static Exception reservedException = null;
 743 
 744         static {
 745             TrustManager[] tmMediator;
 746             try {
 747                 tmMediator = getTrustManagers();
 748             } catch (Exception e) {
 749                 reservedException = e;
 750                 tmMediator = new TrustManager[0];
 751             }
 752             trustManagers = tmMediator;
 753 
 754             if (reservedException == null) {
 755                 KeyManager[] kmMediator;
 756                 try {
 757                     kmMediator = getKeyManagers();
 758                 } catch (Exception e) {
 759                     reservedException = e;
 760                     kmMediator = new KeyManager[0];
 761                 }
 762                 keyManagers = kmMediator;
 763             } else {
 764                 keyManagers = new KeyManager[0];
 765             }
 766         }
 767 
 768         private static TrustManager[] getTrustManagers() throws Exception {
 769             KeyStore ks =
 770                 TrustManagerFactoryImpl.getCacertsKeyStore("defaultctx");
 771 
 772             TrustManagerFactory tmf = TrustManagerFactory.getInstance(
 773                 TrustManagerFactory.getDefaultAlgorithm());
 774             tmf.init(ks);
 775             return tmf.getTrustManagers();
 776         }
 777 
 778         private static KeyManager[] getKeyManagers() throws Exception {
 779 
 780             final Map<String,String> props = new HashMap<>();
 781             AccessController.doPrivileged(
 782                         new PrivilegedExceptionAction<Object>() {
 783                 @Override
 784                 public Object run() throws Exception {
 785                     props.put("keyStore",  System.getProperty(
 786                                 "javax.net.ssl.keyStore", ""));
 787                     props.put("keyStoreType", System.getProperty(
 788                                 "javax.net.ssl.keyStoreType",
 789                                 KeyStore.getDefaultType()));
 790                     props.put("keyStoreProvider", System.getProperty(
 791                                 "javax.net.ssl.keyStoreProvider", ""));
 792                     props.put("keyStorePasswd", System.getProperty(
 793                                 "javax.net.ssl.keyStorePassword", ""));
 794                     return null;
 795                 }
 796             });
 797 
 798             final String defaultKeyStore = props.get("keyStore");
 799             String defaultKeyStoreType = props.get("keyStoreType");
 800             String defaultKeyStoreProvider = props.get("keyStoreProvider");
 801             if (debug != null && Debug.isOn("defaultctx")) {
 802                 System.out.println("keyStore is : " + defaultKeyStore);
 803                 System.out.println("keyStore type is : " +
 804                                         defaultKeyStoreType);
 805                 System.out.println("keyStore provider is : " +
 806                                         defaultKeyStoreProvider);
 807             }
 808 
 809             if (P11KEYSTORE.equals(defaultKeyStoreType) &&
 810                     !NONE.equals(defaultKeyStore)) {
 811                 throw new IllegalArgumentException("if keyStoreType is "
 812                     + P11KEYSTORE + ", then keyStore must be " + NONE);
 813             }
 814 
 815             FileInputStream fs = null;
 816             KeyStore ks = null;
 817             char[] passwd = null;
 818             try {
 819                 if (defaultKeyStore.length() != 0 &&
 820                         !NONE.equals(defaultKeyStore)) {
 821                     fs = AccessController.doPrivileged(
 822                             new PrivilegedExceptionAction<FileInputStream>() {
 823                         @Override
 824                         public FileInputStream run() throws Exception {
 825                             return new FileInputStream(defaultKeyStore);
 826                         }
 827                     });
 828                 }
 829 
 830                 String defaultKeyStorePassword = props.get("keyStorePasswd");
 831                 if (defaultKeyStorePassword.length() != 0) {
 832                     passwd = defaultKeyStorePassword.toCharArray();
 833                 }
 834 
 835                 /**
 836                  * Try to initialize key store.
 837                  */
 838                 if ((defaultKeyStoreType.length()) != 0) {
 839                     if (debug != null && Debug.isOn("defaultctx")) {
 840                         System.out.println("init keystore");
 841                     }
 842                     if (defaultKeyStoreProvider.length() == 0) {
 843                         ks = KeyStore.getInstance(defaultKeyStoreType);
 844                     } else {
 845                         ks = KeyStore.getInstance(defaultKeyStoreType,
 846                                             defaultKeyStoreProvider);
 847                     }
 848 
 849                     // if defaultKeyStore is NONE, fs will be null
 850                     ks.load(fs, passwd);
 851                 }
 852             } finally {
 853                 if (fs != null) {
 854                     fs.close();
 855                     fs = null;
 856                 }
 857             }
 858 
 859             /*
 860              * Try to initialize key manager.
 861              */
 862             if (debug != null && Debug.isOn("defaultctx")) {
 863                 System.out.println("init keymanager of type " +
 864                     KeyManagerFactory.getDefaultAlgorithm());
 865             }
 866             KeyManagerFactory kmf = KeyManagerFactory.getInstance(
 867                 KeyManagerFactory.getDefaultAlgorithm());
 868 
 869             if (P11KEYSTORE.equals(defaultKeyStoreType)) {
 870                 kmf.init(ks, null); // do not pass key passwd if using token
 871             } else {
 872                 kmf.init(ks, passwd);
 873             }
 874 
 875             return kmf.getKeyManagers();
 876         }
 877     }
 878 
 879     // lazy initialization holder class idiom for static default parameters
 880     //
 881     // See Effective Java Second Edition: Item 71.
 882     private static final class DefaultSSLContextHolder {
 883 
 884         private static final SSLContextImpl sslContext;
 885         static Exception reservedException = null;
 886 
 887         static {
 888             SSLContextImpl mediator = null;
 889             if (DefaultManagersHolder.reservedException != null) {
 890                 reservedException = DefaultManagersHolder.reservedException;
 891             } else {
 892                 try {
 893                     mediator = new DefaultSSLContext();
 894                 } catch (Exception e) {
 895                     reservedException = e;
 896                 }
 897             }
 898 
 899             sslContext = mediator;
 900         }
 901     }
 902 
 903     /*
 904      * The SSLContext implementation for default "Default" algorithm
 905      *
 906      * @see SSLContext
 907      */
 908     public static final class DefaultSSLContext extends CustomizedTLSContext {
 909 
 910         // public constructor for SSLContext.getInstance("Default")
 911         public DefaultSSLContext() throws Exception {
 912             if (DefaultManagersHolder.reservedException != null) {
 913                 throw DefaultManagersHolder.reservedException;
 914             }
 915 
 916             try {
 917                 super.engineInit(DefaultManagersHolder.keyManagers,
 918                         DefaultManagersHolder.trustManagers, null);
 919             } catch (Exception e) {
 920                 if (debug != null && Debug.isOn("defaultctx")) {
 921                     System.out.println("default context init failed: " + e);
 922                 }
 923                 throw e;
 924             }
 925         }
 926 
 927         @Override
 928         protected void engineInit(KeyManager[] km, TrustManager[] tm,
 929                                   SecureRandom sr) throws KeyManagementException {
 930             throw new KeyManagementException
 931                     ("Default SSLContext is initialized automatically");
 932         }
 933 
 934         static SSLContextImpl getDefaultImpl() throws Exception {
 935             if (DefaultSSLContextHolder.reservedException != null) {
 936                 throw DefaultSSLContextHolder.reservedException;
 937             }
 938 
 939             return DefaultSSLContextHolder.sslContext;
 940         }
 941     }
 942 
 943 
 944 }
 945 
 946 
 947 final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager
 948             implements X509TrustManager {
 949 
 950     // the delegated trust manager
 951     private final X509TrustManager tm;
 952 
 953     AbstractTrustManagerWrapper(X509TrustManager tm) {
 954         this.tm = tm;
 955     }
 956 
 957     @Override
 958     public void checkClientTrusted(X509Certificate[] chain, String authType)
 959         throws CertificateException {
 960         tm.checkClientTrusted(chain, authType);
 961     }
 962 
 963     @Override
 964     public void checkServerTrusted(X509Certificate[] chain, String authType)
 965         throws CertificateException {
 966         tm.checkServerTrusted(chain, authType);
 967     }
 968 
 969     @Override
 970     public X509Certificate[] getAcceptedIssuers() {
 971         return tm.getAcceptedIssuers();
 972     }
 973 
 974     @Override
 975     public void checkClientTrusted(X509Certificate[] chain, String authType,
 976                 Socket socket) throws CertificateException {
 977         tm.checkClientTrusted(chain, authType);
 978         checkAdditionalTrust(chain, authType, socket, true);
 979     }
 980 
 981     @Override
 982     public void checkServerTrusted(X509Certificate[] chain, String authType,
 983             Socket socket) throws CertificateException {
 984         tm.checkServerTrusted(chain, authType);
 985         checkAdditionalTrust(chain, authType, socket, false);
 986     }
 987 
 988     @Override
 989     public void checkClientTrusted(X509Certificate[] chain, String authType,
 990             SSLEngine engine) throws CertificateException {
 991         tm.checkClientTrusted(chain, authType);
 992         checkAdditionalTrust(chain, authType, engine, true);
 993     }
 994 
 995     @Override
 996     public void checkServerTrusted(X509Certificate[] chain, String authType,
 997             SSLEngine engine) throws CertificateException {
 998         tm.checkServerTrusted(chain, authType);
 999         checkAdditionalTrust(chain, authType, engine, false);
1000     }
1001 
1002     private void checkAdditionalTrust(X509Certificate[] chain, String authType,
1003                 Socket socket, boolean isClient) throws CertificateException {
1004         if (socket != null && socket.isConnected() &&
1005                                     socket instanceof SSLSocket) {
1006 
1007             SSLSocket sslSocket = (SSLSocket)socket;
1008             SSLSession session = sslSocket.getHandshakeSession();
1009             if (session == null) {
1010                 throw new CertificateException("No handshake session");
1011             }
1012 
1013             // check endpoint identity
1014             String identityAlg = sslSocket.getSSLParameters().
1015                                         getEndpointIdentificationAlgorithm();
1016             if (identityAlg != null && identityAlg.length() != 0) {
1017                 String hostname = session.getPeerHost();
1018                 X509TrustManagerImpl.checkIdentity(
1019                                     hostname, chain[0], identityAlg);
1020             }
1021 
1022             // try the best to check the algorithm constraints
1023             ProtocolVersion protocolVersion =
1024                 ProtocolVersion.valueOf(session.getProtocol());
1025             AlgorithmConstraints constraints = null;
1026             if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
1027                 if (session instanceof ExtendedSSLSession) {
1028                     ExtendedSSLSession extSession =
1029                                     (ExtendedSSLSession)session;
1030                     String[] peerSupportedSignAlgs =
1031                             extSession.getLocalSupportedSignatureAlgorithms();
1032 
1033                     constraints = new SSLAlgorithmConstraints(
1034                                     sslSocket, peerSupportedSignAlgs, true);
1035                 } else {
1036                     constraints =
1037                             new SSLAlgorithmConstraints(sslSocket, true);
1038                 }
1039             } else {
1040                 constraints = new SSLAlgorithmConstraints(sslSocket, true);
1041             }
1042 
1043             checkAlgorithmConstraints(chain, constraints);
1044         }
1045     }
1046 
1047     private void checkAdditionalTrust(X509Certificate[] chain, String authType,
1048             SSLEngine engine, boolean isClient) throws CertificateException {
1049         if (engine != null) {
1050             SSLSession session = engine.getHandshakeSession();
1051             if (session == null) {
1052                 throw new CertificateException("No handshake session");
1053             }
1054 
1055             // check endpoint identity
1056             String identityAlg = engine.getSSLParameters().
1057                                         getEndpointIdentificationAlgorithm();
1058             if (identityAlg != null && identityAlg.length() != 0) {
1059                 String hostname = session.getPeerHost();
1060                 X509TrustManagerImpl.checkIdentity(
1061                                     hostname, chain[0], identityAlg);
1062             }
1063 
1064             // try the best to check the algorithm constraints
1065             ProtocolVersion protocolVersion =
1066                 ProtocolVersion.valueOf(session.getProtocol());
1067             AlgorithmConstraints constraints = null;
1068             if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
1069                 if (session instanceof ExtendedSSLSession) {
1070                     ExtendedSSLSession extSession =
1071                                     (ExtendedSSLSession)session;
1072                     String[] peerSupportedSignAlgs =
1073                             extSession.getLocalSupportedSignatureAlgorithms();
1074 
1075                     constraints = new SSLAlgorithmConstraints(
1076                                     engine, peerSupportedSignAlgs, true);
1077                 } else {
1078                     constraints =
1079                             new SSLAlgorithmConstraints(engine, true);
1080                 }
1081             } else {
1082                 constraints = new SSLAlgorithmConstraints(engine, true);
1083             }
1084 
1085             checkAlgorithmConstraints(chain, constraints);
1086         }
1087     }
1088 
1089     private void checkAlgorithmConstraints(X509Certificate[] chain,
1090             AlgorithmConstraints constraints) throws CertificateException {
1091 
1092         try {
1093             // Does the certificate chain end with a trusted certificate?
1094             int checkedLength = chain.length - 1;
1095 
1096             Collection<X509Certificate> trustedCerts = new HashSet<>();
1097             X509Certificate[] certs = tm.getAcceptedIssuers();
1098             if ((certs != null) && (certs.length > 0)){
1099                 Collections.addAll(trustedCerts, certs);
1100             }
1101 
1102             if (trustedCerts.contains(chain[checkedLength])) {
1103                     checkedLength--;
1104             }
1105 
1106             // A forward checker, need to check from trust to target
1107             if (checkedLength >= 0) {
1108                 AlgorithmChecker checker = new AlgorithmChecker(constraints);
1109                 checker.init(false);
1110                 for (int i = checkedLength; i >= 0; i--) {
1111                     Certificate cert = chain[i];
1112                     // We don't care about the unresolved critical extensions.
1113                     checker.check(cert, Collections.<String>emptySet());
1114                 }
1115             }
1116         } catch (CertPathValidatorException cpve) {
1117             throw new CertificateException(
1118                 "Certificates does not conform to algorithm constraints");
1119         }
1120     }
1121 }
1122 
1123 // Dummy X509TrustManager implementation, rejects all peer certificates.
1124 // Used if the application did not specify a proper X509TrustManager.
1125 final class DummyX509TrustManager extends X509ExtendedTrustManager
1126             implements X509TrustManager {
1127 
1128     static final X509TrustManager INSTANCE = new DummyX509TrustManager();
1129 
1130     private DummyX509TrustManager() {
1131         // empty
1132     }
1133 
1134     /*
1135      * Given the partial or complete certificate chain
1136      * provided by the peer, build a certificate path
1137      * to a trusted root and return if it can be
1138      * validated and is trusted for client SSL authentication.
1139      * If not, it throws an exception.
1140      */
1141     @Override
1142     public void checkClientTrusted(X509Certificate[] chain, String authType)
1143         throws CertificateException {
1144         throw new CertificateException(
1145             "No X509TrustManager implementation avaiable");
1146     }
1147 
1148     /*
1149      * Given the partial or complete certificate chain
1150      * provided by the peer, build a certificate path
1151      * to a trusted root and return if it can be
1152      * validated and is trusted for server SSL authentication.
1153      * If not, it throws an exception.
1154      */
1155     @Override
1156     public void checkServerTrusted(X509Certificate[] chain, String authType)
1157         throws CertificateException {
1158         throw new CertificateException(
1159             "No X509TrustManager implementation available");
1160     }
1161 
1162     /*
1163      * Return an array of issuer certificates which are trusted
1164      * for authenticating peers.
1165      */
1166     @Override
1167     public X509Certificate[] getAcceptedIssuers() {
1168         return new X509Certificate[0];
1169     }
1170 
1171     @Override
1172     public void checkClientTrusted(X509Certificate[] chain, String authType,
1173                 Socket socket) throws CertificateException {
1174         throw new CertificateException(
1175             "No X509TrustManager implementation available");
1176     }
1177 
1178     @Override
1179     public void checkServerTrusted(X509Certificate[] chain, String authType,
1180             Socket socket) throws CertificateException {
1181         throw new CertificateException(
1182             "No X509TrustManager implementation available");
1183     }
1184 
1185     @Override
1186     public void checkClientTrusted(X509Certificate[] chain, String authType,
1187             SSLEngine engine) throws CertificateException {
1188         throw new CertificateException(
1189             "No X509TrustManager implementation available");
1190     }
1191 
1192     @Override
1193     public void checkServerTrusted(X509Certificate[] chain, String authType,
1194             SSLEngine engine) throws CertificateException {
1195         throw new CertificateException(
1196             "No X509TrustManager implementation available");
1197     }
1198 }
1199 
1200 /*
1201  * A wrapper class to turn a X509KeyManager into an X509ExtendedKeyManager
1202  */
1203 final class AbstractKeyManagerWrapper extends X509ExtendedKeyManager {
1204 
1205     private final X509KeyManager km;
1206 
1207     AbstractKeyManagerWrapper(X509KeyManager km) {
1208         this.km = km;
1209     }
1210 
1211     @Override
1212     public String[] getClientAliases(String keyType, Principal[] issuers) {
1213         return km.getClientAliases(keyType, issuers);
1214     }
1215 
1216     @Override
1217     public String chooseClientAlias(String[] keyType, Principal[] issuers,
1218             Socket socket) {
1219         return km.chooseClientAlias(keyType, issuers, socket);
1220     }
1221 
1222     @Override
1223     public String[] getServerAliases(String keyType, Principal[] issuers) {
1224         return km.getServerAliases(keyType, issuers);
1225     }
1226 
1227     @Override
1228     public String chooseServerAlias(String keyType, Principal[] issuers,
1229             Socket socket) {
1230         return km.chooseServerAlias(keyType, issuers, socket);
1231     }
1232 
1233     @Override
1234     public X509Certificate[] getCertificateChain(String alias) {
1235         return km.getCertificateChain(alias);
1236     }
1237 
1238     @Override
1239     public PrivateKey getPrivateKey(String alias) {
1240         return km.getPrivateKey(alias);
1241     }
1242 
1243     // Inherit chooseEngineClientAlias() and chooseEngineServerAlias() from
1244     // X509ExtendedKeymanager. It defines them to return null;
1245 }
1246 
1247 
1248 // Dummy X509KeyManager implementation, never returns any certificates/keys.
1249 // Used if the application did not specify a proper X509TrustManager.
1250 final class DummyX509KeyManager extends X509ExtendedKeyManager {
1251 
1252     static final X509ExtendedKeyManager INSTANCE = new DummyX509KeyManager();
1253 
1254     private DummyX509KeyManager() {
1255         // empty
1256     }
1257 
1258     /*
1259      * Get the matching aliases for authenticating the client side of a secure
1260      * socket given the public key type and the list of
1261      * certificate issuer authorities recognized by the peer (if any).
1262      */
1263     @Override
1264     public String[] getClientAliases(String keyType, Principal[] issuers) {
1265         return null;
1266     }
1267 
1268     /*
1269      * Choose an alias to authenticate the client side of a secure
1270      * socket given the public key type and the list of
1271      * certificate issuer authorities recognized by the peer (if any).
1272      */
1273     @Override
1274     public String chooseClientAlias(String[] keyTypes, Principal[] issuers,
1275             Socket socket) {
1276         return null;
1277     }
1278 
1279     /*
1280      * Choose an alias to authenticate the client side of an
1281      * engine given the public key type and the list of
1282      * certificate issuer authorities recognized by the peer (if any).
1283      */
1284     @Override
1285     public String chooseEngineClientAlias(
1286             String[] keyTypes, Principal[] issuers, SSLEngine engine) {
1287         return null;
1288     }
1289 
1290     /*
1291      * Get the matching aliases for authenticating the server side of a secure
1292      * socket given the public key type and the list of
1293      * certificate issuer authorities recognized by the peer (if any).
1294      */
1295     @Override
1296     public String[] getServerAliases(String keyType, Principal[] issuers) {
1297         return null;
1298     }
1299 
1300     /*
1301      * Choose an alias to authenticate the server side of a secure
1302      * socket given the public key type and the list of
1303      * certificate issuer authorities recognized by the peer (if any).
1304      */
1305     @Override
1306     public String chooseServerAlias(String keyType, Principal[] issuers,
1307             Socket socket) {
1308         return null;
1309     }
1310 
1311     /*
1312      * Choose an alias to authenticate the server side of an engine
1313      * given the public key type and the list of
1314      * certificate issuer authorities recognized by the peer (if any).
1315      */
1316     @Override
1317     public String chooseEngineServerAlias(
1318             String keyType, Principal[] issuers, SSLEngine engine) {
1319         return null;
1320     }
1321 
1322     /**
1323      * Returns the certificate chain associated with the given alias.
1324      *
1325      * @param alias the alias name
1326      *
1327      * @return the certificate chain (ordered with the user's certificate first
1328      * and the root certificate authority last)
1329      */
1330     @Override
1331     public X509Certificate[] getCertificateChain(String alias) {
1332         return null;
1333     }
1334 
1335     /*
1336      * Returns the key associated with the given alias, using the given
1337      * password to recover it.
1338      *
1339      * @param alias the alias name
1340      *
1341      * @return the requested key
1342      */
1343     @Override
1344     public PrivateKey getPrivateKey(String alias) {
1345         return null;
1346     }
1347 }