-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 >>>>> "Mark" == Mark Wielaard <[EMAIL PROTECTED]> writes:
Mark> [...] Mark> As you can see very little coding, reviewing or even testing of Mark> code :( My new year resolution will be: Less talking, More Mark> coding! There's a few things relating to the security/crypto bits of Classpath and friends that I'd like to mention, and get opinions on: - Jessie looks like it will become a GNU package someday, but there is still some question of where it should go. Suggestions include * Classpathx, but Nic has indicated that he thinks Classpathx would be better off in Classpath proper. * Classpath, possibly as an optional/sub- project. * On its own, all by its lonesome. Above all I'd like to see this project keep some level of independence from Classpath, so it can evolve at its own pace. - I've been extending the X.509 classes I put into Classpath a while ago to complete the PKI platform with things such as CertPathValidator implementations, and have started implementing NIST's giant PKI test suite [1] around Mauve. There is a *totally* unofficial tarball of this at [2] (the username is "pki" and the password "gnupki" -- I don't want Joe Searchengine downloading this quite yet). I currently have 73 tests (out of hundreds) implemented, and they all pass. The questions here are similar to those for Jessie: where should this go? The PKIX standard is giant and complicated. Anyone with copious spare time (...) is welcome to help. - Classpath, Jessie, and the GNU PKI packages all have their own copies of some simple ASN.1 DER codec classes. This isn't good. Some sort of grand unified ASN.1 library would be nice to have. - KeyStores. We have a brand-new keyring format in GNU Crypto, and attached is a minimal read-only version for public keyrings. - Policy files. I have been working on and off on an implementation of java.security.Policy that reads JDK-style policy files. I've attached my current version. - javax.crypto and javax.net.ssl should go into Classpath, in my opinion. - My own list for future additions include * Kerberos and GSSAPI. * OpenPGP. * Keytool and jarsigner equivalents. - --- [1] http://csrc.nist.gov/pki/testing/x509paths.html [2] http://metastatic.org/source/gnu-pki/ - -- Casey Marshall || [EMAIL PROTECTED] -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.3 (GNU/Linux) Comment: Processed by Mailcrypt 3.5.7 <http://mailcrypt.sourceforge.net/> iD8DBQE/349wgAuWMgRGsWsRAt6OAJ9G+nmGO5e2LNtlg8Fzk3XDr9JgLgCfXUed dX6Agjacd3S2QrBD+uf+Qxo= =RMwn -----END PGP SIGNATURE-----
/* GnuKeyring.java -- minimal, read-only GNU Keyring implementation. Copyright (C) 2003 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package gnu.java.security.provider; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.security.Key; import java.security.KeyStoreSpi; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; import java.util.zip.InflaterInputStream; public class GnuKeyring extends KeyStoreSpi { // Constants and fields. // ------------------------------------------------------------------------- // The value "GKR" | 0x01 private static final int MAGIC = 0x474b5201; // Keyring usage type 2: trusted certificates only. private static final int USAGE = 04; // Packet types. Types not appearing in public keyrings are omitted. private static final int TYPE_PBMAC = 3; private static final int TYPE_COMPRESSED = 4; private static final int TYPE_CERT = 5; private final HashMap entries; // Constructor. // ------------------------------------------------------------------------- public GnuKeyring() { entries = new HashMap(); } // Instance methods. // ------------------------------------------------------------------------- public Key engineGetKey(String alias, char[] password) { throw new UnsupportedOperationException("this is a public keyring"); } public Certificate[] engineGetCertificateChain(String alias) { throw new UnsupportedOperationException("this is a public keyring"); } public Certificate engineGetCertificate(String alias) { Entry e = (Entry) entries.get(canonicalize(alias)); if (e == null) { return null; } return e.getCertificate(); } public Date engineGetCreationDate(String alias) { Entry e = (Entry) entries.get(canonicalize(alias)); if (e == null) { return null; } return e.getCreationDate(); } public void engineSetKeyEntry(String alias, Key key, char[] passwd, Certificate[] chain) { throw new UnsupportedOperationException("this is a read-only keyring"); } public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) { throw new UnsupportedOperationException("this is a read-only keyring"); } public void engineSetCertificateEntry(String alias, Certificate certificate) { throw new UnsupportedOperationException("this is a read-only keyring"); } public void engineDeleteEntry(String alias) { throw new UnsupportedOperationException("this is a read-only keyring"); } public Enumeration engineAliases() { return new Vector(entries.keySet()).elements(); } public boolean engineContainsAlias(String alias) { return entries.containsKey(canonicalize(alias)); } public int engineSize() { return entries.size(); } public boolean engineIsKeyEntry(String alias) { return false; } public boolean engineIsCertificateEntry(String alias) { return engineContainsAlias(alias); } public String engineGetCertificateAlias(Certificate certificate) { for (Iterator it = entries.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry) it.next(); if (certificate.equals(e.getValue())) { return (String) e.getKey(); } } return null; } public void engineStore(OutputStream out, char[] password) { throw new UnsupportedOperationException("this is a read-only keyring"); } public synchronized void engineLoad(InputStream _in, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { entries.clear(); DataInputStream din = new DataInputStream(_in); if (din.readInt() != MAGIC) { throw new IOException("expecting magic bytes"); } if (din.read() != USAGE) { throw new IOException("not a public keyring"); } // Top-level is a password-based MAC entry. if (din.read() != TYPE_PBMAC) { throw new IOException("expecting password-based MAC"); } Map props = readProperties(din); int macLen = 0; try { macLen = Integer.parseInt((String) props.get("maclen")); if (macLen <= 0) { throw new NumberFormatException("must be positive"); } } catch (Exception x) { throw new IOException("malformed mac length: " + x.toString()); } int len = din.readInt(); byte[] payload = new byte[len - macLen]; byte[] mac = new byte[macLen]; din.readFully(payload); din.readFully(mac); if (!verifyHash(props, payload, mac, password)) { throw new IOException("MAC could not be verified"); } DataInputStream in2 = new DataInputStream(new ByteArrayInputStream(payload)); CertificateFactory factory = null; Certificate cert = null; Date date = null; while (in2.available() > 0) { int type = in2.read(); switch (type) { case TYPE_COMPRESSED: props = readProperties(in2); String comp = (String) props.get("algorithm"); if (comp == null || !comp.equalsIgnoreCase("DEFLATE")) { throw new NoSuchAlgorithmException(comp); } payload = new byte[in2.readInt()]; in2.readFully(payload); in2 = new DataInputStream(new InflaterInputStream( new ByteArrayInputStream(payload))); break; case TYPE_CERT: props = readProperties(in2); String alias = (String) props.get("alias"); if (alias == null || alias.length() == 0) { throw new IOException("no alias for certificate entry"); } date = null; try { String s = (String) props.get("creation-date"); if (s == null) { throw new IOException("missing creation date"); } date = new Date(Long.parseLong(s)); } catch (NumberFormatException x) { throw new IOException("malformed date: " + x.toString()); } factory = CertificateFactory.getInstance((String) props.get("type")); payload = new byte[in2.readInt()]; in2.readFully(payload); cert = factory.generateCertificate(new ByteArrayInputStream(payload)); entries.put(alias, new Entry(cert, date)); break; case -1: return; default: throw new IOException("unknown packet type: " + type); } } } // Own methods. // ------------------------------------------------------------------------- private String canonicalize(String key) { if (key == null) { return null; } return key.toLowerCase(); } private boolean verifyHash(Map props, byte[] payload, byte[] hash, char[] password) throws IOException, NoSuchAlgorithmException { // If no password is supplied, don't check the hash. if (password == null) { return true; } String macName = (String) props.get("mac"); if (macName == null || !macName.toLowerCase().startsWith("hmac-")) { throw new NoSuchAlgorithmException("invalid MAC"); } HMac mac = new HMac(MessageDigest.getInstance(macName.substring(5), new Gnu())); // Generate a key from the password. byte[] mackey = new byte[mac.getDigestLength()]; HMac kdfmac = new HMac(MessageDigest.getInstance("SHA-1", new Gnu())); kdfmac.setup(new String(password).getBytes("UTF-8")); byte[] salt = decodeSalt((String) props.get("salt")); int limit = salt.length; byte[] in = new byte[limit + 4]; System.arraycopy(salt, 0, in, 0, salt.length); // The count is appended to 'in', but in this case it is always 1. in[in.length - 1] = 1; for (int i = 0; i < 1000; i++) { kdfmac.reset(); kdfmac.update(in, 0, in.length); in = kdfmac.digest(); for (int j = 0; j < mackey.length; j++) { mackey[j] ^= in[j]; } } // Compute and check the MAC. mac.setup(mackey); mac.update(payload, 0, payload.length); byte[] d = mac.digest(); for (int i = 0; i < d.length && i < hash.length; i++) { if (d[i] != hash[i]) { return false; } } return true; } private Map readProperties(DataInputStream in) throws IOException { HashMap props = new HashMap(); int len = in.readInt(); byte[] buf = new byte[len]; in.readFully(buf); DataInputStream in2 = new DataInputStream(new ByteArrayInputStream(buf)); while (in2.available() > 0) { String key = in2.readUTF(); String value = in2.readUTF(); props.put(canonicalize(key), value); } return props; } private byte[] decodeSalt(String salt) { int limit = salt.length(); byte[] result = new byte[((limit + 1) / 2)]; int i = 0, j = 0; if ((limit & 1) == 1) { result[j++] = (byte) Character.digit(salt.charAt(i++), 16); } while (i < limit) { result[j ] = (byte) (Character.digit(salt.charAt(i++), 16) << 4); result[j++] |= (byte) Character.digit(salt.charAt(i++), 16); } return result; } // Inner classes. // ------------------------------------------------------------------------- /** * A single certificate entry. */ private static final class Entry { // Fields. // ----------------------------------------------------------------------- private final Certificate certificate; private final Date creationDate; // Constructor. // ----------------------------------------------------------------------- Entry(Certificate certificate, Date creationDate) { this.certificate = certificate; this.creationDate = creationDate; } // Instance methods. // ----------------------------------------------------------------------- Certificate getCertificate() { return certificate; } Date getCreationDate() { return (Date) creationDate.clone(); } } /** * Minimal HMac implemnentation, derived from GNU Crypto. */ private static final class HMac { // Constants and fields. // ----------------------------------------------------------------------- private MessageDigest md, ipadHash, opadHash; private byte[] ipad; // Constructor. // ----------------------------------------------------------------------- HMac(MessageDigest md) { this.md = md; } // Instance methods. // ----------------------------------------------------------------------- void setup(byte[] key) { if (key.length > 64) { key = md.digest(key); } if (key.length < 64) { byte[] b = new byte[64]; System.arraycopy(key, 0, b, 0, key.length); key = b; } ipad = new byte[64]; for (int i = 0; i < ipad.length; i++) { ipad[i] = (byte) (key[i] ^ 0x36); } md.reset(); try { opadHash = (MessageDigest) md.clone(); } catch (CloneNotSupportedException shouldNotHappen) { throw new Error(shouldNotHappen); } for (int i = 0; i < key.length; i++) { opadHash.update((byte) (key[i] ^ 0x5C)); } md.update(ipad); try { ipadHash = (MessageDigest) md.clone(); } catch (CloneNotSupportedException shouldNotHappen) { throw new Error(shouldNotHappen); } } void update(byte[] buf, int off, int len) { md.update(buf, off, len); } byte[] digest() { byte[] result = md.digest(); try { md = (MessageDigest) opadHash.clone(); } catch (CloneNotSupportedException shouldNotHappen) { throw new Error(shouldNotHappen); } result = md.digest(result); md.reset(); return result; } void reset() { md.reset(); if (ipad == null) { return; } md.update(ipad); try { ipadHash = (MessageDigest) md.clone(); } catch (CloneNotSupportedException shouldNotHappen) { throw new Error(shouldNotHappen); } } int getDigestLength() { return md.getDigestLength(); } } }
/* PolicyFile.java -- policy file reader. Copyright (C) 2003 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package gnu.java.security; import java.io.File; import java.io.FileReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.Reader; import java.io.StreamTokenizer; import java.lang.reflect.Constructor; import java.net.URL; import java.net.MalformedURLException; import java.security.AccessController; import java.security.CodeSource; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.Permission; import java.security.Permissions; import java.security.PermissionCollection; import java.security.Policy; import java.security.Principal; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.Security; import java.security.UnresolvedPermission; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; /** * An implementation of a [EMAIL PROTECTED] java.security.Policy} object whose * permissions are specified by a <em>policy file</em>. * * <p>The approximate syntax of policy files is:</p> * * <pre> * policyFile ::= keystoreOrGrantEntries ; * * keystoreOrGrantEntries ::= keystoreOrGrantEntry | * keystoreOrGrantEntries keystoreOrGrantEntry | * EMPTY ; * * keystoreOrGrantEntry ::= keystoreEntry | grantEntry ; * * keystoreEntry ::= "keystore" keystoreUrl ';' | * "keystore" keystoreUrl ',' keystoreAlgorithm ';' ; * * keystoreUrl ::= URL ; * keystoreAlgorithm ::= STRING ; * * grantEntry ::= "grant" domainParameters '{' permissions '}' ';' * * domainParameters ::= domainParameter | * domainParameter ',' domainParameters ; * * domainParameter ::= "signedBy" signerNames | * "codeBase" codeBaseUrl | * "principal" principalClassName principalName | * "principal" principalName ; * * signerNames ::= quotedString ; * codeBaseUrl ::= URL ; * principalClassName ::= STRING ; * principalName ::= quotedString ; * * quotedString ::= quoteChar STRING quoteChar ; * quoteChar ::= '"' | '\''; * * permissions ::= permission | permissions permission ; * * permission ::= "permission" permissionClassName permissionTarget permissionAction | * "permission" permissionClassName permissionTarget | * "permission" permissionClassName; * </pre> * * <p>Comments are either form of Java comments. Keystore entries only * affect subsequent grant entries, so if a grant entry preceeds a * keystore entry, that grant entry is not affected by that keystore * entry. Certian instances of <code>${property-name}</code> will be * replaced with <code>System.getProperty("property-name")</code> in * quoted strings.</p> * * <p>This class will load the following files when created or * refreshed, in order:</p> * * <ol> * <li>The file <code>${java.home}/lib/security/java.policy</code>.</li> * <li>All URLs specified by security properties * <code>"policy.file.<i>n</i>"</code>, for increasing <i>n</i> * starting from 1. The sequence stops at the first undefined * property, so you must set <code>"policy.file.1"</code> if you also * set <code>"policy.file.2"</code>, and so on.</li> * <li>The URL specified by the property * <code>"java.security.policy"</code>.</li> * </ol> * * @author Casey Marshall ([EMAIL PROTECTED]) * @see java.security.Policy */ public final class PolicyFile extends Policy { // Constants and fields. // ------------------------------------------------------------------------- private static final boolean DEBUG = true; private static void debug(String msg) { if (DEBUG) { System.err.print(">> PolicyFile: "); System.err.println(msg); } } private static void debug(Throwable t) { if (DEBUG) { System.err.println(">> PolicyFile"); t.printStackTrace(System.err); } } private static final String DEFAULT_POLICY = System.getProperty("java.home") + System.getProperty("file.separator") + "lib" + System.getProperty("file.separator") + "security" + System.getProperty("file.separator") + "java.policy"; private final Map cs2pc; // Constructors. // ------------------------------------------------------------------------- public PolicyFile() { cs2pc = new HashMap(); refresh(); } // Instance methods. // ------------------------------------------------------------------------- public PermissionCollection getPermissions(CodeSource codeSource) { Permissions perms = new Permissions(); for (Iterator it = cs2pc.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry) it.next(); CodeSource cs = (CodeSource) e.getKey(); if (cs.implies(codeSource)) { debug(cs+" -> "+codeSource); PermissionCollection pc = (PermissionCollection) e.getValue(); for (Enumeration ee = pc.elements(); ee.hasMoreElements(); ) { perms.add((Permission) ee.nextElement()); } } else debug(cs+" !-> "+codeSource); } perms.setReadOnly(); return perms; } public void refresh() { cs2pc.clear(); List policyFiles = new LinkedList(); try { policyFiles.add(new File(DEFAULT_POLICY).toURL()); policyFiles.addAll((List) AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws Exception { LinkedList l = new LinkedList(); for (int i = 1; ; i++) { String s = Security.getProperty("policy.file."+i); debug("policy.file."+i+"="+s); if (s == null) break; l.add(new URL(s)); } String s = System.getProperty("java.security.policy"); debug("java.security.policy="+s); if (s != null) l.add(new URL(s)); return l; } })); } catch (PrivilegedActionException pae) { debug(pae); } catch (MalformedURLException mue) { debug(mue); } for (Iterator it = policyFiles.iterator(); it.hasNext(); ) { try { URL url = (URL) it.next(); parse(url); } catch (IOException ioe) { debug(ioe); } } } public String toString() { return super.toString() + " [ " + cs2pc.toString() + " ]"; } // Own methods. // ------------------------------------------------------------------------- private static final int STATE_BEGIN = 0; private static final int STATE_GRANT = 1; private static final int STATE_PERMS = 2; /** * Parse a policy file, incorporating the permission definitions * described therein. * * @param url The URL of the policy file to read. * @throws IOException if an I/O error occurs, or if the policy file * cannot be parsed. */ private void parse(final URL url) throws IOException { final StreamTokenizer in = new StreamTokenizer(new InputStreamReader(url.openStream())); in.resetSyntax(); in.slashSlashComments(true); in.slashStarComments(true); in.wordChars('A', 'Z'); in.wordChars('a', 'z'); in.wordChars('0', '9'); in.wordChars('.', '.'); in.wordChars('_', '_'); in.wordChars('$', '$'); in.whitespaceChars(' ', ' '); in.whitespaceChars('\t', '\t'); in.whitespaceChars('\f', '\f'); in.whitespaceChars('\n', '\n'); in.whitespaceChars('\r', '\r'); in.quoteChar('\''); in.quoteChar('"'); int tok; int state = STATE_BEGIN; List keystores = new LinkedList(); URL currentBase = null; List currentCerts = new LinkedList(); Permissions currentPerms = new Permissions(); while ((tok = in.nextToken()) != StreamTokenizer.TT_EOF) { switch (tok) { case '{': if (state != STATE_GRANT) error(url, in, "spurious '{'"); state = STATE_PERMS; tok = in.nextToken(); break; case '}': if (state != STATE_PERMS) error(url, in, "spurious '}'"); state = STATE_BEGIN; currentPerms.setReadOnly(); Certificate[] c = null; if (!currentCerts.isEmpty()) c = (Certificate[]) currentCerts.toArray(new Certificate[currentCerts.size()]); cs2pc.put(new CodeSource(currentBase, c), currentPerms); currentCerts.clear(); currentPerms = new Permissions(); currentBase = null; tok = in.nextToken(); if (tok != ';') in.pushBack(); continue; } if (tok != StreamTokenizer.TT_WORD) { error(url, in, "expecting word token"); } // keystore "<keystore-path>" [',' "<keystore-type>"] ';' if (in.sval.equalsIgnoreCase("keystore")) { String alg = KeyStore.getDefaultType(); tok = in.nextToken(); if (tok != '"' && tok != '\'') error(url, in, "expecting key store URL"); String store = in.sval; tok = in.nextToken(); if (tok == ',') { tok = in.nextToken(); if (tok != '"' && tok != '\'') error(url, in, "expecting key store type"); alg = in.sval; tok = in.nextToken(); } if (tok != ';') error(url, in, "expecting semicolon"); try { KeyStore keystore = KeyStore.getInstance(alg); keystore.load(new URL(url, store).openStream(), null); keystores.add(keystore); } catch (Exception x) { error(url, in, x.toString()); } } else if (in.sval.equalsIgnoreCase("grant")) { if (state != STATE_BEGIN) error(url, in, "extraneous grant keyword"); state = STATE_GRANT; } else if (in.sval.equalsIgnoreCase("signedBy")) { if (state != STATE_GRANT && state != STATE_PERMS) error(url, in, "spurious 'signedBy'"); if (keystores.isEmpty()) error(url, in, "'signedBy' with no keystores"); tok = in.nextToken(); if (tok != '"' && tok != '\'') error(url, in, "expecting signedBy name"); StringTokenizer st = new StringTokenizer(in.sval, ","); while (st.hasMoreTokens()) { String alias = st.nextToken(); for (Iterator it = keystores.iterator(); it.hasNext(); ) { KeyStore keystore = (KeyStore) it.next(); try { if (keystore.isCertificateEntry(alias)) currentCerts.add(keystore.getCertificate(alias)); } catch (KeyStoreException kse) { error(url, in, kse.toString()); } } } tok = in.nextToken(); if (tok != ',') { if (state != STATE_GRANT) error(url, in, "spurious ','"); in.pushBack(); } } else if (in.sval.equalsIgnoreCase("codeBase")) { if (state != STATE_GRANT) error(url, in, "spurious 'codeBase'"); tok = in.nextToken(); if (tok != '"' && tok != '\'') error(url, in, "expecting code base URL"); String base = expand(in.sval); if (File.separatorChar != '/') base = base.replace(File.separatorChar, '/'); try { currentBase = new URL(base); } catch (MalformedURLException mue) { error(url, in, mue.toString()); } tok = in.nextToken(); if (tok != ',') in.pushBack(); } else if (in.sval.equalsIgnoreCase("principal")) { if (state != STATE_GRANT) error(url, in, "spurious 'principal'"); tok = in.nextToken(); if (tok == StreamTokenizer.TT_WORD) { tok = in.nextToken(); if (tok != '"' && tok != '\'') error(url, in, "expecting principal name"); String name = in.sval; Principal p = null; try { Class pclass = Class.forName(in.sval); Constructor c = pclass.getConstructor(new Class[] { String.class }); p = (Principal) c.newInstance(new Object[] { name }); } catch (Exception x) { error(url, in, x.toString()); } for (Iterator it = keystores.iterator(); it.hasNext(); ) { KeyStore ks = (KeyStore) it.next(); try { for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) { String alias = (String) e.nextElement(); if (ks.isCertificateEntry(alias)) { Certificate cert = ks.getCertificate(alias); if (!(cert instanceof X509Certificate)) continue; if (p.equals(((X509Certificate) cert).getSubjectDN()) || p.equals(((X509Certificate) cert).getSubjectX500Principal())) currentCerts.add(cert); } } } catch (KeyStoreException kse) { error(url, in, kse.toString()); } } } else if (tok == '"' || tok == '\'') { String alias = in.sval; for (Iterator it = keystores.iterator(); it.hasNext(); ) { KeyStore ks = (KeyStore) it.next(); try { if (ks.isCertificateEntry(alias)) currentCerts.add(ks.getCertificate(alias)); } catch (KeyStoreException kse) { error(url, in, kse.toString()); } } } else error(url, in, "expecting principal"); tok = in.nextToken(); if (tok != ',') in.pushBack(); } else if (in.sval.equalsIgnoreCase("permission")) { if (state != STATE_PERMS) error(url, in, "spurious 'permission'"); tok = in.nextToken(); if (tok != StreamTokenizer.TT_WORD) error(url, in, "expecting permission class name"); String className = in.sval; Class clazz = null; try { clazz = Class.forName(className); } catch (ClassNotFoundException cnfe) { } tok = in.nextToken(); if (tok == ';') { if (clazz == null) { currentPerms.add(new UnresolvedPermission(className, null, null, (Certificate[]) currentCerts.toArray(new Certificate[0]))); continue; } try { currentPerms.add((Permission) clazz.newInstance()); } catch (Exception x) { error(url, in, x.toString()); } continue; } if (tok != '"' && tok != '\'') error(url, in, "expecting permission target"); String target = expand(in.sval); tok = in.nextToken(); if (tok == ';') { if (clazz == null) { currentPerms.add(new UnresolvedPermission(className, target, null, (Certificate[]) currentCerts.toArray(new Certificate[0]))); continue; } try { Constructor c = clazz.getConstructor(new Class[] { String.class }); currentPerms.add((Permission) c.newInstance( new Object[] { target })); } catch (Exception x) { error(url, in, x.toString()); } continue; } if (tok != ',') error(url, in, "expecting ','"); tok = in.nextToken(); if (tok == StreamTokenizer.TT_WORD) { if (!in.sval.equalsIgnoreCase("signedBy")) error(url, in, "expecting 'signedBy'"); try { Constructor c = clazz.getConstructor(new Class[] { String.class }); currentPerms.add((Permission) c.newInstance( new Object[] { target })); } catch (Exception x) { error(url, in, x.toString()); } in.pushBack(); continue; } if (tok != '"' && tok != '\'') error(url, in, "expecting permission action"); String action = in.sval; if (clazz == null) { currentPerms.add(new UnresolvedPermission(className, target, action, (Certificate[]) currentCerts.toArray(new Certificate[0]))); continue; } else { try { Constructor c = clazz.getConstructor( new Class[] { String.class, String.class }); currentPerms.add((Permission) c.newInstance( new Object[] { target, action })); } catch (Exception x) { error(url, in, x.toString()); } } tok = in.nextToken(); if (tok != ';' && tok != ',') error(url, in, "expecting ';' or ','"); } } } /** * Expand all instances of <code>"${property-name}"</code> into * <code>System.getProperty("property-name")</code>. */ private static String expand(final String s) { final StringBuffer result = new StringBuffer(); final StringBuffer prop = new StringBuffer(); int state = 0; for (int i = 0; i < s.length(); i++) { switch (state) { case 0: if (s.charAt(i) == '$') state = 1; else result.append(s.charAt(i)); break; case 1: if (s.charAt(i) == '{') state = 2; else { state = 0; result.append('$').append(s.charAt(i)); } break; case 2: if (s.charAt(i) == '}') { String p = prop.toString(); if (p.equals("/")) p = "file.separator"; p = System.getProperty(p); if (p == null) p = ""; result.append(p); prop.setLength(0); state = 0; } else prop.append(s.charAt(i)); break; } } if (state != 0) result.append('$').append('{').append(prop); return result.toString(); } /** * I miss macros. */ private static void error(URL base, StreamTokenizer in, String msg) throws IOException { throw new IOException(base+":"+in.lineno()+": "+msg); } }
_______________________________________________ Classpath mailing list [EMAIL PROTECTED] http://mail.gnu.org/mailman/listinfo/classpath