Home | History | Annotate | Download | only in crypto
      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