|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package javax.crypto; |
|
|
|
import java.util.*; |
|
import java.util.jar.*; |
|
import java.io.*; |
|
import java.net.URL; |
|
import java.nio.file.*; |
|
import java.security.*; |
|
|
|
import java.security.Provider.Service; |
|
|
|
import sun.security.jca.*; |
|
import sun.security.jca.GetInstance.Instance; |
|
import sun.security.util.Debug; |
|
|
|
/** |
|
* This class instantiates implementations of JCE engine classes from |
|
* providers registered with the java.security.Security object. |
|
* |
|
* @author Jan Luehe |
|
* @author Sharon Liu |
|
* @since 1.4 |
|
*/ |
|
|
|
final class JceSecurity { |
|
|
|
static final SecureRandom RANDOM = new SecureRandom(); |
|
|
|
// The defaultPolicy and exemptPolicy will be set up |
|
|
|
private static CryptoPermissions defaultPolicy = null; |
|
private static CryptoPermissions exemptPolicy = null; |
|
|
|
// Map<Provider,?> of the providers we already have verified |
|
// value == PROVIDER_VERIFIED is successfully verified |
|
|
|
private final static Map<Provider, Object> verificationResults = |
|
new IdentityHashMap<>(); |
|
|
|
|
|
private final static Map<Provider, Object> verifyingProviders = |
|
new IdentityHashMap<>(); |
|
|
|
private static final boolean isRestricted; |
|
|
|
private static final Debug debug = |
|
Debug.getInstance("jca", "Cipher"); |
|
|
|
|
|
|
|
*/ |
|
private JceSecurity() { |
|
} |
|
|
|
static { |
|
try { |
|
AccessController.doPrivileged( |
|
new PrivilegedExceptionAction<Object>() { |
|
public Object run() throws Exception { |
|
setupJurisdictionPolicies(); |
|
return null; |
|
} |
|
}); |
|
|
|
isRestricted = defaultPolicy.implies( |
|
CryptoAllPermission.INSTANCE) ? false : true; |
|
} catch (Exception e) { |
|
throw new SecurityException( |
|
"Can not initialize cryptographic mechanism", e); |
|
} |
|
} |
|
|
|
static Instance getInstance(String type, Class<?> clazz, String algorithm, |
|
String provider) throws NoSuchAlgorithmException, |
|
NoSuchProviderException { |
|
Service s = GetInstance.getService(type, algorithm, provider); |
|
Exception ve = getVerificationResult(s.getProvider()); |
|
if (ve != null) { |
|
String msg = "JCE cannot authenticate the provider " + provider; |
|
throw (NoSuchProviderException) |
|
new NoSuchProviderException(msg).initCause(ve); |
|
} |
|
return GetInstance.getInstance(s, clazz); |
|
} |
|
|
|
static Instance getInstance(String type, Class<?> clazz, String algorithm, |
|
Provider provider) throws NoSuchAlgorithmException { |
|
Service s = GetInstance.getService(type, algorithm, provider); |
|
Exception ve = JceSecurity.getVerificationResult(provider); |
|
if (ve != null) { |
|
String msg = "JCE cannot authenticate the provider " |
|
+ provider.getName(); |
|
throw new SecurityException(msg, ve); |
|
} |
|
return GetInstance.getInstance(s, clazz); |
|
} |
|
|
|
static Instance getInstance(String type, Class<?> clazz, String algorithm) |
|
throws NoSuchAlgorithmException { |
|
List<Service> services = GetInstance.getServices(type, algorithm); |
|
NoSuchAlgorithmException failure = null; |
|
for (Service s : services) { |
|
if (canUseProvider(s.getProvider()) == false) { |
|
|
|
continue; |
|
} |
|
try { |
|
Instance instance = GetInstance.getInstance(s, clazz); |
|
return instance; |
|
} catch (NoSuchAlgorithmException e) { |
|
failure = e; |
|
} |
|
} |
|
throw new NoSuchAlgorithmException("Algorithm " + algorithm |
|
+ " not available", failure); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception { |
|
JarVerifier jv = new JarVerifier(codeBase, true); |
|
jv.verify(); |
|
return jv.getPermissions(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void verifyProviderJar(URL codeBase) throws Exception { |
|
// Verify the provider JAR file and all |
|
|
|
JarVerifier jv = new JarVerifier(codeBase, false); |
|
jv.verify(); |
|
} |
|
|
|
private final static Object PROVIDER_VERIFIED = Boolean.TRUE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static synchronized Exception getVerificationResult(Provider p) { |
|
Object o = verificationResults.get(p); |
|
if (o == PROVIDER_VERIFIED) { |
|
return null; |
|
} else if (o != null) { |
|
return (Exception)o; |
|
} |
|
if (verifyingProviders.get(p) != null) { |
|
// this method is static synchronized, must be recursion |
|
|
|
return new NoSuchProviderException("Recursion during verification"); |
|
} |
|
try { |
|
verifyingProviders.put(p, Boolean.FALSE); |
|
URL providerURL = getCodeBase(p.getClass()); |
|
verifyProviderJar(providerURL); |
|
|
|
verificationResults.put(p, PROVIDER_VERIFIED); |
|
return null; |
|
} catch (Exception e) { |
|
verificationResults.put(p, e); |
|
return e; |
|
} finally { |
|
verifyingProviders.remove(p); |
|
} |
|
} |
|
|
|
|
|
static boolean canUseProvider(Provider p) { |
|
return getVerificationResult(p) == null; |
|
} |
|
|
|
|
|
private static final URL NULL_URL; |
|
|
|
static { |
|
try { |
|
NULL_URL = new URL("http://null.oracle.com/"); |
|
} catch (Exception e) { |
|
throw new RuntimeException(e); |
|
} |
|
} |
|
|
|
|
|
private static final Map<Class<?>, URL> codeBaseCacheRef = |
|
new WeakHashMap<>(); |
|
|
|
|
|
|
|
*/ |
|
static URL getCodeBase(final Class<?> clazz) { |
|
synchronized (codeBaseCacheRef) { |
|
URL url = codeBaseCacheRef.get(clazz); |
|
if (url == null) { |
|
url = AccessController.doPrivileged(new PrivilegedAction<URL>() { |
|
public URL run() { |
|
ProtectionDomain pd = clazz.getProtectionDomain(); |
|
if (pd != null) { |
|
CodeSource cs = pd.getCodeSource(); |
|
if (cs != null) { |
|
return cs.getLocation(); |
|
} |
|
} |
|
return NULL_URL; |
|
} |
|
}); |
|
codeBaseCacheRef.put(clazz, url); |
|
} |
|
return (url == NULL_URL) ? null : url; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static void setupJurisdictionPolicies() throws Exception { |
|
// Sanity check the crypto.policy Security property. Single |
|
// directory entry, no pseudo-directories (".", "..", leading/trailing |
|
|
|
String javaHomeProperty = System.getProperty("java.home"); |
|
String cryptoPolicyProperty = Security.getProperty("crypto.policy"); |
|
Path cpPath = (cryptoPolicyProperty == null) ? null : |
|
Paths.get(cryptoPolicyProperty); |
|
|
|
if ((cpPath != null) && ((cpPath.getNameCount() != 1) || |
|
(cpPath.compareTo(cpPath.getFileName())) != 0)) { |
|
throw new SecurityException( |
|
"Invalid policy directory name format: " + |
|
cryptoPolicyProperty); |
|
} |
|
|
|
if (cpPath == null) { |
|
|
|
cpPath = Paths.get(javaHomeProperty, "lib", "security"); |
|
} else { |
|
|
|
cpPath = Paths.get(javaHomeProperty, "lib", "security", |
|
"policy", cryptoPolicyProperty); |
|
} |
|
|
|
if (debug != null) { |
|
debug.println("crypto policy directory: " + cpPath); |
|
} |
|
|
|
File exportJar = new File(cpPath.toFile(),"US_export_policy.jar"); |
|
File importJar = new File(cpPath.toFile(),"local_policy.jar"); |
|
|
|
if (cryptoPolicyProperty == null && (!exportJar.exists() || |
|
!importJar.exists())) { |
|
// Compatibility set up. If crypto.policy is not defined. |
|
// check to see if legacy jars exist in lib directory. If |
|
|
|
cpPath = Paths.get( |
|
javaHomeProperty, "lib", "security", "policy", "unlimited"); |
|
|
|
exportJar = new File(cpPath.toFile(),"US_export_policy.jar"); |
|
importJar = new File(cpPath.toFile(),"local_policy.jar"); |
|
} |
|
|
|
URL jceCipherURL = ClassLoader.getSystemResource |
|
("javax/crypto/Cipher.class"); |
|
|
|
if ((jceCipherURL == null) || |
|
!exportJar.exists() || !importJar.exists()) { |
|
throw new SecurityException |
|
("Cannot locate policy or framework files!"); |
|
} |
|
|
|
|
|
CryptoPermissions defaultExport = new CryptoPermissions(); |
|
CryptoPermissions exemptExport = new CryptoPermissions(); |
|
loadPolicies(exportJar, defaultExport, exemptExport); |
|
|
|
CryptoPermissions defaultImport = new CryptoPermissions(); |
|
CryptoPermissions exemptImport = new CryptoPermissions(); |
|
loadPolicies(importJar, defaultImport, exemptImport); |
|
|
|
|
|
if (defaultExport.isEmpty() || defaultImport.isEmpty()) { |
|
throw new SecurityException("Missing mandatory jurisdiction " + |
|
"policy files"); |
|
} |
|
defaultPolicy = defaultExport.getMinimum(defaultImport); |
|
|
|
|
|
if (exemptExport.isEmpty()) { |
|
exemptPolicy = exemptImport.isEmpty() ? null : exemptImport; |
|
} else { |
|
exemptPolicy = exemptExport.getMinimum(exemptImport); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static void loadPolicies(File jarPathName, |
|
CryptoPermissions defaultPolicy, |
|
CryptoPermissions exemptPolicy) |
|
throws Exception { |
|
|
|
JarFile jf = new JarFile(jarPathName); |
|
|
|
Enumeration<JarEntry> entries = jf.entries(); |
|
while (entries.hasMoreElements()) { |
|
JarEntry je = entries.nextElement(); |
|
InputStream is = null; |
|
try { |
|
if (je.getName().startsWith("default_")) { |
|
is = jf.getInputStream(je); |
|
defaultPolicy.load(is); |
|
} else if (je.getName().startsWith("exempt_")) { |
|
is = jf.getInputStream(je); |
|
exemptPolicy.load(is); |
|
} else { |
|
continue; |
|
} |
|
} finally { |
|
if (is != null) { |
|
is.close(); |
|
} |
|
} |
|
|
|
// Enforce the signer restraint, i.e. signer of JCE framework |
|
// jar should also be the signer of the two jurisdiction policy |
|
|
|
JarVerifier.verifyPolicySigned(je.getCertificates()); |
|
} |
|
|
|
jf.close(); |
|
jf = null; |
|
} |
|
|
|
static CryptoPermissions getDefaultPolicy() { |
|
return defaultPolicy; |
|
} |
|
|
|
static CryptoPermissions getExemptPolicy() { |
|
return exemptPolicy; |
|
} |
|
|
|
static boolean isRestricted() { |
|
return isRestricted; |
|
} |
|
} |