source: java/net/deterlab/abac/Context.java @ 388a3d7

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

Add identities() function

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