View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.security.message;
21  
22  import org.apache.ws.security.WSConstants;
23  import org.apache.ws.security.WSDocInfo;
24  import org.apache.ws.security.WSEncryptionPart;
25  import org.apache.ws.security.WSSConfig;
26  import org.apache.ws.security.WSSecurityException;
27  import org.apache.ws.security.conversation.ConversationException;
28  import org.apache.ws.security.message.token.Reference;
29  import org.apache.ws.security.message.token.SecurityTokenReference;
30  import org.apache.ws.security.transform.STRTransform;
31  import org.apache.ws.security.util.WSSecurityUtil;
32  
33  import org.w3c.dom.Document;
34  import org.w3c.dom.Element;
35  
36  import java.security.NoSuchProviderException;
37  import java.util.ArrayList;
38  import java.util.List;
39  
40  import javax.xml.crypto.XMLStructure;
41  import javax.xml.crypto.dom.DOMStructure;
42  import javax.xml.crypto.dsig.CanonicalizationMethod;
43  import javax.xml.crypto.dsig.SignatureMethod;
44  import javax.xml.crypto.dsig.SignedInfo;
45  import javax.xml.crypto.dsig.XMLSignature;
46  import javax.xml.crypto.dsig.XMLSignatureFactory;
47  import javax.xml.crypto.dsig.XMLSignContext;
48  import javax.xml.crypto.dsig.dom.DOMSignContext;
49  import javax.xml.crypto.dsig.keyinfo.KeyInfo;
50  import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
51  import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
52  import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec;
53  
54  /**
55   * Builder to sign with derived keys
56   * 
57   * @author Ruchith Fernando (ruchith.fernando@gmail.com)
58   * @author Davanum Srinivas (dims@yahoo.com)
59   * @author Werner Dittmann (werner@apache.org)
60   */
61  public class WSSecDKSign extends WSSecDerivedKeyBase {
62  
63      private static org.apache.commons.logging.Log log = 
64          org.apache.commons.logging.LogFactory.getLog(WSSecDKSign.class);
65  
66      private String sigAlgo = WSConstants.HMAC_SHA1;
67      private String digestAlgo = WSConstants.SHA1;
68      private String canonAlgo = WSConstants.C14N_EXCL_OMIT_COMMENTS;
69      private byte[] signatureValue = null;
70      
71      private String keyInfoUri = null;
72      private SecurityTokenReference secRef = null;
73      private String strUri = null;
74      private WSDocInfo wsDocInfo;
75      
76      private KeyInfoFactory keyInfoFactory;
77      private XMLSignatureFactory signatureFactory;
78      private XMLSignature sig;
79      private KeyInfo keyInfo;
80      private CanonicalizationMethod c14nMethod;
81      private Element securityHeader = null;
82  
83      public WSSecDKSign() {
84          super();
85          init();
86      }
87      
88      public WSSecDKSign(WSSConfig config) {
89          super(config);
90          init();
91      }
92      
93      private void init() {
94          // Try to install the Santuario Provider - fall back to the JDK provider if this does
95          // not work
96          try {
97              signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig");
98          } catch (NoSuchProviderException ex) {
99              signatureFactory = XMLSignatureFactory.getInstance("DOM");
100         }
101         try {
102             keyInfoFactory = KeyInfoFactory.getInstance("DOM", "ApacheXMLDSig");
103         } catch (NoSuchProviderException ex) {
104             keyInfoFactory = KeyInfoFactory.getInstance("DOM");
105         }
106     }
107     
108     public Document build(Document doc, WSSecHeader secHeader)
109         throws WSSecurityException, ConversationException {
110         
111         prepare(doc, secHeader);
112         String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
113         if (parts == null) {
114             parts = new ArrayList<WSEncryptionPart>(1);
115             WSEncryptionPart encP = 
116                 new WSEncryptionPart(
117                     WSConstants.ELEM_BODY,
118                     soapNamespace, 
119                     "Content"
120                 );
121             parts.add(encP);
122         } else {
123             for (WSEncryptionPart part : parts) {
124                 if ("STRTransform".equals(part.getName()) && part.getId() == null) {
125                     part.setId(strUri);
126                 }
127             }
128         }
129         
130         List<javax.xml.crypto.dsig.Reference> referenceList = 
131             addReferencesToSign(parts, secHeader);
132         computeSignature(referenceList);
133         
134         //
135         // prepend elements in the right order to the security header
136         //
137         prependDKElementToHeader(secHeader);
138 
139         return doc;
140     }
141     
142     public void prepare(Document doc, WSSecHeader secHeader)
143         throws WSSecurityException, ConversationException {
144         super.prepare(doc);
145         wsDocInfo = new WSDocInfo(doc);
146         securityHeader = secHeader.getSecurityHeader();
147         sig = null;
148         
149         try {
150             C14NMethodParameterSpec c14nSpec = null;
151             if (getWsConfig().isWsiBSPCompliant() && canonAlgo.equals(WSConstants.C14N_EXCL_OMIT_COMMENTS)) {
152                 List<String> prefixes = 
153                     getInclusivePrefixes(secHeader.getSecurityHeader(), false);
154                 c14nSpec = new ExcC14NParameterSpec(prefixes);
155             }
156             
157            c14nMethod = signatureFactory.newCanonicalizationMethod(canonAlgo, c14nSpec);
158         } catch (Exception ex) {
159             log.error("", ex);
160             throw new WSSecurityException(
161                 WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null, ex
162             );
163         }
164 
165         keyInfoUri = getWsConfig().getIdAllocator().createSecureId("KI-", keyInfo);
166         
167         secRef = new SecurityTokenReference(doc);
168         strUri = getWsConfig().getIdAllocator().createSecureId("STR-", secRef);
169         secRef.setID(strUri);
170         
171         Reference refUt = new Reference(document);
172         refUt.setURI("#" + dktId);
173         secRef.setReference(refUt);
174         
175         XMLStructure structure = new DOMStructure(secRef.getElement());
176         wsDocInfo.addTokenElement(secRef.getElement(), false);
177         keyInfo = 
178             keyInfoFactory.newKeyInfo(
179                 java.util.Collections.singletonList(structure), keyInfoUri
180             );
181         
182     }
183     
184     /**
185      * Returns the SignatureElement.
186      * The method can be called any time after <code>prepare()</code>.
187      * @return The DOM Element of the signature.
188      */
189     public Element getSignatureElement() {
190         return
191             WSSecurityUtil.getDirectChildElement(
192                 securityHeader,
193                 WSConstants.SIG_LN,
194                 WSConstants.SIG_NS
195             );
196     }
197     
198     /**
199      * This method adds references to the Signature.
200      * 
201      * @param references The list of references to sign
202      * @param secHeader The Security Header
203      * @throws WSSecurityException
204      */
205     public List<javax.xml.crypto.dsig.Reference> addReferencesToSign(
206         List<WSEncryptionPart> references, 
207         WSSecHeader secHeader
208     ) throws WSSecurityException {
209         return 
210             addReferencesToSign(
211                 document, 
212                 references,
213                 wsDocInfo,
214                 signatureFactory, 
215                 secHeader, 
216                 getWsConfig(), 
217                 digestAlgo
218             );
219     }
220     
221     /**
222      * Compute the Signature over the references.
223      * 
224      * After references are set this method computes the Signature for them.
225      * This method can be called any time after the references were set. See
226      * <code>addReferencesToSign()</code>.
227      * 
228      * @throws WSSecurityException
229      */
230     public void computeSignature(
231         List<javax.xml.crypto.dsig.Reference> referenceList
232     ) throws WSSecurityException {
233         computeSignature(referenceList, true, null);
234     }
235     
236     /**
237      * Compute the Signature over the references.
238      * 
239      * After references are set this method computes the Signature for them.
240      * This method can be called any time after the references were set. See
241      * <code>addReferencesToSign()</code>.
242      * 
243      * @throws WSSecurityException
244      */
245     public void computeSignature(
246         List<javax.xml.crypto.dsig.Reference> referenceList, 
247         boolean prepend,
248         Element siblingElement
249     ) throws WSSecurityException {
250         try {
251             java.security.Key key = 
252                 WSSecurityUtil.prepareSecretKey(sigAlgo, derivedKeyBytes);
253             SignatureMethod signatureMethod = 
254                 signatureFactory.newSignatureMethod(sigAlgo, null);
255             SignedInfo signedInfo = 
256                 signatureFactory.newSignedInfo(c14nMethod, signatureMethod, referenceList);
257             
258             sig = signatureFactory.newXMLSignature(
259                     signedInfo, 
260                     keyInfo,
261                     null,
262                     getWsConfig().getIdAllocator().createId("SIG-", null),
263                     null);
264             
265             //
266             // Figure out where to insert the signature element
267             //
268             XMLSignContext signContext = null;
269             if (prepend) {
270                 if (siblingElement == null) {
271                     siblingElement = (Element)securityHeader.getFirstChild();
272                 }
273                 if (siblingElement == null) {
274                     signContext = new DOMSignContext(key, securityHeader);
275                 } else {
276                     signContext = new DOMSignContext(key, securityHeader, siblingElement);
277                 }
278             } else {
279                 signContext = new DOMSignContext(key, securityHeader);
280             }
281             
282             signContext.putNamespacePrefix(WSConstants.SIG_NS, WSConstants.SIG_PREFIX);
283             if (WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(canonAlgo)) {
284                 signContext.putNamespacePrefix(
285                     WSConstants.C14N_EXCL_OMIT_COMMENTS, 
286                     WSConstants.C14N_EXCL_OMIT_COMMENTS_PREFIX
287                 );
288             }
289             signContext.setProperty(STRTransform.TRANSFORM_WS_DOC_INFO, wsDocInfo);
290             wsDocInfo.setCallbackLookup(callbackLookup);
291             
292             // Add the elements to sign to the Signature Context
293             wsDocInfo.setTokensOnContext((DOMSignContext)signContext);
294             if (secRef != null && secRef.getElement() != null) {
295                 WSSecurityUtil.storeElementInContext((DOMSignContext)signContext, secRef.getElement());
296             }
297             
298             sig.sign(signContext);
299             
300             signatureValue = sig.getSignatureValue().getValue();
301         } catch (Exception ex) {
302             log.error(ex);
303             throw new WSSecurityException(
304                 WSSecurityException.FAILED_SIGNATURE, null, null, ex
305             );
306         }
307     }
308     
309     /**
310      * @see org.apache.ws.security.message.WSSecDerivedKeyBase#getDerivedKeyLength()
311      */
312     protected int getDerivedKeyLength() throws WSSecurityException {
313         return (derivedKeyLength > 0) ? derivedKeyLength : 
314             WSSecurityUtil.getKeyLength(sigAlgo);
315     }
316     
317     /**
318      * Set the signature algorithm to use. The default is WSConstants.SHA1.
319      * @param algorithm the signature algorithm to use.
320      */
321     public void setSignatureAlgorithm(String algorithm) {
322         sigAlgo = algorithm;
323     }
324     
325     /**
326      * @return the signature algorithm to use
327      */
328     public String getSignatureAlgorithm() {
329         return sigAlgo;
330     }
331     
332     /**
333      * Returns the the value of wsu:Id attribute of the Signature element.
334      * 
335      * @return Return the wsu:Id of this token or null if the signature has not been generated.
336      */
337     public String getSignatureId() {
338         if (sig == null) {
339             return null;
340         }
341         return sig.getId();
342     }
343     
344     /**
345      * Set the digest algorithm to use. The default is Constants.ALGO_ID_DIGEST_SHA1.
346      * @param algorithm the digest algorithm to use.
347      */
348     public void setDigestAlgorithm(String algorithm) {
349         digestAlgo = algorithm;
350     }
351     
352     /**
353      * @return the digest algorithm to use
354      */
355     public String getDigestAlgorithm() {
356         return digestAlgo;
357     }
358 
359     /**
360      * @return Returns the signatureValue.
361      */
362     public byte[] getSignatureValue() {
363         return signatureValue;
364     }
365     
366     /**
367      * Set the canonicalization method to use.
368      * 
369      * If the canonicalization method is not set then the recommended Exclusive
370      * XML Canonicalization is used by default Refer to WSConstants which
371      * algorithms are supported.
372      * 
373      * @param algo Is the name of the signature algorithm
374      * @see WSConstants#C14N_OMIT_COMMENTS
375      * @see WSConstants#C14N_WITH_COMMENTS
376      * @see WSConstants#C14N_EXCL_OMIT_COMMENTS
377      * @see WSConstants#C14N_EXCL_WITH_COMMENTS
378      */
379     public void setSigCanonicalization(String algo) {
380         canonAlgo = algo;
381     }
382 
383     /**
384      * Get the canonicalization method.
385      * 
386      * If the canonicalization method was not set then Exclusive XML
387      * Canonicalization is used by default.
388      * 
389      * @return The string describing the canonicalization algorithm.
390      */
391     public String getSigCanonicalization() {
392         return canonAlgo;
393     }
394 
395 }