• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1997, 2016, 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<Provider, Object> verificationResults =
61             new IdentityHashMap<>();
62 
63     // Map<Provider,?> of the providers currently being verified
64     private final static Map<Provider, Object> verifyingProviders =
65             new IdentityHashMap<>();
66 
67     // Android-removed: JCE crypto strength restrictions are never in place on Android.
68     // private static final boolean isRestricted = true;
69 
70     // Android-removed: This debugging mechanism is not used in Android.
71     /*
72     private static final Debug debug =
73                         Debug.getInstance("jca", "Cipher");
74     */
75 
76     /*
77      * Don't let anyone instantiate this.
78      */
JceSecurity()79     private JceSecurity() {
80     }
81 
82     // BEGIN Android-removed: JCE crypto strength restrictions are never in place on Android.
83     /*
84     static {
85         try {
86             AccessController.doPrivileged(
87                 new PrivilegedExceptionAction<Object>() {
88                     public Object run() throws Exception {
89                         setupJurisdictionPolicies();
90                         return null;
91                     }
92                 });
93 
94             isRestricted = defaultPolicy.implies(
95                 CryptoAllPermission.INSTANCE) ? false : true;
96         } catch (Exception e) {
97             throw new SecurityException(
98                     "Can not initialize cryptographic mechanism", e);
99         }
100     }
101     */
102     // END Android-removed: JCE crypto strength restrictions are never in place on Android.
103 
getInstance(String type, Class<?> clazz, String algorithm, String provider)104     static Instance getInstance(String type, Class<?> clazz, String algorithm,
105             String provider) throws NoSuchAlgorithmException,
106             NoSuchProviderException {
107         Service s = GetInstance.getService(type, algorithm, provider);
108         Exception ve = getVerificationResult(s.getProvider());
109         if (ve != null) {
110             String msg = "JCE cannot authenticate the provider " + provider;
111             throw (NoSuchProviderException)
112                                 new NoSuchProviderException(msg).initCause(ve);
113         }
114         return GetInstance.getInstance(s, clazz);
115     }
116 
getInstance(String type, Class<?> clazz, String algorithm, Provider provider)117     static Instance getInstance(String type, Class<?> clazz, String algorithm,
118             Provider provider) throws NoSuchAlgorithmException {
119         Service s = GetInstance.getService(type, algorithm, provider);
120         Exception ve = JceSecurity.getVerificationResult(provider);
121         if (ve != null) {
122             String msg = "JCE cannot authenticate the provider "
123                 + provider.getName();
124             throw new SecurityException(msg, ve);
125         }
126         return GetInstance.getInstance(s, clazz);
127     }
128 
getInstance(String type, Class<?> clazz, String algorithm)129     static Instance getInstance(String type, Class<?> clazz, String algorithm)
130             throws NoSuchAlgorithmException {
131         List<Service> services = GetInstance.getServices(type, algorithm);
132         NoSuchAlgorithmException failure = null;
133         for (Service s : services) {
134             if (canUseProvider(s.getProvider()) == false) {
135                 // allow only signed providers
136                 continue;
137             }
138             try {
139                 Instance instance = GetInstance.getInstance(s, clazz);
140                 return instance;
141             } catch (NoSuchAlgorithmException e) {
142                 failure = e;
143             }
144         }
145         throw new NoSuchAlgorithmException("Algorithm " + algorithm
146                 + " not available", failure);
147     }
148 
149     /**
150      * Verify if the JAR at URL codeBase is a signed exempt application
151      * JAR file and returns the permissions bundled with the JAR.
152      *
153      * @throws Exception on error
154      */
verifyExemptJar(URL codeBase)155     static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception {
156         JarVerifier jv = new JarVerifier(codeBase, true);
157         jv.verify();
158         return jv.getPermissions();
159     }
160 
161     /**
162      * Verify if the JAR at URL codeBase is a signed provider JAR file.
163      *
164      * @throws Exception on error
165      */
verifyProviderJar(URL codeBase)166     static void verifyProviderJar(URL codeBase) throws Exception {
167         // Verify the provider JAR file and all
168         // supporting JAR files if there are any.
169         JarVerifier jv = new JarVerifier(codeBase, false);
170         jv.verify();
171     }
172 
173     private final static Object PROVIDER_VERIFIED = Boolean.TRUE;
174 
175     /*
176      * Verify that the provider JAR files are signed properly, which
177      * means the signer's certificate can be traced back to a
178      * JCE trusted CA.
179      * Return null if ok, failure Exception if verification failed.
180      */
getVerificationResult(Provider p)181     static synchronized Exception getVerificationResult(Provider p) {
182         Object o = verificationResults.get(p);
183         if (o == PROVIDER_VERIFIED) {
184             return null;
185         } else if (o != null) {
186             return (Exception)o;
187         }
188         if (verifyingProviders.get(p) != null) {
189             // this method is static synchronized, must be recursion
190             // return failure now but do not save the result
191             return new NoSuchProviderException("Recursion during verification");
192         }
193         try {
194             verifyingProviders.put(p, Boolean.FALSE);
195             URL providerURL = getCodeBase(p.getClass());
196             verifyProviderJar(providerURL);
197             // Verified ok, cache result
198             verificationResults.put(p, PROVIDER_VERIFIED);
199             return null;
200         } catch (Exception e) {
201             verificationResults.put(p, e);
202             return e;
203         } finally {
204             verifyingProviders.remove(p);
205         }
206     }
207 
208     // return whether this provider is properly signed and can be used by JCE
canUseProvider(Provider p)209     static boolean canUseProvider(Provider p) {
210         // BEGIN Android-changed: All providers are available.
211         // return getVerificationResult(p) == null;
212         return true;
213         // END Android-changed: All providers are available.
214     }
215 
216     // dummy object to represent null
217     private static final URL NULL_URL;
218 
219     static {
220         try {
221             NULL_URL = new URL("http://null.oracle.com/");
222         } catch (Exception e) {
223             throw new RuntimeException(e);
224         }
225     }
226 
227     // reference to a Map we use as a cache for codebases
228     private static final Map<Class<?>, URL> codeBaseCacheRef =
229             new WeakHashMap<>();
230 
231     /*
232      * Returns the CodeBase for the given class.
233      */
getCodeBase(final Class<?> clazz)234     static URL getCodeBase(final Class<?> clazz) {
235         synchronized (codeBaseCacheRef) {
236             URL url = codeBaseCacheRef.get(clazz);
237             if (url == null) {
238                 url = AccessController.doPrivileged(new PrivilegedAction<URL>() {
239                     public URL run() {
240                         ProtectionDomain pd = clazz.getProtectionDomain();
241                         if (pd != null) {
242                             CodeSource cs = pd.getCodeSource();
243                             if (cs != null) {
244                                 return cs.getLocation();
245                             }
246                         }
247                         return NULL_URL;
248                     }
249                 });
250                 codeBaseCacheRef.put(clazz, url);
251             }
252             return (url == NULL_URL) ? null : url;
253         }
254     }
255 
256     // BEGIN Android-removed: JCE crypto strength restrictions are never in place on Android.
257     /*
258      * This is called from within an doPrivileged block.
259      *
260      * Following logic is used to decide what policy files are selected.
261      *
262      * If the new Security property (crypto.policy) is set in the
263      * java.security file, or has been set dynamically using the
264      * Security.setProperty() call before the JCE framework has
265      * been initialized, that setting will be used.
266      * Remember - this property is not defined by default. A conscious
267      * user edit or an application call is required.
268      *
269      * Otherwise, if user has policy jar files installed in the legacy
270      * jre/lib/security/ directory, the JDK will honor whatever
271      * setting is set by those policy files. (legacy/current behavior)
272      *
273      * If none of the above 2 conditions are met, the JDK will default
274      * to using the limited crypto policy files found in the
275      * jre/lib/security/policy/limited/ directory
276      *
277     private static void setupJurisdictionPolicies() throws Exception {
278         // Sanity check the crypto.policy Security property.  Single
279         // directory entry, no pseudo-directories (".", "..", leading/trailing
280         // path separators). normalize()/getParent() will help later.
281         String javaHomeProperty = System.getProperty("java.home");
282         String cryptoPolicyProperty = Security.getProperty("crypto.policy");
283         Path cpPath = (cryptoPolicyProperty == null) ? null :
284                 Paths.get(cryptoPolicyProperty);
285 
286         if ((cpPath != null) && ((cpPath.getNameCount() != 1) ||
287                 (cpPath.compareTo(cpPath.getFileName())) != 0)) {
288             throw new SecurityException(
289                     "Invalid policy directory name format: " +
290                             cryptoPolicyProperty);
291         }
292 
293         if (cpPath == null) {
294             // Security property is not set, use default path
295             cpPath = Paths.get(javaHomeProperty, "lib", "security");
296         } else {
297             // populate with java.home
298             cpPath = Paths.get(javaHomeProperty, "lib", "security",
299                     "policy", cryptoPolicyProperty);
300         }
301 
302         if (debug != null) {
303             debug.println("crypto policy directory: " + cpPath);
304         }
305 
306         File exportJar = new File(cpPath.toFile(),"US_export_policy.jar");
307         File importJar = new File(cpPath.toFile(),"local_policy.jar");
308 
309         if (cryptoPolicyProperty == null && (!exportJar.exists() ||
310                 !importJar.exists())) {
311             // Compatibility set up. If crypto.policy is not defined.
312             // check to see if legacy jars exist in lib directory. If
313             // they don't exist, we default to limited policy mode.
314             cpPath = Paths.get(
315                     javaHomeProperty, "lib", "security", "policy", "limited");
316             // point to the new jar files in limited directory
317             exportJar = new File(cpPath.toFile(),"US_export_policy.jar");
318             importJar = new File(cpPath.toFile(),"local_policy.jar");
319         }
320 
321         URL jceCipherURL = ClassLoader.getSystemResource
322                 ("javax/crypto/Cipher.class");
323 
324         if ((jceCipherURL == null) ||
325                 !exportJar.exists() || !importJar.exists()) {
326             throw new SecurityException
327                                 ("Cannot locate policy or framework files!");
328         }
329 
330         // Read jurisdiction policies.
331         CryptoPermissions defaultExport = new CryptoPermissions();
332         CryptoPermissions exemptExport = new CryptoPermissions();
333         loadPolicies(exportJar, defaultExport, exemptExport);
334 
335         CryptoPermissions defaultImport = new CryptoPermissions();
336         CryptoPermissions exemptImport = new CryptoPermissions();
337         loadPolicies(importJar, defaultImport, exemptImport);
338 
339         // Merge the export and import policies for default applications.
340         if (defaultExport.isEmpty() || defaultImport.isEmpty()) {
341             throw new SecurityException("Missing mandatory jurisdiction " +
342                                         "policy files");
343         }
344         defaultPolicy = defaultExport.getMinimum(defaultImport);
345 
346         // Merge the export and import policies for exempt applications.
347         if (exemptExport.isEmpty())  {
348             exemptPolicy = exemptImport.isEmpty() ? null : exemptImport;
349         } else {
350             exemptPolicy = exemptExport.getMinimum(exemptImport);
351         }
352     }
353     */
354     // END Android-removed: JCE crypto strength restrictions are never in place on Android.
355 
356     /**
357      * Load the policies from the specified file. Also checks that the
358      * policies are correctly signed.
359      */
loadPolicies(File jarPathName, CryptoPermissions defaultPolicy, CryptoPermissions exemptPolicy)360     private static void loadPolicies(File jarPathName,
361                                      CryptoPermissions defaultPolicy,
362                                      CryptoPermissions exemptPolicy)
363         throws Exception {
364 
365         JarFile jf = new JarFile(jarPathName);
366 
367         Enumeration<JarEntry> entries = jf.entries();
368         while (entries.hasMoreElements()) {
369             JarEntry je = entries.nextElement();
370             InputStream is = null;
371             try {
372                 if (je.getName().startsWith("default_")) {
373                     is = jf.getInputStream(je);
374                     defaultPolicy.load(is);
375                 } else if (je.getName().startsWith("exempt_")) {
376                     is = jf.getInputStream(je);
377                     exemptPolicy.load(is);
378                 } else {
379                     continue;
380                 }
381             } finally {
382                 if (is != null) {
383                     is.close();
384                 }
385             }
386 
387             // Enforce the signer restraint, i.e. signer of JCE framework
388             // jar should also be the signer of the two jurisdiction policy
389             // jar files.
390             JarVerifier.verifyPolicySigned(je.getCertificates());
391         }
392         // Close and nullify the JarFile reference to help GC.
393         jf.close();
394         jf = null;
395     }
396 
getDefaultPolicy()397     static CryptoPermissions getDefaultPolicy() {
398         return defaultPolicy;
399     }
400 
getExemptPolicy()401     static CryptoPermissions getExemptPolicy() {
402         return exemptPolicy;
403     }
404 
405     // Android-removed: JCE crypto strength restrictions are never in place on Android.
406     // static boolean isRestricted() {
407     //     return isRestricted;
408     // }
409 }
410