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