|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package javax.crypto; |
|
|
|
import java.io.*; |
|
import java.util.Enumeration; |
|
import java.util.Hashtable; |
|
import java.util.Vector; |
|
import static java.util.Locale.ENGLISH; |
|
|
|
import java.security.GeneralSecurityException; |
|
import java.security.spec.AlgorithmParameterSpec; |
|
import java.lang.reflect.*; |
|
|
|
/** |
|
* JCE has two pairs of jurisdiction policy files: one represents U.S. export |
|
* laws, and the other represents the local laws of the country where the |
|
* JCE will be used. |
|
* |
|
* The jurisdiction policy file has the same syntax as JDK policy files except |
|
* that JCE has new permission classes called javax.crypto.CryptoPermission |
|
* and javax.crypto.CryptoAllPermission. |
|
* |
|
* The format of a permission entry in the jurisdiction policy file is: |
|
* |
|
* permission <crypto permission class name>[, <algorithm name> |
|
* [[, <exemption mechanism name>][, <maxKeySize> |
|
* [, <AlgrithomParameterSpec class name>, <parameters |
|
* for constructing an AlgrithomParameterSpec object>]]]]; |
|
* |
|
* @author Sharon Liu |
|
* |
|
* @see java.security.Permissions |
|
* @see java.security.spec.AlgorithmParameterSpec |
|
* @see javax.crypto.CryptoPermission |
|
* @see javax.crypto.CryptoAllPermission |
|
* @see javax.crypto.CryptoPermissions |
|
* @since 1.4 |
|
*/ |
|
|
|
final class CryptoPolicyParser { |
|
|
|
private Vector<GrantEntry> grantEntries; |
|
|
|
|
|
private StreamTokenizer st; |
|
private int lookahead; |
|
|
|
|
|
|
|
*/ |
|
CryptoPolicyParser() { |
|
grantEntries = new Vector<GrantEntry>(); |
|
} |
|
|
|
/** |
|
* Reads a policy configuration using a Reader object. <p> |
|
* |
|
* @param policy the policy Reader object. |
|
* |
|
* @exception ParsingException if the policy configuration |
|
* contains a syntax error. |
|
* |
|
* @exception IOException if an error occurs while reading |
|
* the policy configuration. |
|
*/ |
|
|
|
void read(Reader policy) |
|
throws ParsingException, IOException |
|
{ |
|
if (!(policy instanceof BufferedReader)) { |
|
policy = new BufferedReader(policy); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
st = new StreamTokenizer(policy); |
|
|
|
st.resetSyntax(); |
|
st.wordChars('a', 'z'); |
|
st.wordChars('A', 'Z'); |
|
st.wordChars('.', '.'); |
|
st.wordChars('0', '9'); |
|
st.wordChars('_', '_'); |
|
st.wordChars('$', '$'); |
|
st.wordChars(128 + 32, 255); |
|
st.whitespaceChars(0, ' '); |
|
st.commentChar('/'); |
|
st.quoteChar('\''); |
|
st.quoteChar('"'); |
|
st.lowerCaseMode(false); |
|
st.ordinaryChar('/'); |
|
st.slashSlashComments(true); |
|
st.slashStarComments(true); |
|
st.parseNumbers(); |
|
|
|
|
|
|
|
|
|
*/ |
|
Hashtable<String, Vector<String>> processedPermissions = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
lookahead = st.nextToken(); |
|
while (lookahead != StreamTokenizer.TT_EOF) { |
|
if (peek("grant")) { |
|
GrantEntry ge = parseGrantEntry(processedPermissions); |
|
if (ge != null) |
|
grantEntries.addElement(ge); |
|
} else { |
|
throw new ParsingException(st.lineno(), "expected grant " + |
|
"statement"); |
|
} |
|
match(";"); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private GrantEntry parseGrantEntry( |
|
Hashtable<String, Vector<String>> processedPermissions) |
|
throws ParsingException, IOException |
|
{ |
|
GrantEntry e = new GrantEntry(); |
|
|
|
match("grant"); |
|
match("{"); |
|
|
|
while(!peek("}")) { |
|
if (peek("Permission")) { |
|
CryptoPermissionEntry pe = |
|
parsePermissionEntry(processedPermissions); |
|
e.add(pe); |
|
match(";"); |
|
} else { |
|
throw new |
|
ParsingException(st.lineno(), "expected permission entry"); |
|
} |
|
} |
|
match("}"); |
|
|
|
return e; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private CryptoPermissionEntry parsePermissionEntry( |
|
Hashtable<String, Vector<String>> processedPermissions) |
|
throws ParsingException, IOException |
|
{ |
|
CryptoPermissionEntry e = new CryptoPermissionEntry(); |
|
|
|
match("Permission"); |
|
e.cryptoPermission = match("permission type"); |
|
|
|
if (e.cryptoPermission.equals("javax.crypto.CryptoAllPermission")) { |
|
|
|
e.alg = CryptoAllPermission.ALG_NAME; |
|
e.maxKeySize = Integer.MAX_VALUE; |
|
return e; |
|
} |
|
|
|
|
|
if (peek("\"")) { |
|
|
|
e.alg = match("quoted string").toUpperCase(ENGLISH); |
|
} else { |
|
|
|
if (peek("*")) { |
|
match("*"); |
|
e.alg = CryptoPermission.ALG_NAME_WILDCARD; |
|
} else { |
|
throw new ParsingException(st.lineno(), |
|
"Missing the algorithm name"); |
|
} |
|
} |
|
|
|
peekAndMatch(","); |
|
|
|
|
|
if (peek("\"")) { |
|
|
|
e.exemptionMechanism = match("quoted string").toUpperCase(ENGLISH); |
|
} |
|
|
|
peekAndMatch(","); |
|
|
|
// Check whether this entry is consistent with other permission entries |
|
|
|
if (!isConsistent(e.alg, e.exemptionMechanism, processedPermissions)) { |
|
throw new ParsingException(st.lineno(), "Inconsistent policy"); |
|
} |
|
|
|
|
|
if (peek("number")) { |
|
e.maxKeySize = match(); |
|
} else { |
|
if (peek("*")) { |
|
match("*"); |
|
e.maxKeySize = Integer.MAX_VALUE; |
|
} else { |
|
if (!peek(";")) { |
|
throw new ParsingException(st.lineno(), |
|
"Missing the maximum " + |
|
"allowable key size"); |
|
} else { |
|
|
|
e.maxKeySize = Integer.MAX_VALUE; |
|
} |
|
} |
|
} |
|
|
|
peekAndMatch(","); |
|
|
|
|
|
if (peek("\"")) { |
|
|
|
String algParamSpecClassName = match("quoted string"); |
|
|
|
Vector<Integer> paramsV = new Vector<>(1); |
|
while (peek(",")) { |
|
match(","); |
|
if (peek("number")) { |
|
paramsV.addElement(new Integer(match())); |
|
} else { |
|
if (peek("*")) { |
|
match("*"); |
|
paramsV.addElement(new Integer(Integer.MAX_VALUE)); |
|
} else { |
|
throw new ParsingException(st.lineno(), |
|
"Expecting an integer"); |
|
} |
|
} |
|
} |
|
|
|
Integer[] params = new Integer[paramsV.size()]; |
|
paramsV.copyInto(params); |
|
|
|
e.checkParam = true; |
|
e.algParamSpec = getInstance(algParamSpecClassName, params); |
|
} |
|
|
|
return e; |
|
} |
|
|
|
private static final AlgorithmParameterSpec getInstance(String type, |
|
Integer[] params) |
|
throws ParsingException |
|
{ |
|
AlgorithmParameterSpec ret = null; |
|
|
|
try { |
|
Class<?> apsClass = Class.forName(type); |
|
Class<?>[] paramClasses = new Class<?>[params.length]; |
|
|
|
for (int i = 0; i < params.length; i++) { |
|
paramClasses[i] = int.class; |
|
} |
|
|
|
Constructor<?> c = apsClass.getConstructor(paramClasses); |
|
ret = (AlgorithmParameterSpec) c.newInstance((Object[]) params); |
|
} catch (Exception e) { |
|
throw new ParsingException("Cannot call the constructor of " + |
|
type + e); |
|
} |
|
return ret; |
|
} |
|
|
|
|
|
private boolean peekAndMatch(String expect) |
|
throws ParsingException, IOException |
|
{ |
|
if (peek(expect)) { |
|
match(expect); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
private boolean peek(String expect) { |
|
boolean found = false; |
|
|
|
switch (lookahead) { |
|
|
|
case StreamTokenizer.TT_WORD: |
|
if (expect.equalsIgnoreCase(st.sval)) |
|
found = true; |
|
break; |
|
case StreamTokenizer.TT_NUMBER: |
|
if (expect.equalsIgnoreCase("number")) { |
|
found = true; |
|
} |
|
break; |
|
case ',': |
|
if (expect.equals(",")) |
|
found = true; |
|
break; |
|
case '{': |
|
if (expect.equals("{")) |
|
found = true; |
|
break; |
|
case '}': |
|
if (expect.equals("}")) |
|
found = true; |
|
break; |
|
case '"': |
|
if (expect.equals("\"")) |
|
found = true; |
|
break; |
|
case '*': |
|
if (expect.equals("*")) |
|
found = true; |
|
break; |
|
case ';': |
|
if (expect.equals(";")) |
|
found = true; |
|
break; |
|
default: |
|
break; |
|
} |
|
return found; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private int match() |
|
throws ParsingException, IOException |
|
{ |
|
int value = -1; |
|
int lineno = st.lineno(); |
|
String sValue = null; |
|
|
|
switch (lookahead) { |
|
case StreamTokenizer.TT_NUMBER: |
|
value = (int)st.nval; |
|
if (value < 0) { |
|
sValue = String.valueOf(st.nval); |
|
} |
|
lookahead = st.nextToken(); |
|
break; |
|
default: |
|
sValue = st.sval; |
|
break; |
|
} |
|
if (value <= 0) { |
|
throw new ParsingException(lineno, "a non-negative number", |
|
sValue); |
|
} |
|
return value; |
|
} |
|
|
|
private String match(String expect) |
|
throws ParsingException, IOException |
|
{ |
|
String value = null; |
|
|
|
switch (lookahead) { |
|
case StreamTokenizer.TT_NUMBER: |
|
throw new ParsingException(st.lineno(), expect, |
|
"number "+String.valueOf(st.nval)); |
|
case StreamTokenizer.TT_EOF: |
|
throw new ParsingException("expected "+expect+", read end of file"); |
|
case StreamTokenizer.TT_WORD: |
|
if (expect.equalsIgnoreCase(st.sval)) { |
|
lookahead = st.nextToken(); |
|
} |
|
else if (expect.equalsIgnoreCase("permission type")) { |
|
value = st.sval; |
|
lookahead = st.nextToken(); |
|
} |
|
else |
|
throw new ParsingException(st.lineno(), expect, st.sval); |
|
break; |
|
case '"': |
|
if (expect.equalsIgnoreCase("quoted string")) { |
|
value = st.sval; |
|
lookahead = st.nextToken(); |
|
} else if (expect.equalsIgnoreCase("permission type")) { |
|
value = st.sval; |
|
lookahead = st.nextToken(); |
|
} |
|
else |
|
throw new ParsingException(st.lineno(), expect, st.sval); |
|
break; |
|
case ',': |
|
if (expect.equals(",")) |
|
lookahead = st.nextToken(); |
|
else |
|
throw new ParsingException(st.lineno(), expect, ","); |
|
break; |
|
case '{': |
|
if (expect.equals("{")) |
|
lookahead = st.nextToken(); |
|
else |
|
throw new ParsingException(st.lineno(), expect, "{"); |
|
break; |
|
case '}': |
|
if (expect.equals("}")) |
|
lookahead = st.nextToken(); |
|
else |
|
throw new ParsingException(st.lineno(), expect, "}"); |
|
break; |
|
case ';': |
|
if (expect.equals(";")) |
|
lookahead = st.nextToken(); |
|
else |
|
throw new ParsingException(st.lineno(), expect, ";"); |
|
break; |
|
case '*': |
|
if (expect.equals("*")) |
|
lookahead = st.nextToken(); |
|
else |
|
throw new ParsingException(st.lineno(), expect, "*"); |
|
break; |
|
default: |
|
throw new ParsingException(st.lineno(), expect, |
|
new String(new char[] {(char)lookahead})); |
|
} |
|
return value; |
|
} |
|
|
|
CryptoPermission[] getPermissions() { |
|
Vector<CryptoPermission> result = new Vector<>(); |
|
|
|
Enumeration<GrantEntry> grantEnum = grantEntries.elements(); |
|
while (grantEnum.hasMoreElements()) { |
|
GrantEntry ge = grantEnum.nextElement(); |
|
Enumeration<CryptoPermissionEntry> permEnum = |
|
ge.permissionElements(); |
|
while (permEnum.hasMoreElements()) { |
|
CryptoPermissionEntry pe = permEnum.nextElement(); |
|
if (pe.cryptoPermission.equals( |
|
"javax.crypto.CryptoAllPermission")) { |
|
result.addElement(CryptoAllPermission.INSTANCE); |
|
} else { |
|
if (pe.checkParam) { |
|
result.addElement(new CryptoPermission( |
|
pe.alg, |
|
pe.maxKeySize, |
|
pe.algParamSpec, |
|
pe.exemptionMechanism)); |
|
} else { |
|
result.addElement(new CryptoPermission( |
|
pe.alg, |
|
pe.maxKeySize, |
|
pe.exemptionMechanism)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
CryptoPermission[] ret = new CryptoPermission[result.size()]; |
|
result.copyInto(ret); |
|
|
|
return ret; |
|
} |
|
|
|
private boolean isConsistent(String alg, String exemptionMechanism, |
|
Hashtable<String, Vector<String>> processedPermissions) { |
|
String thisExemptionMechanism = |
|
exemptionMechanism == null ? "none" : exemptionMechanism; |
|
|
|
if (processedPermissions == null) { |
|
processedPermissions = new Hashtable<String, Vector<String>>(); |
|
Vector<String> exemptionMechanisms = new Vector<>(1); |
|
exemptionMechanisms.addElement(thisExemptionMechanism); |
|
processedPermissions.put(alg, exemptionMechanisms); |
|
return true; |
|
} |
|
|
|
if (processedPermissions.containsKey(CryptoAllPermission.ALG_NAME)) { |
|
return false; |
|
} |
|
|
|
Vector<String> exemptionMechanisms; |
|
|
|
if (processedPermissions.containsKey(alg)) { |
|
exemptionMechanisms = processedPermissions.get(alg); |
|
if (exemptionMechanisms.contains(thisExemptionMechanism)) { |
|
return false; |
|
} |
|
} else { |
|
exemptionMechanisms = new Vector<String>(1); |
|
} |
|
|
|
exemptionMechanisms.addElement(thisExemptionMechanism); |
|
processedPermissions.put(alg, exemptionMechanisms); |
|
return true; |
|
} |
|
|
|
/** |
|
* Each grant entry in the policy configuration file is represented by a |
|
* GrantEntry object. <p> |
|
* |
|
* <p> |
|
* For example, the entry |
|
* <pre> |
|
* grant { |
|
* permission javax.crypto.CryptoPermission "DES", 56; |
|
* }; |
|
* |
|
* </pre> |
|
* is represented internally |
|
* <pre> |
|
* |
|
* pe = new CryptoPermissionEntry("javax.crypto.CryptoPermission", |
|
* "DES", 56); |
|
* |
|
* ge = new GrantEntry(); |
|
* |
|
* ge.add(pe); |
|
* |
|
* </pre> |
|
* |
|
* @see java.security.Permission |
|
* @see javax.crypto.CryptoPermission |
|
* @see javax.crypto.CryptoPermissions |
|
*/ |
|
|
|
private static class GrantEntry { |
|
|
|
private Vector<CryptoPermissionEntry> permissionEntries; |
|
|
|
GrantEntry() { |
|
permissionEntries = new Vector<CryptoPermissionEntry>(); |
|
} |
|
|
|
void add(CryptoPermissionEntry pe) |
|
{ |
|
permissionEntries.addElement(pe); |
|
} |
|
|
|
boolean remove(CryptoPermissionEntry pe) |
|
{ |
|
return permissionEntries.removeElement(pe); |
|
} |
|
|
|
boolean contains(CryptoPermissionEntry pe) |
|
{ |
|
return permissionEntries.contains(pe); |
|
} |
|
|
|
|
|
|
|
*/ |
|
Enumeration<CryptoPermissionEntry> permissionElements(){ |
|
return permissionEntries.elements(); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Each crypto permission entry in the policy configuration file is |
|
* represented by a CryptoPermissionEntry object. <p> |
|
* |
|
* <p> |
|
* For example, the entry |
|
* <pre> |
|
* permission javax.crypto.CryptoPermission "DES", 56; |
|
* </pre> |
|
* is represented internally |
|
* <pre> |
|
* |
|
* pe = new CryptoPermissionEntry("javax.crypto.cryptoPermission", |
|
* "DES", 56); |
|
* </pre> |
|
* |
|
* @see java.security.Permissions |
|
* @see javax.crypto.CryptoPermission |
|
* @see javax.crypto.CryptoAllPermission |
|
*/ |
|
|
|
private static class CryptoPermissionEntry { |
|
|
|
String cryptoPermission; |
|
String alg; |
|
String exemptionMechanism; |
|
int maxKeySize; |
|
boolean checkParam; |
|
AlgorithmParameterSpec algParamSpec; |
|
|
|
CryptoPermissionEntry() { |
|
|
|
maxKeySize = 0; |
|
alg = null; |
|
exemptionMechanism = null; |
|
checkParam = false; |
|
algParamSpec = null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public int hashCode() { |
|
int retval = cryptoPermission.hashCode(); |
|
if (alg != null) retval ^= alg.hashCode(); |
|
if (exemptionMechanism != null) { |
|
retval ^= exemptionMechanism.hashCode(); |
|
} |
|
retval ^= maxKeySize; |
|
if (checkParam) retval ^= 100; |
|
if (algParamSpec != null) { |
|
retval ^= algParamSpec.hashCode(); |
|
} |
|
return retval; |
|
} |
|
|
|
public boolean equals(Object obj) { |
|
if (obj == this) |
|
return true; |
|
|
|
if (!(obj instanceof CryptoPermissionEntry)) |
|
return false; |
|
|
|
CryptoPermissionEntry that = (CryptoPermissionEntry) obj; |
|
|
|
if (this.cryptoPermission == null) { |
|
if (that.cryptoPermission != null) return false; |
|
} else { |
|
if (!this.cryptoPermission.equals( |
|
that.cryptoPermission)) |
|
return false; |
|
} |
|
|
|
if (this.alg == null) { |
|
if (that.alg != null) return false; |
|
} else { |
|
if (!this.alg.equalsIgnoreCase(that.alg)) |
|
return false; |
|
} |
|
|
|
if (!(this.maxKeySize == that.maxKeySize)) return false; |
|
|
|
if (this.checkParam != that.checkParam) return false; |
|
|
|
if (this.algParamSpec == null) { |
|
if (that.algParamSpec != null) return false; |
|
} else { |
|
if (!this.algParamSpec.equals(that.algParamSpec)) |
|
return false; |
|
} |
|
|
|
|
|
return true; |
|
} |
|
} |
|
|
|
static final class ParsingException extends GeneralSecurityException { |
|
|
|
private static final long serialVersionUID = 7147241245566588374L; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
ParsingException(String msg) { |
|
super(msg); |
|
} |
|
|
|
ParsingException(int line, String msg) { |
|
super("line " + line + ": " + msg); |
|
} |
|
|
|
ParsingException(int line, String expect, String actual) { |
|
super("line "+line+": expected '"+expect+"', found '"+actual+"'"); |
|
} |
|
} |
|
} |