1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/**
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
21 * under the License.
22 */
23package com.sun.org.apache.xml.internal.security.keys;
24
25import java.security.PrivateKey;
26import java.security.PublicKey;
27import java.security.cert.X509Certificate;
28import java.util.ArrayList;
29import java.util.Iterator;
30import java.util.List;
31
32import javax.crypto.SecretKey;
33
34import com.sun.org.apache.xml.internal.security.encryption.EncryptedKey;
35import com.sun.org.apache.xml.internal.security.encryption.XMLCipher;
36import com.sun.org.apache.xml.internal.security.encryption.XMLEncryptionException;
37import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException;
38import com.sun.org.apache.xml.internal.security.keys.content.DEREncodedKeyValue;
39import com.sun.org.apache.xml.internal.security.keys.content.KeyInfoReference;
40import com.sun.org.apache.xml.internal.security.keys.content.KeyName;
41import com.sun.org.apache.xml.internal.security.keys.content.KeyValue;
42import com.sun.org.apache.xml.internal.security.keys.content.MgmtData;
43import com.sun.org.apache.xml.internal.security.keys.content.PGPData;
44import com.sun.org.apache.xml.internal.security.keys.content.RetrievalMethod;
45import com.sun.org.apache.xml.internal.security.keys.content.SPKIData;
46import com.sun.org.apache.xml.internal.security.keys.content.X509Data;
47import com.sun.org.apache.xml.internal.security.keys.content.keyvalues.DSAKeyValue;
48import com.sun.org.apache.xml.internal.security.keys.content.keyvalues.RSAKeyValue;
49import com.sun.org.apache.xml.internal.security.keys.keyresolver.KeyResolver;
50import com.sun.org.apache.xml.internal.security.keys.keyresolver.KeyResolverException;
51import com.sun.org.apache.xml.internal.security.keys.keyresolver.KeyResolverSpi;
52import com.sun.org.apache.xml.internal.security.keys.storage.StorageResolver;
53import com.sun.org.apache.xml.internal.security.transforms.Transforms;
54import com.sun.org.apache.xml.internal.security.utils.Constants;
55import com.sun.org.apache.xml.internal.security.utils.EncryptionConstants;
56import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy;
57import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
58import org.w3c.dom.Attr;
59import org.w3c.dom.Document;
60import org.w3c.dom.Element;
61import org.w3c.dom.Node;
62import org.w3c.dom.NodeList;
63
64/**
65 * This class stand for KeyInfo Element that may contain keys, names,
66 * certificates and other public key management information,
67 * such as in-band key distribution or key agreement data.
68 * <BR />
69 * KeyInfo Element has two basic functions:
70 * One is KeyResolve for getting the public key in signature validation processing.
71 * the other one is toElement for getting the element in signature generation processing.
72 * <BR />
73 * The <CODE>lengthXXX()</CODE> methods provide access to the internal Key
74 * objects:
75 * <UL>
76 * <LI>If the <CODE>KeyInfo</CODE> was constructed from an Element
77 * (Signature verification), the <CODE>lengthXXX()</CODE> methods searches
78 * for child elements of <CODE>ds:KeyInfo</CODE> for known types. </LI>
79 * <LI>If the <CODE>KeyInfo</CODE> was constructed from scratch (during
80 * Signature generation), the <CODE>lengthXXX()</CODE> methods return the number
81 * of <CODE>XXXs</CODE> objects already passed to the KeyInfo</LI>
82 * </UL>
83 * <BR />
84 * The <CODE>addXXX()</CODE> methods are used for adding Objects of the
85 * appropriate type to the <CODE>KeyInfo</CODE>. This is used during signature
86 * generation.
87 * <BR />
88 * The <CODE>itemXXX(int i)</CODE> methods return the i'th object of the
89 * corresponding type.
90 * <BR />
91 * The <CODE>containsXXX()</CODE> methods return <I>whether</I> the KeyInfo
92 * contains the corresponding type.
93 *
94 */
95public class KeyInfo extends SignatureElementProxy {
96
97    /** {@link org.apache.commons.logging} logging facility */
98    private static java.util.logging.Logger log =
99        java.util.logging.Logger.getLogger(KeyInfo.class.getName());
100
101    // We need at least one StorageResolver otherwise
102    // the KeyResolvers would not be called.
103    // The default StorageResolver is null.
104
105    private List<X509Data> x509Datas = null;
106    private List<EncryptedKey> encryptedKeys = null;
107
108    private static final List<StorageResolver> nullList;
109    static {
110        List<StorageResolver> list = new ArrayList<StorageResolver>(1);
111        list.add(null);
112        nullList = java.util.Collections.unmodifiableList(list);
113    }
114
115    /** Field storageResolvers */
116    private List<StorageResolver> storageResolvers = nullList;
117
118    /**
119     * Stores the individual (per-KeyInfo) {@link KeyResolverSpi}s
120     */
121    private List<KeyResolverSpi> internalKeyResolvers = new ArrayList<KeyResolverSpi>();
122
123    private boolean secureValidation;
124
125    /**
126     * Constructor KeyInfo
127     * @param doc
128     */
129    public KeyInfo(Document doc) {
130        super(doc);
131
132        XMLUtils.addReturnToElement(this.constructionElement);
133    }
134
135    /**
136     * Constructor KeyInfo
137     *
138     * @param element
139     * @param baseURI
140     * @throws XMLSecurityException
141     */
142    public KeyInfo(Element element, String baseURI) throws XMLSecurityException {
143        super(element, baseURI);
144
145        Attr attr = element.getAttributeNodeNS(null, "Id");
146        if (attr != null) {
147            element.setIdAttributeNode(attr, true);
148        }
149    }
150
151    /**
152     * Set whether secure processing is enabled or not. The default is false.
153     */
154    public void setSecureValidation(boolean secureValidation) {
155        this.secureValidation = secureValidation;
156    }
157
158    /**
159     * Sets the <code>Id</code> attribute
160     *
161     * @param Id ID
162     */
163    public void setId(String id) {
164        if (id != null) {
165            this.constructionElement.setAttributeNS(null, Constants._ATT_ID, id);
166            this.constructionElement.setIdAttributeNS(null, Constants._ATT_ID, true);
167        }
168    }
169
170    /**
171     * Returns the <code>Id</code> attribute
172     *
173     * @return the <code>Id</code> attribute
174     */
175    public String getId() {
176        return this.constructionElement.getAttributeNS(null, Constants._ATT_ID);
177    }
178
179    /**
180     * Method addKeyName
181     *
182     * @param keynameString
183     */
184    public void addKeyName(String keynameString) {
185        this.add(new KeyName(this.doc, keynameString));
186    }
187
188    /**
189     * Method add
190     *
191     * @param keyname
192     */
193    public void add(KeyName keyname) {
194        this.constructionElement.appendChild(keyname.getElement());
195        XMLUtils.addReturnToElement(this.constructionElement);
196    }
197
198    /**
199     * Method addKeyValue
200     *
201     * @param pk
202     */
203    public void addKeyValue(PublicKey pk) {
204        this.add(new KeyValue(this.doc, pk));
205    }
206
207    /**
208     * Method addKeyValue
209     *
210     * @param unknownKeyValueElement
211     */
212    public void addKeyValue(Element unknownKeyValueElement) {
213        this.add(new KeyValue(this.doc, unknownKeyValueElement));
214    }
215
216    /**
217     * Method add
218     *
219     * @param dsakeyvalue
220     */
221    public void add(DSAKeyValue dsakeyvalue) {
222        this.add(new KeyValue(this.doc, dsakeyvalue));
223    }
224
225    /**
226     * Method add
227     *
228     * @param rsakeyvalue
229     */
230    public void add(RSAKeyValue rsakeyvalue) {
231        this.add(new KeyValue(this.doc, rsakeyvalue));
232    }
233
234    /**
235     * Method add
236     *
237     * @param pk
238     */
239    public void add(PublicKey pk) {
240        this.add(new KeyValue(this.doc, pk));
241    }
242
243    /**
244     * Method add
245     *
246     * @param keyvalue
247     */
248    public void add(KeyValue keyvalue) {
249        this.constructionElement.appendChild(keyvalue.getElement());
250        XMLUtils.addReturnToElement(this.constructionElement);
251    }
252
253    /**
254     * Method addMgmtData
255     *
256     * @param mgmtdata
257     */
258    public void addMgmtData(String mgmtdata) {
259        this.add(new MgmtData(this.doc, mgmtdata));
260    }
261
262    /**
263     * Method add
264     *
265     * @param mgmtdata
266     */
267    public void add(MgmtData mgmtdata) {
268        this.constructionElement.appendChild(mgmtdata.getElement());
269        XMLUtils.addReturnToElement(this.constructionElement);
270    }
271
272    /**
273     * Method addPGPData
274     *
275     * @param pgpdata
276     */
277    public void add(PGPData pgpdata) {
278        this.constructionElement.appendChild(pgpdata.getElement());
279        XMLUtils.addReturnToElement(this.constructionElement);
280    }
281
282    /**
283     * Method addRetrievalMethod
284     *
285     * @param uri
286     * @param transforms
287     * @param Type
288     */
289    public void addRetrievalMethod(String uri, Transforms transforms, String Type) {
290        this.add(new RetrievalMethod(this.doc, uri, transforms, Type));
291    }
292
293    /**
294     * Method add
295     *
296     * @param retrievalmethod
297     */
298    public void add(RetrievalMethod retrievalmethod) {
299        this.constructionElement.appendChild(retrievalmethod.getElement());
300        XMLUtils.addReturnToElement(this.constructionElement);
301    }
302
303    /**
304     * Method add
305     *
306     * @param spkidata
307     */
308    public void add(SPKIData spkidata) {
309        this.constructionElement.appendChild(spkidata.getElement());
310        XMLUtils.addReturnToElement(this.constructionElement);
311    }
312
313    /**
314     * Method addX509Data
315     *
316     * @param x509data
317     */
318    public void add(X509Data x509data) {
319        if (x509Datas == null) {
320            x509Datas = new ArrayList<X509Data>();
321        }
322        x509Datas.add(x509data);
323        this.constructionElement.appendChild(x509data.getElement());
324        XMLUtils.addReturnToElement(this.constructionElement);
325    }
326
327    /**
328     * Method addEncryptedKey
329     *
330     * @param encryptedKey
331     * @throws XMLEncryptionException
332     */
333
334    public void add(EncryptedKey encryptedKey) throws XMLEncryptionException {
335        if (encryptedKeys == null) {
336            encryptedKeys = new ArrayList<EncryptedKey>();
337        }
338        encryptedKeys.add(encryptedKey);
339        XMLCipher cipher = XMLCipher.getInstance();
340        this.constructionElement.appendChild(cipher.martial(encryptedKey));
341    }
342
343    /**
344     * Method addDEREncodedKeyValue
345     *
346     * @param pk
347     * @throws XMLSecurityException
348     */
349    public void addDEREncodedKeyValue(PublicKey pk) throws XMLSecurityException {
350        this.add(new DEREncodedKeyValue(this.doc, pk));
351    }
352
353    /**
354     * Method add
355     *
356     * @param derEncodedKeyValue
357     */
358    public void add(DEREncodedKeyValue derEncodedKeyValue) {
359        this.constructionElement.appendChild(derEncodedKeyValue.getElement());
360        XMLUtils.addReturnToElement(this.constructionElement);
361    }
362
363    /**
364     * Method addKeyInfoReference
365     *
366     * @param URI
367     * @throws XMLSecurityException
368     */
369    public void addKeyInfoReference(String URI) throws XMLSecurityException {
370        this.add(new KeyInfoReference(this.doc, URI));
371    }
372
373    /**
374     * Method add
375     *
376     * @param keyInfoReference
377     */
378    public void add(KeyInfoReference keyInfoReference) {
379        this.constructionElement.appendChild(keyInfoReference.getElement());
380        XMLUtils.addReturnToElement(this.constructionElement);
381    }
382
383    /**
384     * Method addUnknownElement
385     *
386     * @param element
387     */
388    public void addUnknownElement(Element element) {
389        this.constructionElement.appendChild(element);
390        XMLUtils.addReturnToElement(this.constructionElement);
391    }
392
393    /**
394     * Method lengthKeyName
395     *
396     * @return the number of the KeyName tags
397     */
398    public int lengthKeyName() {
399        return this.length(Constants.SignatureSpecNS, Constants._TAG_KEYNAME);
400    }
401
402    /**
403     * Method lengthKeyValue
404     *
405     *@return the number of the KeyValue tags
406     */
407    public int lengthKeyValue() {
408        return this.length(Constants.SignatureSpecNS, Constants._TAG_KEYVALUE);
409    }
410
411    /**
412     * Method lengthMgmtData
413     *
414     *@return the number of the MgmtData tags
415     */
416    public int lengthMgmtData() {
417        return this.length(Constants.SignatureSpecNS, Constants._TAG_MGMTDATA);
418    }
419
420    /**
421     * Method lengthPGPData
422     *
423     *@return the number of the PGPDat. tags
424     */
425    public int lengthPGPData() {
426        return this.length(Constants.SignatureSpecNS, Constants._TAG_PGPDATA);
427    }
428
429    /**
430     * Method lengthRetrievalMethod
431     *
432     *@return the number of the RetrievalMethod tags
433     */
434    public int lengthRetrievalMethod() {
435        return this.length(Constants.SignatureSpecNS, Constants._TAG_RETRIEVALMETHOD);
436    }
437
438    /**
439     * Method lengthSPKIData
440     *
441     *@return the number of the SPKIData tags
442     */
443    public int lengthSPKIData() {
444        return this.length(Constants.SignatureSpecNS, Constants._TAG_SPKIDATA);
445    }
446
447    /**
448     * Method lengthX509Data
449     *
450     *@return the number of the X509Data tags
451     */
452    public int lengthX509Data() {
453        if (x509Datas != null) {
454            return x509Datas.size();
455        }
456        return this.length(Constants.SignatureSpecNS, Constants._TAG_X509DATA);
457    }
458
459    /**
460     * Method lengthDEREncodedKeyValue
461     *
462     *@return the number of the DEREncodedKeyValue tags
463     */
464    public int lengthDEREncodedKeyValue() {
465        return this.length(Constants.SignatureSpec11NS, Constants._TAG_DERENCODEDKEYVALUE);
466    }
467
468    /**
469     * Method lengthKeyInfoReference
470     *
471     *@return the number of the KeyInfoReference tags
472     */
473    public int lengthKeyInfoReference() {
474        return this.length(Constants.SignatureSpec11NS, Constants._TAG_KEYINFOREFERENCE);
475    }
476
477    /**
478     * Method lengthUnknownElement
479     * NOTE possibly buggy.
480     * @return the number of the UnknownElement tags
481     */
482    public int lengthUnknownElement() {
483        int res = 0;
484        NodeList nl = this.constructionElement.getChildNodes();
485
486        for (int i = 0; i < nl.getLength(); i++) {
487            Node current = nl.item(i);
488
489            /**
490             * $todo$ using this method, we don't see unknown Elements
491             *  from Signature NS; revisit
492             */
493            if ((current.getNodeType() == Node.ELEMENT_NODE)
494                && current.getNamespaceURI().equals(Constants.SignatureSpecNS)) {
495                res++;
496            }
497        }
498
499        return res;
500    }
501
502    /**
503     * Method itemKeyName
504     *
505     * @param i
506     * @return the asked KeyName element, null if the index is too big
507     * @throws XMLSecurityException
508     */
509    public KeyName itemKeyName(int i) throws XMLSecurityException {
510        Element e =
511            XMLUtils.selectDsNode(
512                this.constructionElement.getFirstChild(), Constants._TAG_KEYNAME, i);
513
514        if (e != null) {
515            return new KeyName(e, this.baseURI);
516        }
517        return null;
518    }
519
520    /**
521     * Method itemKeyValue
522     *
523     * @param i
524     * @return the asked KeyValue element, null if the index is too big
525     * @throws XMLSecurityException
526     */
527    public KeyValue itemKeyValue(int i) throws XMLSecurityException {
528        Element e =
529            XMLUtils.selectDsNode(
530                this.constructionElement.getFirstChild(), Constants._TAG_KEYVALUE, i);
531
532        if (e != null) {
533            return new KeyValue(e, this.baseURI);
534        }
535        return null;
536    }
537
538    /**
539     * Method itemMgmtData
540     *
541     * @param i
542     * @return the asked MgmtData element, null if the index is too big
543     * @throws XMLSecurityException
544     */
545    public MgmtData itemMgmtData(int i) throws XMLSecurityException {
546        Element e =
547            XMLUtils.selectDsNode(
548                this.constructionElement.getFirstChild(), Constants._TAG_MGMTDATA, i);
549
550        if (e != null) {
551            return new MgmtData(e, this.baseURI);
552        }
553        return null;
554    }
555
556    /**
557     * Method itemPGPData
558     *
559     * @param i
560     * @return the asked PGPData element, null if the index is too big
561     * @throws XMLSecurityException
562     */
563    public PGPData itemPGPData(int i) throws XMLSecurityException {
564        Element e =
565            XMLUtils.selectDsNode(
566                this.constructionElement.getFirstChild(), Constants._TAG_PGPDATA, i);
567
568        if (e != null) {
569            return new PGPData(e, this.baseURI);
570        }
571        return null;
572    }
573
574    /**
575     * Method itemRetrievalMethod
576     *
577     * @param i
578     *@return the asked RetrievalMethod element, null if the index is too big
579     * @throws XMLSecurityException
580     */
581    public RetrievalMethod itemRetrievalMethod(int i) throws XMLSecurityException {
582        Element e =
583            XMLUtils.selectDsNode(
584                this.constructionElement.getFirstChild(), Constants._TAG_RETRIEVALMETHOD, i);
585
586        if (e != null) {
587            return new RetrievalMethod(e, this.baseURI);
588        }
589        return null;
590    }
591
592    /**
593     * Method itemSPKIData
594     *
595     * @param i
596     * @return the asked SPKIData element, null if the index is too big
597     * @throws XMLSecurityException
598     */
599    public SPKIData itemSPKIData(int i) throws XMLSecurityException {
600        Element e =
601            XMLUtils.selectDsNode(
602                this.constructionElement.getFirstChild(), Constants._TAG_SPKIDATA, i);
603
604        if (e != null) {
605            return new SPKIData(e, this.baseURI);
606        }
607        return null;
608    }
609
610    /**
611     * Method itemX509Data
612     *
613     * @param i
614     * @return the asked X509Data element, null if the index is too big
615     * @throws XMLSecurityException
616     */
617    public X509Data itemX509Data(int i) throws XMLSecurityException {
618        if (x509Datas != null) {
619            return x509Datas.get(i);
620        }
621        Element e =
622            XMLUtils.selectDsNode(
623                this.constructionElement.getFirstChild(), Constants._TAG_X509DATA, i);
624
625        if (e != null) {
626            return new X509Data(e, this.baseURI);
627        }
628        return null;
629    }
630
631    /**
632     * Method itemEncryptedKey
633     *
634     * @param i
635     * @return the asked EncryptedKey element, null if the index is too big
636     * @throws XMLSecurityException
637     */
638    public EncryptedKey itemEncryptedKey(int i) throws XMLSecurityException {
639        if (encryptedKeys != null) {
640            return encryptedKeys.get(i);
641        }
642        Element e =
643            XMLUtils.selectXencNode(
644                this.constructionElement.getFirstChild(), EncryptionConstants._TAG_ENCRYPTEDKEY, i);
645
646        if (e != null) {
647            XMLCipher cipher = XMLCipher.getInstance();
648            cipher.init(XMLCipher.UNWRAP_MODE, null);
649            return cipher.loadEncryptedKey(e);
650        }
651        return null;
652    }
653
654    /**
655     * Method itemDEREncodedKeyValue
656     *
657     * @param i
658     * @return the asked DEREncodedKeyValue element, null if the index is too big
659     * @throws XMLSecurityException
660     */
661    public DEREncodedKeyValue itemDEREncodedKeyValue(int i) throws XMLSecurityException {
662        Element e =
663            XMLUtils.selectDs11Node(
664                this.constructionElement.getFirstChild(), Constants._TAG_DERENCODEDKEYVALUE, i);
665
666        if (e != null) {
667            return new DEREncodedKeyValue(e, this.baseURI);
668        }
669        return null;
670    }
671
672    /**
673     * Method itemKeyInfoReference
674     *
675     * @param i
676     * @return the asked KeyInfoReference element, null if the index is too big
677     * @throws XMLSecurityException
678     */
679    public KeyInfoReference itemKeyInfoReference(int i) throws XMLSecurityException {
680        Element e =
681            XMLUtils.selectDs11Node(
682                this.constructionElement.getFirstChild(), Constants._TAG_KEYINFOREFERENCE, i);
683
684        if (e != null) {
685            return new KeyInfoReference(e, this.baseURI);
686        }
687        return null;
688    }
689
690    /**
691     * Method itemUnknownElement
692     *
693     * @param i index
694     * @return the element number of the unknown elements
695     */
696    public Element itemUnknownElement(int i) {
697        NodeList nl = this.constructionElement.getChildNodes();
698        int res = 0;
699
700        for (int j = 0; j < nl.getLength(); j++) {
701            Node current = nl.item(j);
702
703            /**
704             * $todo$ using this method, we don't see unknown Elements
705             *  from Signature NS; revisit
706             */
707            if ((current.getNodeType() == Node.ELEMENT_NODE)
708                && current.getNamespaceURI().equals(Constants.SignatureSpecNS)) {
709                res++;
710
711                if (res == i) {
712                    return (Element) current;
713                }
714            }
715        }
716
717        return null;
718    }
719
720    /**
721     * Method isEmpty
722     *
723     * @return true if the element has no descendants.
724     */
725    public boolean isEmpty() {
726        return this.constructionElement.getFirstChild() == null;
727    }
728
729    /**
730     * Method containsKeyName
731     *
732     * @return If the KeyInfo contains a KeyName node
733     */
734    public boolean containsKeyName() {
735        return this.lengthKeyName() > 0;
736    }
737
738    /**
739     * Method containsKeyValue
740     *
741     * @return If the KeyInfo contains a KeyValue node
742     */
743    public boolean containsKeyValue() {
744        return this.lengthKeyValue() > 0;
745    }
746
747    /**
748     * Method containsMgmtData
749     *
750     * @return If the KeyInfo contains a MgmtData node
751     */
752    public boolean containsMgmtData() {
753        return this.lengthMgmtData() > 0;
754    }
755
756    /**
757     * Method containsPGPData
758     *
759     * @return If the KeyInfo contains a PGPData node
760     */
761    public boolean containsPGPData() {
762        return this.lengthPGPData() > 0;
763    }
764
765    /**
766     * Method containsRetrievalMethod
767     *
768     * @return If the KeyInfo contains a RetrievalMethod node
769     */
770    public boolean containsRetrievalMethod() {
771        return this.lengthRetrievalMethod() > 0;
772    }
773
774    /**
775     * Method containsSPKIData
776     *
777     * @return If the KeyInfo contains a SPKIData node
778     */
779    public boolean containsSPKIData() {
780        return this.lengthSPKIData() > 0;
781    }
782
783    /**
784     * Method containsUnknownElement
785     *
786     * @return If the KeyInfo contains a UnknownElement node
787     */
788    public boolean containsUnknownElement() {
789        return this.lengthUnknownElement() > 0;
790    }
791
792    /**
793     * Method containsX509Data
794     *
795     * @return If the KeyInfo contains a X509Data node
796     */
797    public boolean containsX509Data() {
798        return this.lengthX509Data() > 0;
799    }
800
801    /**
802     * Method containsDEREncodedKeyValue
803     *
804     * @return If the KeyInfo contains a DEREncodedKeyValue node
805     */
806    public boolean containsDEREncodedKeyValue() {
807        return this.lengthDEREncodedKeyValue() > 0;
808    }
809
810    /**
811     * Method containsKeyInfoReference
812     *
813     * @return If the KeyInfo contains a KeyInfoReference node
814     */
815    public boolean containsKeyInfoReference() {
816        return this.lengthKeyInfoReference() > 0;
817    }
818
819    /**
820     * This method returns the public key.
821     *
822     * @return If the KeyInfo contains a PublicKey node
823     * @throws KeyResolverException
824     */
825    public PublicKey getPublicKey() throws KeyResolverException {
826        PublicKey pk = this.getPublicKeyFromInternalResolvers();
827
828        if (pk != null) {
829            if (log.isLoggable(java.util.logging.Level.FINE)) {
830                log.log(java.util.logging.Level.FINE, "I could find a key using the per-KeyInfo key resolvers");
831            }
832
833            return pk;
834        }
835        if (log.isLoggable(java.util.logging.Level.FINE)) {
836            log.log(java.util.logging.Level.FINE, "I couldn't find a key using the per-KeyInfo key resolvers");
837        }
838
839        pk = this.getPublicKeyFromStaticResolvers();
840
841        if (pk != null) {
842            if (log.isLoggable(java.util.logging.Level.FINE)) {
843                log.log(java.util.logging.Level.FINE, "I could find a key using the system-wide key resolvers");
844            }
845
846            return pk;
847        }
848        if (log.isLoggable(java.util.logging.Level.FINE)) {
849            log.log(java.util.logging.Level.FINE, "I couldn't find a key using the system-wide key resolvers");
850        }
851
852        return null;
853    }
854
855    /**
856     * Searches the library wide KeyResolvers for public keys
857     *
858     * @return The public key contained in this Node.
859     * @throws KeyResolverException
860     */
861    PublicKey getPublicKeyFromStaticResolvers() throws KeyResolverException {
862        Iterator<KeyResolverSpi> it = KeyResolver.iterator();
863        while (it.hasNext()) {
864            KeyResolverSpi keyResolver = it.next();
865            keyResolver.setSecureValidation(secureValidation);
866            Node currentChild = this.constructionElement.getFirstChild();
867            String uri = this.getBaseURI();
868            while (currentChild != null) {
869                if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
870                    for (StorageResolver storage : storageResolvers) {
871                        PublicKey pk =
872                            keyResolver.engineLookupAndResolvePublicKey(
873                                (Element) currentChild, uri, storage
874                            );
875
876                        if (pk != null) {
877                            return pk;
878                        }
879                    }
880                }
881                currentChild = currentChild.getNextSibling();
882            }
883        }
884        return null;
885    }
886
887    /**
888     * Searches the per-KeyInfo KeyResolvers for public keys
889     *
890     * @return The public key contained in this Node.
891     * @throws KeyResolverException
892     */
893    PublicKey getPublicKeyFromInternalResolvers() throws KeyResolverException {
894        for (KeyResolverSpi keyResolver : internalKeyResolvers) {
895            if (log.isLoggable(java.util.logging.Level.FINE)) {
896                log.log(java.util.logging.Level.FINE, "Try " + keyResolver.getClass().getName());
897            }
898            keyResolver.setSecureValidation(secureValidation);
899            Node currentChild = this.constructionElement.getFirstChild();
900            String uri = this.getBaseURI();
901            while (currentChild != null)      {
902                if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
903                    for (StorageResolver storage : storageResolvers) {
904                        PublicKey pk =
905                            keyResolver.engineLookupAndResolvePublicKey(
906                                (Element) currentChild, uri, storage
907                            );
908
909                        if (pk != null) {
910                            return pk;
911                        }
912                    }
913                }
914                currentChild = currentChild.getNextSibling();
915            }
916        }
917
918        return null;
919    }
920
921    /**
922     * Method getX509Certificate
923     *
924     * @return The certificate contained in this KeyInfo
925     * @throws KeyResolverException
926     */
927    public X509Certificate getX509Certificate() throws KeyResolverException {
928        // First search using the individual resolvers from the user
929        X509Certificate cert = this.getX509CertificateFromInternalResolvers();
930
931        if (cert != null) {
932            if (log.isLoggable(java.util.logging.Level.FINE)) {
933                log.log(java.util.logging.Level.FINE, "I could find a X509Certificate using the per-KeyInfo key resolvers");
934            }
935
936            return cert;
937        }
938        if (log.isLoggable(java.util.logging.Level.FINE)) {
939            log.log(java.util.logging.Level.FINE, "I couldn't find a X509Certificate using the per-KeyInfo key resolvers");
940        }
941
942        // Then use the system-wide Resolvers
943        cert = this.getX509CertificateFromStaticResolvers();
944
945        if (cert != null) {
946            if (log.isLoggable(java.util.logging.Level.FINE)) {
947                log.log(java.util.logging.Level.FINE, "I could find a X509Certificate using the system-wide key resolvers");
948            }
949
950            return cert;
951        }
952        if (log.isLoggable(java.util.logging.Level.FINE)) {
953            log.log(java.util.logging.Level.FINE, "I couldn't find a X509Certificate using the system-wide key resolvers");
954        }
955
956        return null;
957    }
958
959    /**
960     * This method uses each System-wide {@link KeyResolver} to search the
961     * child elements. Each combination of {@link KeyResolver} and child element
962     * is checked against all {@link StorageResolver}s.
963     *
964     * @return The certificate contained in this KeyInfo
965     * @throws KeyResolverException
966     */
967    X509Certificate getX509CertificateFromStaticResolvers()
968        throws KeyResolverException {
969        if (log.isLoggable(java.util.logging.Level.FINE)) {
970            log.log(java.util.logging.Level.FINE,
971                "Start getX509CertificateFromStaticResolvers() with " + KeyResolver.length()
972                + " resolvers"
973            );
974        }
975        String uri = this.getBaseURI();
976        Iterator<KeyResolverSpi> it = KeyResolver.iterator();
977        while (it.hasNext()) {
978            KeyResolverSpi keyResolver = it.next();
979            keyResolver.setSecureValidation(secureValidation);
980            X509Certificate cert = applyCurrentResolver(uri, keyResolver);
981            if (cert != null) {
982                return cert;
983            }
984        }
985        return null;
986    }
987
988    private X509Certificate applyCurrentResolver(
989        String uri, KeyResolverSpi keyResolver
990    ) throws KeyResolverException {
991        Node currentChild = this.constructionElement.getFirstChild();
992        while (currentChild != null)      {
993            if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
994                for (StorageResolver storage : storageResolvers) {
995                    X509Certificate cert =
996                        keyResolver.engineLookupResolveX509Certificate(
997                            (Element) currentChild, uri, storage
998                        );
999
1000                    if (cert != null) {
1001                        return cert;
1002                    }
1003                }
1004            }
1005            currentChild = currentChild.getNextSibling();
1006        }
1007        return null;
1008    }
1009
1010    /**
1011     * Method getX509CertificateFromInternalResolvers
1012     *
1013     * @return The certificate contained in this KeyInfo
1014     * @throws KeyResolverException
1015     */
1016    X509Certificate getX509CertificateFromInternalResolvers()
1017        throws KeyResolverException {
1018        if (log.isLoggable(java.util.logging.Level.FINE)) {
1019            log.log(java.util.logging.Level.FINE,
1020                "Start getX509CertificateFromInternalResolvers() with "
1021                + this.lengthInternalKeyResolver() + " resolvers"
1022            );
1023        }
1024        String uri = this.getBaseURI();
1025        for (KeyResolverSpi keyResolver : internalKeyResolvers) {
1026            if (log.isLoggable(java.util.logging.Level.FINE)) {
1027                log.log(java.util.logging.Level.FINE, "Try " + keyResolver.getClass().getName());
1028            }
1029            keyResolver.setSecureValidation(secureValidation);
1030            X509Certificate cert = applyCurrentResolver(uri, keyResolver);
1031            if (cert != null) {
1032                return cert;
1033            }
1034        }
1035
1036        return null;
1037    }
1038
1039    /**
1040     * This method returns a secret (symmetric) key. This is for XML Encryption.
1041     * @return the secret key contained in this KeyInfo
1042     * @throws KeyResolverException
1043     */
1044    public SecretKey getSecretKey() throws KeyResolverException {
1045        SecretKey sk = this.getSecretKeyFromInternalResolvers();
1046
1047        if (sk != null) {
1048            if (log.isLoggable(java.util.logging.Level.FINE)) {
1049                log.log(java.util.logging.Level.FINE, "I could find a secret key using the per-KeyInfo key resolvers");
1050            }
1051
1052            return sk;
1053        }
1054        if (log.isLoggable(java.util.logging.Level.FINE)) {
1055            log.log(java.util.logging.Level.FINE, "I couldn't find a secret key using the per-KeyInfo key resolvers");
1056        }
1057
1058        sk = this.getSecretKeyFromStaticResolvers();
1059
1060        if (sk != null) {
1061            if (log.isLoggable(java.util.logging.Level.FINE)) {
1062                log.log(java.util.logging.Level.FINE, "I could find a secret key using the system-wide key resolvers");
1063            }
1064
1065            return sk;
1066        }
1067        if (log.isLoggable(java.util.logging.Level.FINE)) {
1068            log.log(java.util.logging.Level.FINE, "I couldn't find a secret key using the system-wide key resolvers");
1069        }
1070
1071        return null;
1072    }
1073
1074    /**
1075     * Searches the library wide KeyResolvers for Secret keys
1076     *
1077     * @return the secret key contained in this KeyInfo
1078     * @throws KeyResolverException
1079     */
1080    SecretKey getSecretKeyFromStaticResolvers() throws KeyResolverException {
1081        Iterator<KeyResolverSpi> it = KeyResolver.iterator();
1082        while (it.hasNext()) {
1083            KeyResolverSpi keyResolver = it.next();
1084            keyResolver.setSecureValidation(secureValidation);
1085
1086            Node currentChild = this.constructionElement.getFirstChild();
1087            String uri = this.getBaseURI();
1088            while (currentChild != null)      {
1089                if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
1090                    for (StorageResolver storage : storageResolvers) {
1091                        SecretKey sk =
1092                            keyResolver.engineLookupAndResolveSecretKey(
1093                                (Element) currentChild, uri, storage
1094                            );
1095
1096                        if (sk != null) {
1097                            return sk;
1098                        }
1099                    }
1100                }
1101                currentChild = currentChild.getNextSibling();
1102            }
1103        }
1104        return null;
1105    }
1106
1107    /**
1108     * Searches the per-KeyInfo KeyResolvers for secret keys
1109     *
1110     * @return the secret key contained in this KeyInfo
1111     * @throws KeyResolverException
1112     */
1113
1114    SecretKey getSecretKeyFromInternalResolvers() throws KeyResolverException {
1115        for (KeyResolverSpi keyResolver : internalKeyResolvers) {
1116            if (log.isLoggable(java.util.logging.Level.FINE)) {
1117                log.log(java.util.logging.Level.FINE, "Try " + keyResolver.getClass().getName());
1118            }
1119            keyResolver.setSecureValidation(secureValidation);
1120            Node currentChild = this.constructionElement.getFirstChild();
1121            String uri = this.getBaseURI();
1122            while (currentChild != null)      {
1123                if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
1124                    for (StorageResolver storage : storageResolvers) {
1125                        SecretKey sk =
1126                            keyResolver.engineLookupAndResolveSecretKey(
1127                                (Element) currentChild, uri, storage
1128                            );
1129
1130                        if (sk != null) {
1131                            return sk;
1132                        }
1133                    }
1134                }
1135                currentChild = currentChild.getNextSibling();
1136            }
1137        }
1138
1139        return null;
1140    }
1141
1142    /**
1143     * This method returns a private key. This is for Key Transport in XML Encryption.
1144     * @return the private key contained in this KeyInfo
1145     * @throws KeyResolverException
1146     */
1147    public PrivateKey getPrivateKey() throws KeyResolverException {
1148        PrivateKey pk = this.getPrivateKeyFromInternalResolvers();
1149
1150        if (pk != null) {
1151            if (log.isLoggable(java.util.logging.Level.FINE)) {
1152                log.log(java.util.logging.Level.FINE, "I could find a private key using the per-KeyInfo key resolvers");
1153            }
1154            return pk;
1155        }
1156        if (log.isLoggable(java.util.logging.Level.FINE)) {
1157            log.log(java.util.logging.Level.FINE, "I couldn't find a secret key using the per-KeyInfo key resolvers");
1158        }
1159
1160        pk = this.getPrivateKeyFromStaticResolvers();
1161        if (pk != null) {
1162            if (log.isLoggable(java.util.logging.Level.FINE)) {
1163                log.log(java.util.logging.Level.FINE, "I could find a private key using the system-wide key resolvers");
1164            }
1165            return pk;
1166        }
1167        if (log.isLoggable(java.util.logging.Level.FINE)) {
1168            log.log(java.util.logging.Level.FINE, "I couldn't find a private key using the system-wide key resolvers");
1169        }
1170
1171        return null;
1172    }
1173
1174    /**
1175     * Searches the library wide KeyResolvers for Private keys
1176     *
1177     * @return the private key contained in this KeyInfo
1178     * @throws KeyResolverException
1179     */
1180    PrivateKey getPrivateKeyFromStaticResolvers() throws KeyResolverException {
1181        Iterator<KeyResolverSpi> it = KeyResolver.iterator();
1182        while (it.hasNext()) {
1183            KeyResolverSpi keyResolver = it.next();
1184            keyResolver.setSecureValidation(secureValidation);
1185
1186            Node currentChild = this.constructionElement.getFirstChild();
1187            String uri = this.getBaseURI();
1188            while (currentChild != null)      {
1189                if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
1190                    // not using StorageResolvers at the moment
1191                    // since they cannot return private keys
1192                    PrivateKey pk =
1193                        keyResolver.engineLookupAndResolvePrivateKey(
1194                            (Element) currentChild, uri, null
1195                        );
1196
1197                    if (pk != null) {
1198                        return pk;
1199                    }
1200                }
1201                currentChild = currentChild.getNextSibling();
1202            }
1203        }
1204        return null;
1205    }
1206
1207    /**
1208     * Searches the per-KeyInfo KeyResolvers for private keys
1209     *
1210     * @return the private key contained in this KeyInfo
1211     * @throws KeyResolverException
1212     */
1213    PrivateKey getPrivateKeyFromInternalResolvers() throws KeyResolverException {
1214        for (KeyResolverSpi keyResolver : internalKeyResolvers) {
1215            if (log.isLoggable(java.util.logging.Level.FINE)) {
1216                log.log(java.util.logging.Level.FINE, "Try " + keyResolver.getClass().getName());
1217            }
1218            keyResolver.setSecureValidation(secureValidation);
1219            Node currentChild = this.constructionElement.getFirstChild();
1220            String uri = this.getBaseURI();
1221            while (currentChild != null) {
1222                if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
1223                    // not using StorageResolvers at the moment
1224                    // since they cannot return private keys
1225                    PrivateKey pk =
1226                        keyResolver.engineLookupAndResolvePrivateKey(
1227                            (Element) currentChild, uri, null
1228                        );
1229
1230                    if (pk != null) {
1231                        return pk;
1232                    }
1233                }
1234                currentChild = currentChild.getNextSibling();
1235            }
1236        }
1237
1238        return null;
1239    }
1240
1241    /**
1242     * This method is used to add a custom {@link KeyResolverSpi} to a KeyInfo
1243     * object.
1244     *
1245     * @param realKeyResolver
1246     */
1247    public void registerInternalKeyResolver(KeyResolverSpi realKeyResolver) {
1248        this.internalKeyResolvers.add(realKeyResolver);
1249    }
1250
1251    /**
1252     * Method lengthInternalKeyResolver
1253     * @return the length of the key
1254     */
1255    int lengthInternalKeyResolver() {
1256        return this.internalKeyResolvers.size();
1257    }
1258
1259    /**
1260     * Method itemInternalKeyResolver
1261     *
1262     * @param i the index
1263     * @return the KeyResolverSpi for the index.
1264     */
1265    KeyResolverSpi itemInternalKeyResolver(int i) {
1266        return this.internalKeyResolvers.get(i);
1267    }
1268
1269    /**
1270     * Method addStorageResolver
1271     *
1272     * @param storageResolver
1273     */
1274    public void addStorageResolver(StorageResolver storageResolver) {
1275        if (storageResolvers == nullList) {
1276            // Replace the default null StorageResolver
1277            storageResolvers = new ArrayList<StorageResolver>();
1278        }
1279        this.storageResolvers.add(storageResolver);
1280    }
1281
1282
1283    /** @inheritDoc */
1284    public String getBaseLocalName() {
1285        return Constants._TAG_KEYINFO;
1286    }
1287}
1288