source: java/net/deterlab/abac/Context.java @ 83cdf0f

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

Add constructor from a Credential Collection

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