Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: hk/hku/cecid/phoenix/pki/ApacheXMLDSigner.java


1   /*
2    * Academic Free License
3    * Version 1.0
4    *
5    * This Academic Free License applies to any software and associated 
6    * documentation (the "Software") whose owner (the "Licensor") has placed the 
7    * statement "Licensed under the Academic Free License Version 1.0" immediately 
8    * after the copyright notice that applies to the Software. 
9    *
10   * Permission is hereby granted, free of charge, to any person obtaining a copy 
11   * of the Software (1) to use, copy, modify, merge, publish, perform, 
12   * distribute, sublicense, and/or sell copies of the Software, and to permit 
13   * persons to whom the Software is furnished to do so, and (2) under patent 
14   * claims owned or controlled by the Licensor that are embodied in the Software 
15   * as furnished by the Licensor, to make, use, sell and offer for sale the 
16   * Software and derivative works thereof, subject to the following conditions: 
17   *
18   * - Redistributions of the Software in source code form must retain all 
19   *   copyright notices in the Software as furnished by the Licensor, this list 
20   *   of conditions, and the following disclaimers. 
21   * - Redistributions of the Software in executable form must reproduce all 
22   *   copyright notices in the Software as furnished by the Licensor, this list 
23   *   of conditions, and the following disclaimers in the documentation and/or 
24   *   other materials provided with the distribution. 
25   * - Neither the names of Licensor, nor the names of any contributors to the 
26   *   Software, nor any of their trademarks or service marks, may be used to 
27   *   endorse or promote products derived from this Software without express 
28   *   prior written permission of the Licensor. 
29   *
30   * DISCLAIMERS: LICENSOR WARRANTS THAT THE COPYRIGHT IN AND TO THE SOFTWARE IS 
31   * OWNED BY THE LICENSOR OR THAT THE SOFTWARE IS DISTRIBUTED BY LICENSOR UNDER 
32   * A VALID CURRENT LICENSE. EXCEPT AS EXPRESSLY STATED IN THE IMMEDIATELY 
33   * PRECEDING SENTENCE, THE SOFTWARE IS PROVIDED BY THE LICENSOR, CONTRIBUTORS 
34   * AND COPYRIGHT OWNERS "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
35   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
36   * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 
37   * LICENSOR, CONTRIBUTORS OR COPYRIGHT OWNERS BE LIABLE FOR ANY CLAIM, DAMAGES 
38   * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
39   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE. 
40   *
41   * This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. 
42   * Permission is hereby granted to copy and distribute this license without 
43   * modification. This license may not be modified without the express written 
44   * permission of its copyright owner. 
45   */
46  
47  /* ===== 
48   *
49   * $Header: /ebxml/staff/cecid/cvs_repository/pki/src/hk/hku/cecid/phoenix/pki/ApacheXMLDSigner.java,v 1.8 2002/12/13 03:59:13 kcyee Exp $
50   *
51   * Code authored by:
52   *
53   * kcyee [2002-05-16]
54   *
55   * Code reviewed by:
56   *
57   * username [YYYY-MM-DD]
58   *
59   * Remarks:
60   *
61   * =====
62   */
63  
64  package hk.hku.cecid.phoenix.pki;
65  
66  import java.io.InputStream;
67  import java.io.IOException;
68  import java.security.*;
69  import java.security.cert.*;
70  import java.util.ArrayList;
71  import org.apache.xml.security.exceptions.XMLSecurityException;
72  import org.apache.xml.security.keys.KeyInfo;
73  import org.apache.xml.security.keys.keyresolver.KeyResolverException;
74  import org.apache.xml.security.signature.XMLSignature;
75  import org.apache.xml.security.signature.XMLSignatureException;
76  import org.apache.xml.security.transforms.Transforms;
77  import org.apache.xml.security.transforms.TransformationException;
78  import org.w3c.dom.Document;
79  import org.w3c.dom.Element;
80  import org.w3c.dom.Node;
81  import org.w3c.dom.NodeList;
82  import hk.hku.cecid.phoenix.common.util.Logger;
83  import hk.hku.cecid.phoenix.common.util.Version;
84  
85  /**
86   * This class hides the details for digital signature. The digital signature
87   * routines are provided by the Apache XML Security library. We defined a
88   * standard way to have the document signed as interface. Different classes
89   * will implement the interface using different library behind.
90   *
91   * @author kcyee
92   * @version $Revision: 1.8 $
93   */
94  public class ApacheXMLDSigner implements XMLDSigner {
95  
96      /**
97       * Logger
98       */
99      protected static Logger logger = Logger.getLogger(
100         ApacheXMLDSigner.class.getName());
101 
102     /**
103      * The prefix of XML digital signature element.
104      */
105     protected static final String DSIG_URI = 
106         "http://www.w3.org/2000/09/xmldsig#";
107 
108     static {
109         org.apache.xml.security.Init.init();
110     }
111 
112     /**
113      * Internal variable for holding the envelope of the signature.
114      */
115     protected Document envelope;
116 
117     /**
118      * Internal variable for holding the documents needed to be referred
119      * in the signature.
120      */
121     protected ArrayList documents;
122 
123     /**
124      * Internal variable for holding the trusted anchor for certificate
125      * path verification.
126      */
127     protected CompositeKeyStore trusted;
128 
129     /**
130      * Internal variable of the Apache XML Security library signature object
131      * for doing the actual signing/verifying algorithm.
132      */
133     protected XMLSignature signature;
134 
135     /**
136      * Default constructor to initialize the internal variables.
137      */
138     public ApacheXMLDSigner() {
139         documents = new ArrayList();
140         envelope = null;
141         signature = null;
142         trusted = null;
143     }
144 
145     /**
146      * Set the envelope to host the Signature element. That is the
147      * XML document where the Signature element to be added. The
148      * digital signature here will always be an enveloped signature.
149      * The envelope will be included in the process of signing.
150      *
151      * @param doc the XML document to host the Signature element
152      * @param algo the algorithm used for digital signature. Currently, only 
153      *             two values are tested: <code>dsa-sha1</code> and 
154      *             <code>rsa-sha1</code>.
155      * @throws SignException internal exception when doing initialization
156      *                       on Apache XML Security library
157      */
158     public void setEnvelope(Document doc, String algo) throws SignException {
159         envelope = doc;
160         try {
161             signature = new XMLSignature(envelope, DSIG_URI, DSIG_URI + algo);
162         }
163         catch (XMLSecurityException e) {
164             throw new SignException("Cannot create XMLSignature object:\n"
165                 + e.getMessage());
166         }
167         logger.debug("setEnvelope, using algorithm: " + algo);
168     }
169 
170     /**
171      * Set the envelope to host the Signature element. That is the
172      * XML document where the Signature element to be added. The
173      * digital signature here will always be an enveloped signature.
174      * The envelope will be included in the process of signing.
175      *
176      * @param doc the XML document to host the Signature element
177      * @throws SignException internal exception when doing initialization
178      *                       on Apache XML Security library
179      */
180     public void setEnvelope(Document doc) throws SignException {
181         setEnvelope(doc, "dsa-sha1");
182     }
183 
184     /**
185      * Adds a reference to a document attachment to the signature.
186      *
187      * @param uri the URI of the document attachment
188      * @param is the input stream of the content of the document
189      * @param contentType the content type of the document
190      */
191     public void addDocument(String uri, InputStream is, String contentType) {
192         DocumentDetail dd = new DocumentDetail();
193         dd.uri = uri;
194         dd.stream = is;
195         dd.contentType = contentType;
196         documents.add(dd);
197         logger.debug(
198             "addDocument URI: " + uri + ", contentType: " + contentType);
199     }
200 
201     /**
202      * Signs the envelope and documents by using the specified key 
203      * in the keystore.
204      *
205      * @param ks the keystore holding the key for signing
206      * @param alias the alias of the key for signing
207      * @param password the password for accessing the key for signing
208      * @throws SignException when there is any error in the processing of
209      *                       signing
210      */
211     public void sign(CompositeKeyStore ks, String alias, char[] password)
212             throws SignException {
213         logger.debug("start signing...");
214 
215         if (envelope == null) {
216             throw new SignException("Envelope element not set!");
217         }
218 
219         DocumentDetail[] doc_array = new DocumentDetail[documents.size()];
220         for (int i=0 ; i<doc_array.length ; i++) {
221             doc_array[i] = (DocumentDetail) documents.get(i);
222         }
223         DocumentResolver resolver = new DocumentResolver(doc_array);
224         signature.addResourceResolver(resolver);
225         logger.debug("created DocumentResolver");
226 
227         Transforms transforms = new Transforms(envelope);
228         try {
229             transforms.addTransform(DSIG_URI + "enveloped-signature");
230             Element xpath = envelope.createElementNS(DSIG_URI, "XPath");
231             xpath.setAttribute(
232                 "xmlns:soap-env", "http://schemas.xmlsoap.org/soap/envelope/");
233             xpath.appendChild(envelope.createTextNode(
234                 "not(ancestor-or-self::node()[@soap-env:actor=\""
235                 + "urn:oasis:names:tc:ebxml-msg:actor:nextMSH\"] | "
236                 + "ancestor-or-self::node()[@soap-env:actor=\""
237                 + "http://schemas.xmlsoap.org/soap/actor/next\"])"));
238             xpath.setPrefix("ds");
239             transforms.addTransform(
240                 "http://www.w3.org/TR/1999/REC-xpath-19991116", xpath);
241             // add canonicalization transform
242             transforms.addTransform(
243                 "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
244         }
245         catch (TransformationException e) {
246             throw new SignException("Cannot add tranform:\n" + e.getMessage());
247         }
248         logger.debug("created Transform");
249 
250         try {
251             signature.addDocument("", transforms, DSIG_URI + "sha1");
252         }
253         catch (XMLSignatureException e) {
254             throw new SignException("Cannot add envelope document:\n"
255                 + e.getMessage());
256         }
257         logger.debug("added main document (envelope)");
258 
259         for (int i=0 ; i<documents.size() ; i++) {
260             DocumentDetail dd = (DocumentDetail) documents.get(i);
261             try {
262                 signature.addDocument(dd.uri);
263             }
264             catch (XMLSignatureException e) {
265                 throw new SignException("Cannot add document " + dd.uri + ":\n"
266                     + e.getMessage());
267             }
268         }
269         logger.debug("added " + documents.size() + " attachment documents");
270 
271         java.security.cert.Certificate[] certificates;
272         try {
273             certificates = ks.getCertificateChain(alias);
274             if (certificates == null) {
275                 throw new SignException("Cannot get certificates path - " 
276                     + alias);
277             }
278         }
279         catch (KeyStoreException e) {
280             throw new SignException("Cannot get certificates path - " + alias 
281                 + ":\n" + e.getMessage());
282         }
283         logger.debug("got the certificate chain from keystore");
284 
285         for (int i=0 ; i<certificates.length ; i++) {
286             try {
287                 signature.addKeyInfo((X509Certificate) certificates[i]);
288             }
289             catch (XMLSecurityException e) {
290                 throw new SignException("Cannot add key info:\n"
291                     + e.getMessage());
292             }
293         }
294         logger.debug("added the certificate chain to signature");
295 
296         PrivateKey pk;
297         try {
298             pk = (PrivateKey) ks.getKey(alias, password);
299         }
300         catch (Exception e) {
301             throw new SignException("Cannot get private key - " + alias + ":\n"
302                 + e.getMessage());
303         }
304         logger.debug("got private key from keystore");
305 
306         try {
307             signature.sign(pk);
308         }
309         catch (XMLSignatureException e) {
310             e.printStackTrace();
311             throw new SignException("Cannot sign:\n"
312                 + e.getMessage());
313         }
314         logger.debug("signed");
315     }
316 
317     /**
318      * Sets the trust anchor for verfication of certificate path.
319      *
320      * @param ks the keystore providing the trusted certificates
321      */
322     public void setTrustAnchor(CompositeKeyStore ks) {
323         trusted = ks;
324     }
325 
326     /**
327      * Verifies the signature in the envelope passed in, which may reference
328      * the documents specified using the addDocument method.
329      *
330      * @return true if the signature can be verified successfully, false
331      *         if otherwise.
332      * @throws VerifyException when there is any error in the processing of
333      *                         verification
334      */
335     public boolean verify() throws VerifyException {
336         logger.debug("start verifying...");
337 
338         if (envelope == null) {
339             throw new VerifyException("Envelope element not set!");
340         }
341 
342         NodeList nodeList = envelope.getElementsByTagNameNS(
343             DSIG_URI, "Signature");
344 
345         if (nodeList.getLength() == 0) {
346             throw new VerifyException("No <ds:Signature> found!");
347         }
348         Element signatureElement = (Element) nodeList.item(0);
349         String baseUri = DSIG_URI;
350         logger.debug("got the signature element");
351 
352         try {
353             signature = new XMLSignature(signatureElement, baseUri);
354         }
355         catch (XMLSecurityException e) {
356             throw new VerifyException("Cannot create XMLSignature object:\n"
357                 + e.getMessage());
358         }
359         catch (IOException e) {
360             throw new VerifyException("Cannot create XMLSignature object:\n"
361                 + e.getMessage());
362         }
363         logger.debug("created signature object");
364 
365         DocumentDetail[] doc_array = new DocumentDetail[documents.size()];
366         for (int i=0 ; i<doc_array.length ; i++) {
367             doc_array[i] = (DocumentDetail) documents.get(i);
368         }
369         DocumentResolver resolver = new DocumentResolver(doc_array);
370         signature.addResourceResolver(resolver);
371         logger.debug("created document resolver");
372 
373         PublicKey publicKey = null;
374         KeyInfo keyInfo = signature.getKeyInfo();
375         java.security.cert.Certificate[] certs = null;
376         if (keyInfo != null) {
377             try {
378                 int certPathLen = keyInfo.lengthX509Data();
379                 if (certPathLen > 0) {
380                     certs = new java.security.cert.Certificate[certPathLen];
381                     for (int i=0 ; i<certPathLen ; i++) {
382                         try {
383                             certs[i] = keyInfo.itemX509Data(i)
384                                         .itemCertificate(0)
385                                         .getX509Certificate();
386                         }
387                         catch (XMLSecurityException e) {
388                             throw new VerifyException(
389                                 "Cannot get keys from <ds:KeyInfo>:\n"
390                                 + e.getMessage());
391                         }
392                     }
393                 }
394                 X509Certificate certificate = keyInfo.getX509Certificate();
395                 if (certificate != null) {
396                     publicKey = certificate.getPublicKey();
397                 }
398             }
399             catch (KeyResolverException e) {
400                 throw new VerifyException("Cannot extract key info:\n"
401                     + e.getMessage());
402             }
403         }
404         if (publicKey == null) {
405             throw new VerifyException("No PublicKey can be found!");
406         }
407         logger.debug("got public key and certificate chain from signature");
408 
409         boolean ret = false;
410         try {
411             ret = signature.checkSignatureValue(publicKey);
412         }
413         catch (XMLSignatureException e) {
414             throw new VerifyException("Cannot verify:\n" + e.getMessage());
415         }
416         logger.debug("checked signature value, result: " + ret);
417 
418         double javaVersion = Version.getJDKVersion();
419 
420         if (ret == true && trusted != null && certs != null 
421             && certs.length > 1 && javaVersion >= 1.4) {
422 
423             logger.debug("start verifying cert path...");
424             CertPathVerifier.verify(certs, trusted);
425             logger.debug("verified, result: " + ret);
426         }
427         else {
428             logger.debug("verification of cert path skipped...");
429         }
430 
431         return ret;
432     }
433 
434     /**
435      * Gets the DOM element of the signature generated.
436      *
437      * @return the DOM element of the signature
438      */
439     public Element getElement() {
440         if (signature != null) {
441             return signature.getElement();
442         }
443         else {
444             return null;
445         }
446     }
447 }