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