blob: 11b124ac00a998574444cb8d74477bc4df194700 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-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
26package javax.crypto;
27
28import java.lang.ref.*;
29import java.util.*;
30import java.util.jar.*;
31import java.io.*;
32import java.net.URL;
33import java.security.*;
34
35import java.security.Provider.Service;
36
37import sun.security.jca.*;
38import sun.security.jca.GetInstance.Instance;
39
40/**
41 * This class instantiates implementations of JCE engine classes from
42 * providers registered with the java.security.Security object.
43 *
44 * @author Jan Luehe
45 * @author Sharon Liu
46 * @since 1.4
47 */
48
49final class JceSecurity {
50
51 static final SecureRandom RANDOM = new SecureRandom();
52
53 // The defaultPolicy and exemptPolicy will be set up
54 // in the static initializer.
55 private static CryptoPermissions defaultPolicy = null;
56 private static CryptoPermissions exemptPolicy = null;
57
58 // Map<Provider,?> of the providers we already have verified
59 // value == PROVIDER_VERIFIED is successfully verified
60 // value is failure cause Exception in error case
61 private final static Map verificationResults = new IdentityHashMap();
62
63 // Map<Provider,?> of the providers currently being verified
64 private final static Map verifyingProviders = new IdentityHashMap();
65
66 // Set the default value. May be changed in the static initializer.
67 private static boolean isRestricted = true;
68
69 /*
70 * Don't let anyone instantiate this.
71 */
72 private JceSecurity() {
73 }
74
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
95 static Instance getInstance(String type, Class clazz, String algorithm,
96 String provider) throws NoSuchAlgorithmException,
97 NoSuchProviderException {
98 Service s = GetInstance.getService(type, algorithm, provider);
99 Exception ve = getVerificationResult(s.getProvider());
100 if (ve != null) {
101 String msg = "JCE cannot authenticate the provider " + provider;
102 throw (NoSuchProviderException)
103 new NoSuchProviderException(msg).initCause(ve);
104 }
105 return GetInstance.getInstance(s, clazz);
106 }
107
108 static Instance getInstance(String type, Class clazz, String algorithm,
109 Provider provider) throws NoSuchAlgorithmException {
110 Service s = GetInstance.getService(type, algorithm, provider);
111 Exception ve = JceSecurity.getVerificationResult(provider);
112 if (ve != null) {
113 String msg = "JCE cannot authenticate the provider "
114 + provider.getName();
115 throw new SecurityException(msg, ve);
116 }
117 return GetInstance.getInstance(s, clazz);
118 }
119
120 static Instance getInstance(String type, Class clazz, String algorithm)
121 throws NoSuchAlgorithmException {
122 List services = GetInstance.getServices(type, algorithm);
123 NoSuchAlgorithmException failure = null;
124 for (Iterator t = services.iterator(); t.hasNext(); ) {
125 Service s = (Service)t.next();
126 if (canUseProvider(s.getProvider()) == false) {
127 // allow only signed providers
128 continue;
129 }
130 try {
131 Instance instance = GetInstance.getInstance(s, clazz);
132 return instance;
133 } catch (NoSuchAlgorithmException e) {
134 failure = e;
135 }
136 }
137 throw new NoSuchAlgorithmException("Algorithm " + algorithm
138 + " not available", failure);
139 }
140
141 /**
142 * Verify if the JAR at URL codeBase is a signed exempt application
143 * JAR file and returns the permissions bundled with the JAR.
144 *
145 * @throws Exception on error
146 */
147 static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception {
148 JarVerifier jv = new JarVerifier(codeBase, true);
149 jv.verify();
150 return jv.getPermissions();
151 }
152
153 /**
154 * Verify if the JAR at URL codeBase is a signed provider JAR file.
155 *
156 * @throws Exception on error
157 */
158 static void verifyProviderJar(URL codeBase) throws Exception {
159 // Verify the provider JAR file and all
160 // supporting JAR files if there are any.
161 JarVerifier jv = new JarVerifier(codeBase, false);
162 jv.verify();
163 }
164
165 private final static Object PROVIDER_VERIFIED = Boolean.TRUE;
166
167 /*
168 * Verify that the provider JAR files are signed properly, which
169 * means the signer's certificate can be traced back to a
170 * JCE trusted CA.
171 * Return null if ok, failure Exception if verification failed.
172 */
173 static synchronized Exception getVerificationResult(Provider p) {
174 Object o = verificationResults.get(p);
175 if (o == PROVIDER_VERIFIED) {
176 return null;
177 } else if (o != null) {
178 return (Exception)o;
179 }
180 if (verifyingProviders.get(p) != null) {
181 // this method is static synchronized, must be recursion
182 // return failure now but do not save the result
183 return new NoSuchProviderException("Recursion during verification");
184 }
185 try {
186 verifyingProviders.put(p, Boolean.FALSE);
187 URL providerURL = getCodeBase(p.getClass());
188 verifyProviderJar(providerURL);
189 // Verified ok, cache result
190 verificationResults.put(p, PROVIDER_VERIFIED);
191 return null;
192 } catch (Exception e) {
193 verificationResults.put(p, e);
194 return e;
195 } finally {
196 verifyingProviders.remove(p);
197 }
198 }
199
200 // return whether this provider is properly signed and can be used by JCE
201 static boolean canUseProvider(Provider p) {
202 return getVerificationResult(p) == null;
203 }
204
205 // dummy object to represent null
206 private static final URL NULL_URL;
207
208 static {
209 try {
210 NULL_URL = new URL("http://null.sun.com/");
211 } catch (Exception e) {
212 throw new RuntimeException(e);
213 }
214 }
215
216 // reference to a Map we use as a cache for codebases
217 private static final Map codeBaseCacheRef = new WeakHashMap();
218
219 /*
220 * Retuns the CodeBase for the given class.
221 */
222 static URL getCodeBase(final Class clazz) {
223 URL url = (URL)codeBaseCacheRef.get(clazz);
224 if (url == null) {
225 url = (URL)AccessController.doPrivileged(new PrivilegedAction() {
226 public Object run() {
227 ProtectionDomain pd = clazz.getProtectionDomain();
228 if (pd != null) {
229 CodeSource cs = pd.getCodeSource();
230 if (cs != null) {
231 return cs.getLocation();
232 }
233 }
234 return NULL_URL;
235 }
236 });
237 codeBaseCacheRef.put(clazz, url);
238 }
239 return (url == NULL_URL) ? null : url;
240 }
241
242 private static void setupJurisdictionPolicies() throws Exception {
243 String javaHomeDir = System.getProperty("java.home");
244 String sep = File.separator;
245 String pathToPolicyJar = javaHomeDir + sep + "lib" + sep +
246 "security" + sep;
247
248 File exportJar = new File(pathToPolicyJar, "US_export_policy.jar");
249 File importJar = new File(pathToPolicyJar, "local_policy.jar");
250 URL jceCipherURL = ClassLoader.getSystemResource
251 ("javax/crypto/Cipher.class");
252
253 if ((jceCipherURL == null) ||
254 !exportJar.exists() || !importJar.exists()) {
255 throw new SecurityException
256 ("Cannot locate policy or framework files!");
257 }
258
259 // Enforce the signer restraint, i.e. signer of JCE framework
260 // jar should also be the signer of the two jurisdiction policy
261 // jar files.
262 JarVerifier.verifyFrameworkSigned(jceCipherURL);
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}