1 /* 2 * Copyright (c) 1997, 2009, 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 javax.crypto; 27 28 import java.util.*; 29 import java.util.jar.*; 30 import java.io.*; 31 import java.net.URL; 32 import java.security.*; 33 34 import java.security.Provider.Service; 35 36 import sun.security.jca.*; 37 import sun.security.jca.GetInstance.Instance; 38 39 /** 40 * This class instantiates implementations of JCE engine classes from 41 * providers registered with the java.security.Security object. 42 * 43 * @author Jan Luehe 44 * @author Sharon Liu 45 * @since 1.4 46 */ 47 48 final class JceSecurity { 49 50 static final SecureRandom RANDOM = new SecureRandom(); 51 52 // The defaultPolicy and exemptPolicy will be set up 53 // in the static initializer. 54 private static CryptoPermissions defaultPolicy = null; 55 private static CryptoPermissions exemptPolicy = null; 56 57 // Map<Provider,?> of the providers we already have verified 58 // value == PROVIDER_VERIFIED is successfully verified 59 // value is failure cause Exception in error case 60 private final static Map verificationResults = new IdentityHashMap(); 61 62 // Map<Provider,?> of the providers currently being verified 63 private final static Map verifyingProviders = new IdentityHashMap(); 64 65 // Set the default value. May be changed in the static initializer. 66 private static boolean isRestricted = true; 67 68 /* 69 * Don't let anyone instantiate this. 70 */ 71 private JceSecurity() { 72 } 73 74 /* ----- BEGIN android ----- 75 static { 76 try { 77 AccessController.doPrivileged(new PrivilegedExceptionAction() { 78 public Object run() throws Exception { 79 setupJurisdictionPolicies(); 80 return null; 81 } 82 }); 83 84 isRestricted = defaultPolicy.implies( 85 CryptoAllPermission.INSTANCE) ? false : true; 86 } catch (Exception e) { 87 SecurityException se = 88 new SecurityException( 89 "Can not initialize cryptographic mechanism"); 90 se.initCause(e); 91 throw se; 92 } 93 } 94 ----- END android ----- */ 95 96 static Instance getInstance(String type, Class clazz, String algorithm, 97 String provider) throws NoSuchAlgorithmException, 98 NoSuchProviderException { 99 Service s = GetInstance.getService(type, algorithm, provider); 100 Exception ve = getVerificationResult(s.getProvider()); 101 if (ve != null) { 102 String msg = "JCE cannot authenticate the provider " + provider; 103 throw (NoSuchProviderException) 104 new NoSuchProviderException(msg).initCause(ve); 105 } 106 return GetInstance.getInstance(s, clazz); 107 } 108 109 static Instance getInstance(String type, Class clazz, String algorithm, 110 Provider provider) throws NoSuchAlgorithmException { 111 Service s = GetInstance.getService(type, algorithm, provider); 112 Exception ve = JceSecurity.getVerificationResult(provider); 113 if (ve != null) { 114 String msg = "JCE cannot authenticate the provider " 115 + provider.getName(); 116 throw new SecurityException(msg, ve); 117 } 118 return GetInstance.getInstance(s, clazz); 119 } 120 121 static Instance getInstance(String type, Class clazz, String algorithm) 122 throws NoSuchAlgorithmException { 123 List services = GetInstance.getServices(type, algorithm); 124 NoSuchAlgorithmException failure = null; 125 for (Iterator t = services.iterator(); t.hasNext(); ) { 126 Service s = (Service)t.next(); 127 if (canUseProvider(s.getProvider()) == false) { 128 // allow only signed providers 129 continue; 130 } 131 try { 132 Instance instance = GetInstance.getInstance(s, clazz); 133 return instance; 134 } catch (NoSuchAlgorithmException e) { 135 failure = e; 136 } 137 } 138 throw new NoSuchAlgorithmException("Algorithm " + algorithm 139 + " not available", failure); 140 } 141 142 /** 143 * Verify if the JAR at URL codeBase is a signed exempt application 144 * JAR file and returns the permissions bundled with the JAR. 145 * 146 * @throws Exception on error 147 */ 148 static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception { 149 JarVerifier jv = new JarVerifier(codeBase, true); 150 jv.verify(); 151 return jv.getPermissions(); 152 } 153 154 /** 155 * Verify if the JAR at URL codeBase is a signed provider JAR file. 156 * 157 * @throws Exception on error 158 */ 159 static void verifyProviderJar(URL codeBase) throws Exception { 160 // Verify the provider JAR file and all 161 // supporting JAR files if there are any. 162 JarVerifier jv = new JarVerifier(codeBase, false); 163 jv.verify(); 164 } 165 166 private final static Object PROVIDER_VERIFIED = Boolean.TRUE; 167 168 /* 169 * Verify that the provider JAR files are signed properly, which 170 * means the signer's certificate can be traced back to a 171 * JCE trusted CA. 172 * Return null if ok, failure Exception if verification failed. 173 */ 174 static synchronized Exception getVerificationResult(Provider p) { 175 Object o = verificationResults.get(p); 176 if (o == PROVIDER_VERIFIED) { 177 return null; 178 } else if (o != null) { 179 return (Exception)o; 180 } 181 if (verifyingProviders.get(p) != null) { 182 // this method is static synchronized, must be recursion 183 // return failure now but do not save the result 184 return new NoSuchProviderException("Recursion during verification"); 185 } 186 try { 187 verifyingProviders.put(p, Boolean.FALSE); 188 URL providerURL = getCodeBase(p.getClass()); 189 verifyProviderJar(providerURL); 190 // Verified ok, cache result 191 verificationResults.put(p, PROVIDER_VERIFIED); 192 return null; 193 } catch (Exception e) { 194 verificationResults.put(p, e); 195 return e; 196 } finally { 197 verifyingProviders.remove(p); 198 } 199 } 200 201 // return whether this provider is properly signed and can be used by JCE 202 static boolean canUseProvider(Provider p) { 203 /* ----- BEGIN android 204 return getVerificationResult(p) == null; 205 */ 206 return true; 207 // ----- END android ----- 208 } 209 210 // dummy object to represent null 211 private static final URL NULL_URL; 212 213 static { 214 try { 215 NULL_URL = new URL("http://null.sun.com/"); 216 } catch (Exception e) { 217 throw new RuntimeException(e); 218 } 219 } 220 221 // reference to a Map we use as a cache for codebases 222 private static final Map codeBaseCacheRef = new WeakHashMap(); 223 224 /* 225 * Retuns the CodeBase for the given class. 226 */ 227 static URL getCodeBase(final Class clazz) { 228 URL url = (URL)codeBaseCacheRef.get(clazz); 229 if (url == null) { 230 url = (URL)AccessController.doPrivileged(new PrivilegedAction() { 231 public Object run() { 232 ProtectionDomain pd = clazz.getProtectionDomain(); 233 if (pd != null) { 234 CodeSource cs = pd.getCodeSource(); 235 if (cs != null) { 236 return cs.getLocation(); 237 } 238 } 239 return NULL_URL; 240 } 241 }); 242 codeBaseCacheRef.put(clazz, url); 243 } 244 return (url == NULL_URL) ? null : url; 245 } 246 247 private static void setupJurisdictionPolicies() throws Exception { 248 String javaHomeDir = System.getProperty("java.home"); 249 String sep = File.separator; 250 String pathToPolicyJar = javaHomeDir + sep + "lib" + sep + 251 "security" + sep; 252 253 File exportJar = new File(pathToPolicyJar, "US_export_policy.jar"); 254 File importJar = new File(pathToPolicyJar, "local_policy.jar"); 255 URL jceCipherURL = ClassLoader.getSystemResource 256 ("javax/crypto/Cipher.class"); 257 258 if ((jceCipherURL == null) || 259 !exportJar.exists() || !importJar.exists()) { 260 throw new SecurityException 261 ("Cannot locate policy or framework files!"); 262 } 263 264 // Read jurisdiction policies. 265 CryptoPermissions defaultExport = new CryptoPermissions(); 266 CryptoPermissions exemptExport = new CryptoPermissions(); 267 loadPolicies(exportJar, defaultExport, exemptExport); 268 269 CryptoPermissions defaultImport = new CryptoPermissions(); 270 CryptoPermissions exemptImport = new CryptoPermissions(); 271 loadPolicies(importJar, defaultImport, exemptImport); 272 273 // Merge the export and import policies for default applications. 274 if (defaultExport.isEmpty() || defaultImport.isEmpty()) { 275 throw new SecurityException("Missing mandatory jurisdiction " + 276 "policy files"); 277 } 278 defaultPolicy = defaultExport.getMinimum(defaultImport); 279 280 // Merge the export and import policies for exempt applications. 281 if (exemptExport.isEmpty()) { 282 exemptPolicy = exemptImport.isEmpty() ? null : exemptImport; 283 } else { 284 exemptPolicy = exemptExport.getMinimum(exemptImport); 285 } 286 } 287 288 /** 289 * Load the policies from the specified file. Also checks that the 290 * policies are correctly signed. 291 */ 292 private static void loadPolicies(File jarPathName, 293 CryptoPermissions defaultPolicy, 294 CryptoPermissions exemptPolicy) 295 throws Exception { 296 297 JarFile jf = new JarFile(jarPathName); 298 299 Enumeration entries = jf.entries(); 300 while (entries.hasMoreElements()) { 301 JarEntry je = (JarEntry)entries.nextElement(); 302 InputStream is = null; 303 try { 304 if (je.getName().startsWith("default_")) { 305 is = jf.getInputStream(je); 306 defaultPolicy.load(is); 307 } else if (je.getName().startsWith("exempt_")) { 308 is = jf.getInputStream(je); 309 exemptPolicy.load(is); 310 } else { 311 continue; 312 } 313 } finally { 314 if (is != null) { 315 is.close(); 316 } 317 } 318 319 // Enforce the signer restraint, i.e. signer of JCE framework 320 // jar should also be the signer of the two jurisdiction policy 321 // jar files. 322 JarVerifier.verifyPolicySigned(je.getCertificates()); 323 } 324 // Close and nullify the JarFile reference to help GC. 325 jf.close(); 326 jf = null; 327 } 328 329 static CryptoPermissions getDefaultPolicy() { 330 return defaultPolicy; 331 } 332 333 static CryptoPermissions getExemptPolicy() { 334 return exemptPolicy; 335 } 336 337 static boolean isRestricted() { 338 return isRestricted; 339 } 340 } 341