source: java/net/deterlab/abac/Context.java @ 238717d

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 238717d was 238717d, checked in by Ted Faber <faber@…>, 13 years ago

Auto-load the BouncyCastle? provider. (Makes jnlp work)

  • Property mode set to 100644
File size: 27.4 KB
Line 
1package net.deterlab.abac;
2
3import edu.uci.ics.jung.graph.*;
4import edu.uci.ics.jung.graph.util.*;
5
6import java.io.*;
7import java.util.*;
8import java.util.zip.*;
9import java.security.*;
10import java.security.cert.*;
11
12import org.bouncycastle.asn1.*;
13import org.bouncycastle.asn1.x509.*;
14import org.bouncycastle.x509.*;
15import org.bouncycastle.openssl.*;
16import org.bouncycastle.jce.provider.BouncyCastleProvider;
17
18/**
19 * Represents a global graph of credentials in the form of principals and
20 * attributes.  Contains the identities and credentials that can be used in a
21 * proof.
22 * @author <a href="http://abac.deterlab.net">ISI ABAC team</a>
23 * @version 1.3
24 */
25public class Context {
26    /** Certificate imported successfully */
27    public static final int ABAC_CERT_SUCCESS = 0;
28    /** Certificate import failed, invalid certificate */
29    public static final int ABAC_CERT_INVALID = -1;
30    /** Certificate import failed, signature filed */
31    public static final int ABAC_CERT_BAD_SIG = -2;
32    /** Certificate import failed, unknown issuer */
33    public static final int ABAC_CERT_MISSING_ISSUER = -3;
34
35    /** Internal graph representation */
36    protected Graph<Role,Credential> g;
37    /** Set of edges in the graph that were added by the logic.  */
38    protected Set<Credential> derived_edges;
39    /** Internal persistent query object */
40    protected Query pq;
41    /** True when the graph has been changed since the last set of implied
42     * edges were calculated. */
43    protected boolean dirty;
44    /** Set of identities known to this Context. */
45    protected Set<Identity> m_identities;
46
47    /** Translation from issuer CN to issuer pubkey identifier */
48    protected Map<String, String> nicknames;
49    /** Translation from issuer pubkey identifier to issuer CN */
50    protected Map<String, String> keys;
51
52    /** True once BouncyCastle has been loaded. */
53    static boolean providerLoaded = false;
54
55    /**
56     * Load the BouncyCastle provider, necessary for ABAC crypto (shouldn't
57     * need to be called directly).  This is called from the Context static
58     * constructor and static constructors in other ABAC classes that use
59     * BouncyCastle crypto (which loads a Context, which calls it as well) to
60     * make sure crypto is available. 
61     */
62    static void loadBouncyCastle() {
63        if ( !providerLoaded ) {
64            AccessController.doPrivileged(new PrivilegedAction<Object>() {
65                public Object run() {
66                    Security.addProvider(new BouncyCastleProvider());
67                    return null;
68                }
69            });
70            providerLoaded = true;
71        }
72    }
73
74    /** Load the BouncyCastle provider. */
75    static { loadBouncyCastle(); };
76
77    /**
78     * The result of a query on this context.  The credentials form a partial
79     * or total proof, and success indicates whether the proof succeeded.
80     * @author <a href="http://abac.deterlab.net">ISI ABAC team</a>
81     * @version 1.3
82     */
83    public class QueryResult {
84        /** Credentials returned */
85        protected Collection<Credential> creds;
86        /** True if the proof succeeded. */
87        protected boolean success;
88
89        /**
90         * Construct a result from components.
91         * @param c the collection of credentials in the proof
92         * @param s a boolean, true if the query succeeded.
93         */
94        QueryResult(Collection<Credential> c, boolean s) {
95            creds = c;
96            success = s;
97        }
98
99        /**
100         * Empty constructor
101         */
102        public QueryResult() { 
103            creds = new TreeSet<Credential>();
104            success = false;
105        }
106
107        /**
108         * Return the credentials in the proof.
109         * @return the collection of credentials
110         */
111        public Collection<Credential> getCredentials() { return creds; }
112        /**
113         * Return the success in the proof.
114         * @return the boolean, true on success
115         */
116        public boolean getSuccess() { return success; }
117    }
118
119
120    /**
121     * Create an empty Context.
122     */
123    public Context() {
124        /* create the graph */
125        g = Graphs.<Role,Credential>synchronizedDirectedGraph(
126                new DirectedSparseGraph<Role,Credential>());
127        derived_edges = new HashSet<Credential>();
128        pq = new Query(g);
129        dirty = false;
130        m_identities = new TreeSet<Identity>();
131        nicknames = new TreeMap<String, String>();
132        keys = new TreeMap<String, String>();
133    }
134
135    /**
136     * Create a context from another context.
137     * @param c the Context to copy
138     */
139    public Context(Context c) {
140        this();
141        for (Identity i: c.m_identities) 
142            load_id_chunk(i);
143        for (Credential cr: c.credentials()) 
144            load_attribute_chunk(cr);
145        derive_implied_edges();
146    }
147
148    /**
149     * Create a Context from a collection of Credentials.  A jabac extension.
150     * @param creds the collection of credentials
151     */
152    public Context(Collection<Credential> creds) {
153        this();
154        for (Credential c: creds) {
155            Identity i = c.issuer();
156
157            if (i != null ) load_id_chunk(i);
158            load_attribute_chunk(c);
159        }
160    }
161
162    /**
163     * Load an Identity from a file.
164     * @param fn a String containing the file name.
165     * @return one of the static int return codes.
166     */
167    public int load_id_file(String fn) { return load_id_chunk(new File(fn)); }
168    /**
169     * Load an Identity from a file.
170     * @param fn a File containing the file name.
171     * @return one of the static int return codes.
172     */
173    public int load_id_file(File fn) { return load_id_chunk(fn); }
174    /**
175     * Load an Identity from an object.  Supported objects are an Identity, a
176     * String, a File, or a java.security.cert.X509Certificate.  A string
177     * creates an new identity, and the others are derived from the contents of
178     * the data or file.
179     * @param c an object convertable to an identity as above.
180     * @return one of the static int return codes.
181     */
182    public int load_id_chunk(Object c) {
183        try {
184            if (c instanceof Identity)
185                addIdentity((Identity) c);
186            else if (c instanceof String) 
187                addIdentity(new Identity((String) c));
188            else if (c instanceof File) 
189                addIdentity(new Identity((File) c));
190            else if (c instanceof X509Certificate) 
191                addIdentity(new Identity((X509Certificate) c));
192            else 
193                return ABAC_CERT_INVALID;
194        }
195        catch (SignatureException sig) {
196            return ABAC_CERT_BAD_SIG;
197        }
198        catch (Exception e) {
199            return ABAC_CERT_INVALID;
200        }
201        return ABAC_CERT_SUCCESS;
202    }
203
204    /**
205     * Load an attribute certificate from a file.
206     * @param fn a String containing the file name.
207     * @return one of the static int return codes.
208     */
209    public int load_attribute_file(String fn) { 
210        return load_attribute_chunk(new File(fn));
211    }
212
213    /**
214     * Load an attribute certificate from a file.
215     * @param fn a File containing the file name.
216     * @return one of the static int return codes.
217     */
218    public int load_attribute_file(File fn) { return load_attribute_chunk(fn); }
219
220    /**
221     * Load an Identity from an object.  Supported objects are a Credential, a
222     * String, a File, or an org.bouncycastle.x509.X509V2AttributeCertificate.
223     * A string creates an new Credential, and the others are derived from the
224     * contents of the data or file.
225     * @param c an object convertable to a Credential as above.
226     * @return one of the static int return codes.
227     */
228    public int load_attribute_chunk(Object c) {
229        try {
230            if (c instanceof Credential)
231                add_credential((Credential) c);
232            else if (c instanceof String) 
233                add_credential(new Credential((String) c, m_identities));
234            else if (c instanceof File) 
235                add_credential(new Credential((File) c, m_identities));
236            else if ( c instanceof X509V2AttributeCertificate) 
237                add_credential(new Credential((X509V2AttributeCertificate)c,
238                            m_identities));
239            else 
240                return ABAC_CERT_INVALID;
241        }
242        catch (InvalidKeyException sig) {
243            return ABAC_CERT_MISSING_ISSUER ;
244        }
245        catch (SignatureException sig) {
246            return ABAC_CERT_BAD_SIG;
247        }
248        catch (Exception e) {
249            return ABAC_CERT_INVALID;
250        }
251        return ABAC_CERT_SUCCESS;
252    }
253
254    /**
255     * Determine if prinicpal possesses role in the current context.  If so,
256     * return a proof of that, otherwise return a partial proof of it.
257     * @param role a String encoding the role to check for.
258     * @param principal a String with the principal ID in it.
259     * @return a Context.QueryResult containing the result.
260     */
261    public QueryResult query(String role, String principal) {
262        derive_implied_edges();
263
264        Query q = new Query(g);
265        Graph<Role, Credential> rg = q.run(role, principal);
266
267        return new QueryResult(rg.getEdges(), q.successful());
268    }
269
270    /**
271     * Return a collection of the credentials in the graph.s
272     * @return a collection of the credentials in the graph.
273     */
274    public Collection<Credential> credentials() {
275        Collection<Credential> creds = new HashSet<Credential>();
276
277        // only return creds with a cert: all others are derived edges
278        for (Credential cred : g.getEdges())
279            if (cred.cert() != null)
280                creds.add(cred);
281
282        return creds;
283    }
284
285    /**
286     * Return all the Identities known in this context.  A jabac extension.
287     * @return all the Identities known in this context.
288     */
289    public Collection<Identity> identities() {
290        return m_identities;
291    }
292
293    /**
294     * Returns true if the given Identity is known in this Context.  A jabac
295     * extension.
296     * @param i the Identity to look for
297     * @return a boolean, true if the Identity is known.
298     */
299    public boolean knowsIdentity(Identity i) { return m_identities.contains(i);}
300    /**
301     * Returns true if an Identity with the given string representation is
302     * known in this Context.  A jabac extension.
303     * @param k the string representing the Identity to look for
304     * @return a boolean, true if the Identity is known.
305     */
306    public boolean knowsKeyID(String k) {
307        boolean known = false;
308        for (Identity i: m_identities)
309            if (k.equals(i.getKeyID())) return true;
310        return false;
311    }
312
313
314    /**
315     * Add a credential to the graph.
316     * @param cred the Credential to add
317     */
318    protected void add_credential(Credential cred) {
319        Role tail = cred.tail();
320        Role head = cred.head();
321
322        // explicitly add the vertices, to avoid a null pointer exception
323        if ( !g.containsVertex(head)) 
324            g.addVertex(head);
325        if ( !g.containsVertex(tail)) 
326            g.addVertex(tail);
327
328        if (!g.containsEdge(cred))
329            g.addEdge(cred, tail, head);
330
331        // add the prereqs of an intersection to the graph
332        if (tail.is_intersection())
333            for (Role prereq : tail.prereqs())
334                g.addVertex(prereq);
335
336        dirty = true;
337    }
338
339    /**
340     * Remove a credential from the graph.
341     * @param cred the Credential to remove
342     */
343    protected void remove_credential(Credential cred) {
344        if (g.containsEdge(cred))
345            g.removeEdge(cred);
346        dirty = true;
347    }
348
349    /**
350     * Add a role w/o an edge
351     * @param v the Role to add
352     */
353    protected void add_vertex(Role v) {
354        if (!g.containsVertex(v)) {
355            g.addVertex(v);
356            dirty = true;
357        }
358    }
359
360    /**
361     * Remove a role and connected edges.
362     * @param v the Role to remove
363     */
364    protected void remove_vertex(Role v) {
365        if (g.containsVertex(v)) {
366            g.removeVertex(v);
367            dirty = true;
368        }
369    }
370
371    /**
372     * Derive the implied edges in the graph, according to RT0 derivation rules.
373     * They are added to this graph. See "Distributed Credential Chain Discovery
374     * in Trust Management" by Ninghui Li et al. for details. Note that a
375     * derived linking edge can imply a new intersection edge and vice versa.
376     * Therefore we iteratively derive edges, giving up when an iteration
377     * produces 0 new edges.
378     */
379    protected synchronized void derive_implied_edges() {
380        // nothing to do on a clean graph
381        if (!dirty)
382            return;
383
384        clear_old_edges();
385
386        // iteratively derive links. continue as long as new links are added
387        while (derive_links_iter() > 0)
388            ;
389        dirty = false;
390    }
391
392    /**
393     * Single iteration of deriving implied edges. Returns the number of new
394     * links added.
395     * @return the number of new links added.
396     */
397    protected int derive_links_iter() {
398        int count = 0;
399
400        /* for every node in the graph.. */
401        for (Role vertex : g.getVertices()) {
402            if (vertex.is_intersection()) {
403                // for each prereq edge:
404                //     find set of principals that have the prereq
405                // find the intersection of all sets (i.e., principals
406                //     that satisfy all prereqs)
407                // for each principal in intersection:
408                //     add derived edge
409
410                Set<Role> principals = null;
411
412                for (Role prereq : vertex.prereqs()) {
413                    Set<Role> cur_principals = pq.find_principals(prereq);
414
415                    if (principals == null)
416                        principals = cur_principals;
417                    else
418                        // no, they couldn't just call it "intersection"
419                        principals.retainAll(cur_principals);
420
421                    if (principals.size() == 0)
422                        break;
423                }
424
425                // add em
426                for (Role principal : principals)
427                    if (add_derived_edge(vertex, principal))
428                        ++count;
429            }
430
431            else if (vertex.is_linking()) {
432                // make the rest of the code a bit clearer
433                Role A_r1_r2 = vertex;
434
435                Role A_r1 = new Role(A_r1_r2.A_r1());
436                String r2 = A_r1_r2.r2();
437
438                /* locate the node A.r1 */
439                if (!g.containsVertex(A_r1)) continue; 
440
441                /* for each B that satisfies A_r1 */
442                for (Role principal : pq.find_principals(A_r1)) {
443                    Role B_r2 = new Role(principal + "." + r2);
444                    if (!g.containsVertex(B_r2)) continue;
445
446                    if (add_derived_edge(A_r1_r2, B_r2))
447                        ++count;
448                }
449            }
450        }
451
452        return count;
453    }
454
455    /**
456     * Add a derived edge in the graph. Returns true only if the edge does not
457     * exist.
458     * @return a boolean, true if an edge has been added
459     */
460    protected boolean add_derived_edge(Role head, Role tail) {
461        // edge exists: return false
462        if (g.findEdge(tail, head) != null)
463            return false;
464
465        // add the new edge
466        Credential derived_edge = new Credential(head, tail);
467        derived_edges.add(derived_edge);
468        g.addEdge(derived_edge, tail, head);
469
470        return true;
471    }
472
473    /**
474     * Clear the derived edges that currently exist in the graph. This is done
475     * before the edges are rederived. The derived edges in filtered graphs are
476     * also cleared.
477     */
478    protected void clear_old_edges() { 
479        for (Credential i: derived_edges) 
480            g.removeEdge(i);
481        derived_edges = new HashSet<Credential>();
482    }
483    /**
484     * Put the Identity into the set of ids used to validate certificates.
485     * Also put the keyID and name into the translation mappings used by Roles
486     * to pretty print.  In the role mapping, if multiple ids use the same
487     * common name they are disambiguated.  Only one entry for keyid is
488     * allowed.
489     * @param id the Identity to add
490     */
491    protected void addIdentity(Identity id) { 
492        m_identities.add(id);
493        if (id.getName() != null && id.getKeyID() != null) {
494            if ( !keys.containsKey(id.getKeyID()) ) {
495                String name = id.getName();
496                int n= 1;
497
498                while (nicknames.containsKey(name)) {
499                    name = id.getName() + n++;
500                }
501                nicknames.put(name, id.getKeyID());
502                keys.put(id.getKeyID(), name);
503            }
504        }
505    }
506    /**
507     * Translate either keys to nicknames or vice versa.  Break the string into
508     * space separated tokens and then each of them into period separated
509     * strings.  If any of the smallest strings is in the map, replace it with
510     * the value.
511     * @param is the string to manipulate
512     * @param m the Map containing translations
513     * @return the string after modification
514     */
515    protected String replace(String is, Map<String, String> m) {
516        String rv = "";
517        for (String tok: is.split(" ")) {
518            String term = "";
519            for (String s: tok.split("\\.")) {
520                String next = m.containsKey(s) ? m.get(s) : s;
521
522                if (term.isEmpty()) term = next;
523                else term += "." + next;
524            }
525            if (rv.isEmpty()) rv = term;
526            else rv += " " + term;
527        }
528        return rv;
529    }
530
531    /**
532     * Expand menmonic names in a Role string, e.g. the CN of the issuer
533     * certificate, into the full key ID.  Used internally by Roles to provide
534     * transparent use of mnemonics
535     * @param s the string to expand
536     * @return the String after expansion.
537     */
538    String expandKeyID(String s) { return replace(s, nicknames); }
539    /**
540     * Convert key IDs to  menmonic names in a Role string.  The inverse of
541     * expandKeyID.
542     * @param s the string to expand
543     * @return the String after expansion.
544     */
545    String expandNickname(String s) { return replace(s, keys); }
546
547    /**
548     * Import a zip file.  First import all the identities
549     * (pem), then the credentials (der) into the credential graph then any
550     * alias files into the two maps.  If keys is not null, any key pairs in
551     * PEM files are put in there.  If errors is not null, errors reading files
552     * are added indexed by filename.  This is a jabac extension.
553     * @param zf the File to read
554     * @param keys a Collection into which to insert unmatched keys
555     * @param errors a Map from entry name to generated exception
556     * @throws IOException if the file is unreadable.  Per entry exceptions are
557     *                     returned in the errors parameter.
558     */
559    public void load_zip_file(File zf, Collection<KeyPair> keys, 
560            Map<String, Exception> errors) throws IOException {
561        Vector<ZipEntry> derEntries = new Vector<ZipEntry>();
562        Map<String, Identity> ids = new TreeMap<String, Identity>();
563        Map<String, KeyPair> kps = new TreeMap<String, KeyPair>();
564
565        ZipFile z = new ZipFile(zf);
566
567        for (Enumeration<? extends ZipEntry> ze = z.entries(); 
568                ze.hasMoreElements();) {
569            ZipEntry  f = ze.nextElement();
570            try {
571                PEMReader r = new PEMReader(
572                        new InputStreamReader(z.getInputStream(f)));
573                Object o = readPEM(r);
574
575                if ( o != null ) {
576                    if (o instanceof Identity) {
577                        Identity i = (Identity) o;
578                        String kid = i.getKeyID();
579
580                        if (kps.containsKey(kid) ) {
581                            i.setKeyPair(kps.get(kid));
582                            kps.remove(kid);
583                        }
584                        else if (i.getKeyPair() == null ) 
585                            ids.put(i.getKeyID(), i);
586
587                        load_id_chunk(i);
588                    }
589                    else if (o instanceof KeyPair ) {
590                        KeyPair kp = (KeyPair) o;
591                        String kid = extractKeyID(kp.getPublic());
592
593                        if (ids.containsKey(kid)) {
594                            Identity i = ids.get(kid);
595
596                            i.setKeyPair(kp);
597                            ids.remove(kid);
598                        }
599                        else {
600                            kps.put(kid, kp);
601                        }
602                    }
603                }
604                else {
605                    // Not a PEM file
606                    derEntries.add(f);
607                    continue;
608                }
609            }
610            catch (Exception e ) {
611                if (errors != null ) errors.put(f.getName(), e);
612            }
613        }
614
615        for ( ZipEntry f : derEntries ) {
616            try {
617                add_credential(new Credential(z.getInputStream(f),
618                            m_identities));
619            }
620            catch (Exception e ) {
621                if (errors != null ) errors.put(f.getName(), e);
622            }
623        }
624    }
625    /**
626     * Equivalent to load_zip_file(d, null, null).
627     * @param d the File to read
628     * @throws IOException if the file is unreadable. To see per-entry
629     *                      exceptions use a signature with the errors parameter
630     */
631    public void load_zip_file(File d) 
632            throws IOException {
633        load_zip_file(d, null, null);
634    }
635    /**
636     * Equivalent to load_zip_file(d, null, errors).
637     * @param d the File to read
638     * @param errors a Map from entry name to generated exception
639     * @throws IOException if the file is unreadable.  Per entry exceptions are
640     *                     returned in the errors parameter.
641     */
642    public void load_zip_file(File d, 
643            Map<String, Exception> errors) throws IOException {
644        load_zip_file(d, null, errors);
645    }
646    /**
647     * Equivalent to load_zip_file(d, keys, null).
648     * @param d the File to read
649     * @param keys a Collection into which to insert unmatched keys
650     * @throws IOException if the file is unreadable. To see per-entry
651     *                      exceptions use a signature with the errors parameter
652     */
653    public void load_zip_file(File d, 
654            Collection<KeyPair> keys) throws IOException {
655        load_zip_file(d, keys, null);
656    }
657
658    /**
659     * Read a PEM file that contains an X509 Certificate, a key pair, or both.
660     * If a cert is present it is converted into an Identity.  A key pair is
661     * returned as a java.security.KeyPair and both are returned as an Identity
662     * with an associated key pair.
663     * @param r a PEMReader from which to read
664     * @return an object encoding the contents (as above)
665     * @throws IOException for an unreadable or badly formated input
666     */
667    protected Object readPEM(PEMReader r) throws IOException {
668        Identity i = null;
669        KeyPair keys = null;
670        Object o = null;
671
672        while ( (o = r.readObject()) != null ) {
673            if (o instanceof X509Certificate) {
674                if ( i == null ) {
675                    try {
676                        i = new Identity((X509Certificate)o);
677                    }
678                    catch (Exception e) {
679                        // Translate Idenitiy exceptions to IOException
680                        throw new IOException(e);
681                    }
682                    if (keys != null ) {
683                        i.setKeyPair(keys);
684                        keys = null;
685                    }
686                }
687                else throw new IOException("Two certificates");
688            }
689            else if (o instanceof KeyPair ) {
690                if ( i != null ) i.setKeyPair((KeyPair) o);
691                else keys = (KeyPair) o;
692            }
693            else {
694                throw new IOException("Unexpected PEM object: " + 
695                        o.getClass().getName());
696            }
697        }
698
699        if ( i != null ) return i;
700        else if ( keys != null) return keys;
701        else return null;
702    }
703
704    /**
705     * Import a directory full of files.  First import all the identities
706     * (pem), then the credentials (der) into the credential graph then any
707     * alias files into the two maps.  If keys is not null, any key pairs in
708     * PEM files are put in there.  If errors is not null, errors reading files
709     * are added indexed by filename.  This behaves slightly differently from
710     * the load_directory description in the general libabac documentation.
711     * @param d the File to read.  If it is a directory its contents are read
712     * @param keys a Collection into which to insert unmatched keys
713     * @param errors a Map from entry name to generated exception
714     * @throws IOException if the file is unreadable.  Per file exceptions are
715     *                     returned in the errors parameter.
716     */
717    public void load_directory(File d, Collection<KeyPair> keys, 
718            Map<String, Exception> errors) {
719        Vector<File> derFiles = new Vector<File>();
720        Collection<File> files = new Vector<File>();
721        Map<String, Identity> ids = new TreeMap<String, Identity>();
722        Map<String, KeyPair> kps = new TreeMap<String, KeyPair>();
723
724        if (d.isDirectory() ) 
725            for (File f : d.listFiles()) 
726                files.add(f);
727        else files.add(d);
728
729        for (File f: files ) {
730            try {
731                PEMReader r = new PEMReader(new FileReader(f));
732                Object o = readPEM(r);
733
734                if ( o != null ) {
735                    if (o instanceof Identity) {
736                        Identity i = (Identity) o;
737                        String kid = i.getKeyID();
738
739                        if (kps.containsKey(kid) ) {
740                            i.setKeyPair(kps.get(kid));
741                            kps.remove(kid);
742                        }
743                        else if (i.getKeyPair() == null ) 
744                            ids.put(i.getKeyID(), i);
745
746                        load_id_chunk(i);
747                    }
748                    else if (o instanceof KeyPair ) {
749                        KeyPair kp = (KeyPair) o;
750                        String kid = extractKeyID(kp.getPublic());
751
752                        if (ids.containsKey(kid)) {
753                            Identity i = ids.get(kid);
754
755                            i.setKeyPair(kp);
756                            ids.remove(kid);
757                        }
758                        else {
759                            kps.put(kid, kp);
760                        }
761                    }
762                }
763                else {
764                    // Not a PEM file
765                    derFiles.add(f);
766                    continue;
767                }
768            }
769            catch (Exception e ) {
770                if (errors != null ) errors.put(f.getName(), e);
771            }
772        }
773
774        for ( File f : derFiles ) {
775            try {
776                add_credential(new Credential(f, m_identities));
777            }
778            catch (Exception e ) {
779                if (errors != null ) errors.put(f.getName(), e);
780            }
781        }
782    }
783    /**
784     * Equivalent to load_directory(d, null, null).
785     * @param d the File to read.  If it is a directory its contents are read
786     * @throws IOException if the file is unreadable.  To see per-file
787     *                     exceptions use a signature with the errors parameter.
788     */
789    public void load_directory(File d) {
790        load_directory(d, null, null);
791    }
792    /**
793     * Equivalent to load_directory(d, null, null).
794     * @param d the File to read.  If it is a directory its contents are read
795     * @param errors a Map from entry name to generated exception
796     * @throws IOException if the file is unreadable.  Per file exceptions are
797     *                     returned in the errors parameter.
798     */
799    public void load_directory(File d, Map<String, Exception> errors) {
800        load_directory(d, null, errors);
801    }
802    /**
803     * Equivalent to load_directory(d, null, null).
804     * @param d the File to read.  If it is a directory its contents are read
805     * @param keys a Collection into which to insert unmatched keys
806     * @throws IOException if the file is unreadable.  To see per-file
807     *                     exceptions use a signature with the errors parameter.
808     */
809    public void load_directory(File d, Collection<KeyPair> keys) {
810        load_directory(d, keys, null);
811    }
812
813    /**
814     * Write the certificates that make up the context as a zip file, with an
815     * entry for each credential or identity.  The files are all zipped in a
816     * directory derived from the filename.
817     * @param f the File to write
818     * @param allIDs a boolean, if true write certificates for all Identities,
819     * whether used in signing a credential or not.
820     * @param withPrivateKeys a boolean, if true write the Identities as PEM
821     * file containing both the certificate and the private keys.
822     * @throws IOException if there is a problem writing the file.
823     */
824    public void write_zip_file(File f, boolean allIDs, boolean withPrivateKeys) 
825            throws IOException {
826        ZipOutputStream z = new ZipOutputStream(new FileOutputStream(f));
827        Set<Identity> ids = allIDs ?  m_identities : new TreeSet<Identity>();
828        String baseDir = f.getName();
829        int idx = baseDir.indexOf('.');
830
831
832        if (idx != -1) 
833            baseDir = baseDir.substring(0, idx);
834
835        int n = 0;
836        for (Credential c: credentials()) {
837            z.putNextEntry(new ZipEntry(baseDir + File.separator + 
838                        "attr" + n++  + ".der"));
839            c.write(z);
840            z.closeEntry();
841            if ( c.issuer() != null && !allIDs) ids.add(c.issuer());
842        }
843        for (Identity i: ids) {
844            z.putNextEntry(new ZipEntry(baseDir + File.separator + 
845                        i.getName() + ".pem"));
846            i.write(z);
847            if (withPrivateKeys)
848                i.writePrivateKey(z);
849            z.closeEntry();
850        }
851        z.close();
852    }
853
854    /**
855     * Get to the SHA1 hash of the key.  Used by Roles and Identities to get a
856     * key ID.
857     * @param k the PublicKey to get the ID from.
858     * @return a String with the key identifier
859     */
860    static String extractKeyID(PublicKey k) {
861        SubjectPublicKeyInfo ki = extractSubjectPublicKeyInfo(k);
862        SubjectKeyIdentifier id = 
863            SubjectKeyIdentifier.createSHA1KeyIdentifier(ki);
864
865        // Now format it into a string for keeps
866        Formatter fmt = new Formatter(new StringWriter());
867        for (byte b: id.getKeyIdentifier())
868            fmt.format("%02x", b);
869        return fmt.out().toString();
870    }
871
872    /**
873     * Extratct the SubjectPublicKeyInfo.  Useful for some other encryptions,
874     * notably Certificate.make_cert().
875     * @param k the PublicKey to get the ID from.
876     * @return a String with the key identifier
877     */
878    static SubjectPublicKeyInfo extractSubjectPublicKeyInfo(
879            PublicKey k) {
880        ASN1Sequence seq = null;
881        try {
882            seq = (ASN1Sequence) new ASN1InputStream(
883                    k.getEncoded()).readObject();
884        }
885        catch (IOException ie) {
886            // Badly formatted key??
887            return null;
888        }
889        return new SubjectPublicKeyInfo(seq);
890    }
891
892
893}
Note: See TracBrowser for help on using the repository browser.