1 /*
   2  * Copyright (c) 2001, 2013, 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.util.*;
  29 import java.math.BigInteger;
  30 
  31 import java.security.*;
  32 import java.security.interfaces.RSAPublicKey;
  33 import java.security.spec.*;
  34 
  35 import javax.crypto.*;
  36 
  37 // explicit import to override the Provider class in this package
  38 import java.security.Provider;
  39 
  40 // need internal Sun classes for FIPS tricks
  41 import sun.security.jca.Providers;
  42 import sun.security.jca.ProviderList;
  43 
  44 import sun.security.util.ECUtil;
  45 
  46 import static sun.security.ssl.SunJSSE.cryptoProvider;
  47 
  48 /**
  49  * This class contains a few static methods for interaction with the JCA/JCE
  50  * to obtain implementations, etc.
  51  *
  52  * @author  Andreas Sterbenz
  53  */
  54 final class JsseJce {
  55 
  56     private final static ProviderList fipsProviderList;
  57 
  58     // Flag indicating whether EC crypto is available.
  59     // If null, then we have not checked yet.
  60     // If yes, then all the EC based crypto we need is available.
  61     private static Boolean ecAvailable;
  62 
  63     // Flag indicating whether Kerberos crypto is available.
  64     // If true, then all the Kerberos-based crypto we need is available.
  65     private final static boolean kerberosAvailable;
  66     static {
  67         boolean temp;
  68         try {
  69             AccessController.doPrivileged(
  70                 new PrivilegedExceptionAction<Void>() {
  71                     @Override
  72                     public Void run() throws Exception {
  73                         // Test for Kerberos using the bootstrap class loader
  74                         Class.forName("sun.security.krb5.PrincipalName", true,
  75                                 null);
  76                         return null;
  77                     }
  78                 });
  79             temp = true;
  80 
  81         } catch (Exception e) {
  82             temp = false;
  83         }
  84         kerberosAvailable = temp;
  85     }
  86 
  87     static {
  88         // force FIPS flag initialization
  89         // Because isFIPS() is synchronized and cryptoProvider is not modified
  90         // after it completes, this also eliminates the need for any further
  91         // synchronization when accessing cryptoProvider
  92         if (SunJSSE.isFIPS() == false) {
  93             fipsProviderList = null;
  94         } else {
  95             // Setup a ProviderList that can be used by the trust manager
  96             // during certificate chain validation. All the crypto must be
  97             // from the FIPS provider, but we also allow the required
  98             // certificate related services from the SUN provider.
  99             Provider sun = Security.getProvider("SUN");
 100             if (sun == null) {
 101                 throw new RuntimeException
 102                     ("FIPS mode: SUN provider must be installed");
 103             }
 104             Provider sunCerts = new SunCertificates(sun);
 105             fipsProviderList = ProviderList.newList(cryptoProvider, sunCerts);
 106         }
 107     }
 108 
 109     private static final class SunCertificates extends Provider {
 110         private static final long serialVersionUID = -3284138292032213752L;
 111 
 112         SunCertificates(final Provider p) {
 113             super("SunCertificates", 1.8d, "SunJSSE internal");
 114             AccessController.doPrivileged(new PrivilegedAction<Object>() {
 115                 @Override
 116                 public Object run() {
 117                     // copy certificate related services from the Sun provider
 118                     for (Map.Entry<Object,Object> entry : p.entrySet()) {
 119                         String key = (String)entry.getKey();
 120                         if (key.startsWith("CertPathValidator.")
 121                                 || key.startsWith("CertPathBuilder.")
 122                                 || key.startsWith("CertStore.")
 123                                 || key.startsWith("CertificateFactory.")) {
 124                             put(key, entry.getValue());
 125                         }
 126                     }
 127                     return null;
 128                 }
 129             });
 130         }
 131     }
 132 
 133     /**
 134      * JCE transformation string for RSA with PKCS#1 v1.5 padding.
 135      * Can be used for encryption, decryption, signing, verifying.
 136      */
 137     final static String CIPHER_RSA_PKCS1 = "RSA/ECB/PKCS1Padding";
 138     /**
 139      * JCE transformation string for the stream cipher RC4.
 140      */
 141     final static String CIPHER_RC4 = "RC4";
 142     /**
 143      * JCE transformation string for DES in CBC mode without padding.
 144      */
 145     final static String CIPHER_DES = "DES/CBC/NoPadding";
 146     /**
 147      * JCE transformation string for (3-key) Triple DES in CBC mode
 148      * without padding.
 149      */
 150     final static String CIPHER_3DES = "DESede/CBC/NoPadding";
 151     /**
 152      * JCE transformation string for AES in CBC mode
 153      * without padding.
 154      */
 155     final static String CIPHER_AES = "AES/CBC/NoPadding";
 156     /**
 157      * JCE transformation string for AES in GCM mode
 158      * without padding.
 159      */
 160     final static String CIPHER_AES_GCM = "AES/GCM/NoPadding";
 161     /**
 162      * JCA identifier string for DSA, i.e. a DSA with SHA-1.
 163      */
 164     final static String SIGNATURE_DSA = "DSA";
 165     /**
 166      * JCA identifier string for ECDSA, i.e. a ECDSA with SHA-1.
 167      */
 168     final static String SIGNATURE_ECDSA = "SHA1withECDSA";
 169     /**
 170      * JCA identifier string for Raw DSA, i.e. a DSA signature without
 171      * hashing where the application provides the SHA-1 hash of the data.
 172      * Note that the standard name is "NONEwithDSA" but we use "RawDSA"
 173      * for compatibility.
 174      */
 175     final static String SIGNATURE_RAWDSA = "RawDSA";
 176     /**
 177      * JCA identifier string for Raw ECDSA, i.e. a DSA signature without
 178      * hashing where the application provides the SHA-1 hash of the data.
 179      */
 180     final static String SIGNATURE_RAWECDSA = "NONEwithECDSA";
 181     /**
 182      * JCA identifier string for Raw RSA, i.e. a RSA PKCS#1 v1.5 signature
 183      * without hashing where the application provides the hash of the data.
 184      * Used for RSA client authentication with a 36 byte hash.
 185      */
 186     final static String SIGNATURE_RAWRSA = "NONEwithRSA";
 187     /**
 188      * JCA identifier string for the SSL/TLS style RSA Signature. I.e.
 189      * an signature using RSA with PKCS#1 v1.5 padding signing a
 190      * concatenation of an MD5 and SHA-1 digest.
 191      */
 192     final static String SIGNATURE_SSLRSA = "MD5andSHA1withRSA";
 193 
 194     private JsseJce() {
 195         // no instantiation of this class
 196     }
 197 
 198     synchronized static boolean isEcAvailable() {
 199         if (ecAvailable == null) {
 200             try {
 201                 JsseJce.getSignature(SIGNATURE_ECDSA);
 202                 JsseJce.getSignature(SIGNATURE_RAWECDSA);
 203                 JsseJce.getKeyAgreement("ECDH");
 204                 JsseJce.getKeyFactory("EC");
 205                 JsseJce.getKeyPairGenerator("EC");
 206                 ecAvailable = true;
 207             } catch (Exception e) {
 208                 ecAvailable = false;
 209             }
 210         }
 211         return ecAvailable;
 212     }
 213 
 214     synchronized static void clearEcAvailable() {
 215         ecAvailable = null;
 216     }
 217 
 218     static boolean isKerberosAvailable() {
 219         return kerberosAvailable;
 220     }
 221 
 222     /**
 223      * Return an JCE cipher implementation for the specified algorithm.
 224      */
 225     static Cipher getCipher(String transformation)
 226             throws NoSuchAlgorithmException {
 227         try {
 228             if (cryptoProvider == null) {
 229                 return Cipher.getInstance(transformation);
 230             } else {
 231                 return Cipher.getInstance(transformation, cryptoProvider);
 232             }
 233         } catch (NoSuchPaddingException e) {
 234             throw new NoSuchAlgorithmException(e);
 235         }
 236     }
 237 
 238     /**
 239      * Return an JCA signature implementation for the specified algorithm.
 240      * The algorithm string should be one of the constants defined
 241      * in this class.
 242      */
 243     static Signature getSignature(String algorithm)
 244             throws NoSuchAlgorithmException {
 245         if (cryptoProvider == null) {
 246             return Signature.getInstance(algorithm);
 247         } else {
 248             // reference equality
 249             if (algorithm == SIGNATURE_SSLRSA) {
 250                 // The SunPKCS11 provider currently does not support this
 251                 // special algorithm. We allow a fallback in this case because
 252                 // the SunJSSE implementation does the actual crypto using
 253                 // a NONEwithRSA signature obtained from the cryptoProvider.
 254                 if (cryptoProvider.getService("Signature", algorithm) == null) {
 255                     // Calling Signature.getInstance() and catching the
 256                     // exception would be cleaner, but exceptions are a little
 257                     // expensive. So we check directly via getService().
 258                     try {
 259                         return Signature.getInstance(algorithm, "SunJSSE");
 260                     } catch (NoSuchProviderException e) {
 261                         throw new NoSuchAlgorithmException(e);
 262                     }
 263                 }
 264             }
 265             return Signature.getInstance(algorithm, cryptoProvider);
 266         }
 267     }
 268 
 269     static KeyGenerator getKeyGenerator(String algorithm)
 270             throws NoSuchAlgorithmException {
 271         if (cryptoProvider == null) {
 272             return KeyGenerator.getInstance(algorithm);
 273         } else {
 274             return KeyGenerator.getInstance(algorithm, cryptoProvider);
 275         }
 276     }
 277 
 278     static KeyPairGenerator getKeyPairGenerator(String algorithm)
 279             throws NoSuchAlgorithmException {
 280         if (cryptoProvider == null) {
 281             return KeyPairGenerator.getInstance(algorithm);
 282         } else {
 283             return KeyPairGenerator.getInstance(algorithm, cryptoProvider);
 284         }
 285     }
 286 
 287     static KeyAgreement getKeyAgreement(String algorithm)
 288             throws NoSuchAlgorithmException {
 289         if (cryptoProvider == null) {
 290             return KeyAgreement.getInstance(algorithm);
 291         } else {
 292             return KeyAgreement.getInstance(algorithm, cryptoProvider);
 293         }
 294     }
 295 
 296     static Mac getMac(String algorithm)
 297             throws NoSuchAlgorithmException {
 298         if (cryptoProvider == null) {
 299             return Mac.getInstance(algorithm);
 300         } else {
 301             return Mac.getInstance(algorithm, cryptoProvider);
 302         }
 303     }
 304 
 305     static KeyFactory getKeyFactory(String algorithm)
 306             throws NoSuchAlgorithmException {
 307         if (cryptoProvider == null) {
 308             return KeyFactory.getInstance(algorithm);
 309         } else {
 310             return KeyFactory.getInstance(algorithm, cryptoProvider);
 311         }
 312     }
 313 
 314     static SecureRandom getSecureRandom() throws KeyManagementException {
 315         if (cryptoProvider == null) {
 316             return new SecureRandom();
 317         }
 318         // Try "PKCS11" first. If that is not supported, iterate through
 319         // the provider and return the first working implementation.
 320         try {
 321             return SecureRandom.getInstance("PKCS11", cryptoProvider);
 322         } catch (NoSuchAlgorithmException e) {
 323             // ignore
 324         }
 325         for (Provider.Service s : cryptoProvider.getServices()) {
 326             if (s.getType().equals("SecureRandom")) {
 327                 try {
 328                     return SecureRandom.getInstance(s.getAlgorithm(), cryptoProvider);
 329                 } catch (NoSuchAlgorithmException ee) {
 330                     // ignore
 331                 }
 332             }
 333         }
 334         throw new KeyManagementException("FIPS mode: no SecureRandom "
 335             + " implementation found in provider " + cryptoProvider.getName());
 336     }
 337 
 338     static MessageDigest getMD5() {
 339         return getMessageDigest("MD5");
 340     }
 341 
 342     static MessageDigest getSHA() {
 343         return getMessageDigest("SHA");
 344     }
 345 
 346     static MessageDigest getMessageDigest(String algorithm) {
 347         try {
 348             if (cryptoProvider == null) {
 349                 return MessageDigest.getInstance(algorithm);
 350             } else {
 351                 return MessageDigest.getInstance(algorithm, cryptoProvider);
 352             }
 353         } catch (NoSuchAlgorithmException e) {
 354             throw new RuntimeException
 355                         ("Algorithm " + algorithm + " not available", e);
 356         }
 357     }
 358 
 359     static int getRSAKeyLength(PublicKey key) {
 360         BigInteger modulus;
 361         if (key instanceof RSAPublicKey) {
 362             modulus = ((RSAPublicKey)key).getModulus();
 363         } else {
 364             RSAPublicKeySpec spec = getRSAPublicKeySpec(key);
 365             modulus = spec.getModulus();
 366         }
 367         return modulus.bitLength();
 368     }
 369 
 370     static RSAPublicKeySpec getRSAPublicKeySpec(PublicKey key) {
 371         if (key instanceof RSAPublicKey) {
 372             RSAPublicKey rsaKey = (RSAPublicKey)key;
 373             return new RSAPublicKeySpec(rsaKey.getModulus(),
 374                                         rsaKey.getPublicExponent());
 375         }
 376         try {
 377             KeyFactory factory = JsseJce.getKeyFactory("RSA");
 378             return factory.getKeySpec(key, RSAPublicKeySpec.class);
 379         } catch (Exception e) {
 380             throw new RuntimeException(e);
 381         }
 382     }
 383 
 384     static ECParameterSpec getECParameterSpec(String namedCurveOid) {
 385         return ECUtil.getECParameterSpec(cryptoProvider, namedCurveOid);
 386     }
 387 
 388     static String getNamedCurveOid(ECParameterSpec params) {
 389         return ECUtil.getCurveName(cryptoProvider, params);
 390     }
 391 
 392     static ECPoint decodePoint(byte[] encoded, EllipticCurve curve)
 393             throws java.io.IOException {
 394         return ECUtil.decodePoint(encoded, curve);
 395     }
 396 
 397     static byte[] encodePoint(ECPoint point, EllipticCurve curve) {
 398         return ECUtil.encodePoint(point, curve);
 399     }
 400 
 401     // In FIPS mode, set thread local providers; otherwise a no-op.
 402     // Must be paired with endFipsProvider.
 403     static Object beginFipsProvider() {
 404         if (fipsProviderList == null) {
 405             return null;
 406         } else {
 407             return Providers.beginThreadProviderList(fipsProviderList);
 408         }
 409     }
 410 
 411     static void endFipsProvider(Object o) {
 412         if (fipsProviderList != null) {
 413             Providers.endThreadProviderList((ProviderList)o);
 414         }
 415     }
 416 
 417 }