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.wss4j.dom.message;
21  
22  import java.security.NoSuchProviderException;
23  import java.security.Provider;
24  import java.util.List;
25  
26  import javax.xml.crypto.XMLStructure;
27  import javax.xml.crypto.dom.DOMStructure;
28  import javax.xml.crypto.dsig.CanonicalizationMethod;
29  import javax.xml.crypto.dsig.SignatureMethod;
30  import javax.xml.crypto.dsig.SignedInfo;
31  import javax.xml.crypto.dsig.XMLSignContext;
32  import javax.xml.crypto.dsig.XMLSignature;
33  import javax.xml.crypto.dsig.XMLSignatureFactory;
34  import javax.xml.crypto.dsig.dom.DOMSignContext;
35  import javax.xml.crypto.dsig.keyinfo.KeyInfo;
36  import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
37  import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
38  import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec;
39  
40  import org.w3c.dom.Document;
41  import org.w3c.dom.Element;
42  
43  import org.apache.wss4j.common.WSEncryptionPart;
44  import org.apache.wss4j.common.derivedKey.ConversationConstants;
45  import org.apache.wss4j.common.ext.WSSecurityException;
46  import org.apache.wss4j.common.token.Reference;
47  import org.apache.wss4j.common.token.SecurityTokenReference;
48  import org.apache.wss4j.common.util.KeyUtils;
49  import org.apache.wss4j.common.util.XMLUtils;
50  import org.apache.wss4j.dom.WSConstants;
51  import org.apache.wss4j.dom.WSDocInfo;
52  import org.apache.wss4j.dom.transform.STRTransform;
53  import org.apache.wss4j.dom.util.WSSecurityUtil;
54  
55  /**
56   * Builder to sign with derived keys
57   */
58  public class WSSecDKSign extends WSSecDerivedKeyBase {
59  
60      private static final org.slf4j.Logger LOG =
61          org.slf4j.LoggerFactory.getLogger(WSSecDKSign.class);
62  
63      private String sigAlgo = WSConstants.HMAC_SHA1;
64      private String digestAlgo = WSConstants.SHA1;
65      private String canonAlgo = WSConstants.C14N_EXCL_OMIT_COMMENTS;
66      private byte[] signatureValue;
67  
68      private String keyInfoUri;
69      private SecurityTokenReference secRef;
70      private String strUri;
71      private WSDocInfo wsDocInfo;
72  
73      private XMLSignatureFactory signatureFactory;
74      private XMLSignature sig;
75      private KeyInfo keyInfo;
76      private CanonicalizationMethod c14nMethod;
77      private int derivedKeyLength = -1;
78      private boolean addInclusivePrefixes = true;
79  
80      public WSSecDKSign(WSSecHeader securityHeader) {
81          super(securityHeader);
82          init(null);
83      }
84  
85      public WSSecDKSign(Document doc) {
86          this(doc, null);
87      }
88  
89      public WSSecDKSign(Document doc, Provider provider) {
90          super(doc);
91          init(provider);
92      }
93  
94      private void init(Provider provider) {
95          if (provider == null) {
96              // Try to install the Santuario Provider - fall back to the JDK provider if this does
97              // not work
98              try {
99                  signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig");
100             } catch (NoSuchProviderException ex) {
101                 signatureFactory = XMLSignatureFactory.getInstance("DOM");
102             }
103         } else {
104             signatureFactory = XMLSignatureFactory.getInstance("DOM", provider);
105         }
106     }
107 
108     public Document build(byte[] ephemeralKey) throws WSSecurityException {
109 
110         prepare(ephemeralKey);
111         if (getParts().isEmpty()) {
112             getParts().add(WSSecurityUtil.getDefaultEncryptionPart(getDocument()));
113         } else {
114             for (WSEncryptionPart part : getParts()) {
115                 if ("STRTransform".equals(part.getName()) && part.getId() == null) {
116                     part.setId(strUri);
117                 }
118             }
119         }
120 
121         List<javax.xml.crypto.dsig.Reference> referenceList = addReferencesToSign(getParts());
122         computeSignature(referenceList);
123 
124         //
125         // prepend elements in the right order to the security header
126         //
127         prependDKElementToHeader();
128 
129         return getDocument();
130     }
131 
132     public void prepare(byte[] ephemeralKey) throws WSSecurityException {
133         super.prepare(ephemeralKey);
134         wsDocInfo = new WSDocInfo(getDocument());
135         sig = null;
136 
137         try {
138             C14NMethodParameterSpec c14nSpec = null;
139             if (addInclusivePrefixes && canonAlgo.equals(WSConstants.C14N_EXCL_OMIT_COMMENTS)) {
140                 Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
141                 List<String> prefixes =
142                     getInclusivePrefixes(securityHeaderElement, false);
143                 c14nSpec = new ExcC14NParameterSpec(prefixes);
144             }
145 
146            c14nMethod = signatureFactory.newCanonicalizationMethod(canonAlgo, c14nSpec);
147         } catch (Exception ex) {
148             LOG.error("", ex);
149             throw new WSSecurityException(
150                 WSSecurityException.ErrorCode.FAILED_SIGNATURE, ex, "noXMLSig"
151             );
152         }
153 
154         keyInfoUri = getIdAllocator().createSecureId("KI-", keyInfo);
155 
156         secRef = new SecurityTokenReference(getDocument());
157         strUri = getIdAllocator().createSecureId("STR-", secRef);
158         secRef.setID(strUri);
159         if (addWSUNamespace) {
160             secRef.addWSUNamespace();
161         }
162 
163         Reference ref = new Reference(getDocument());
164         ref.setURI("#" + getId());
165         String ns =
166             ConversationConstants.getWSCNs(getWscVersion())
167             + ConversationConstants.TOKEN_TYPE_DERIVED_KEY_TOKEN;
168         ref.setValueType(ns);
169         secRef.setReference(ref);
170 
171         XMLStructure structure = new DOMStructure(secRef.getElement());
172         wsDocInfo.addTokenElement(secRef.getElement(), false);
173         KeyInfoFactory keyInfoFactory = signatureFactory.getKeyInfoFactory();
174         keyInfo =
175             keyInfoFactory.newKeyInfo(
176                 java.util.Collections.singletonList(structure), keyInfoUri
177             );
178 
179     }
180 
181     /**
182      * Returns the SignatureElement.
183      * The method can be called any time after <code>prepare()</code>.
184      * @return The DOM Element of the signature.
185      */
186     public Element getSignatureElement() {
187         Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
188         return
189             XMLUtils.getDirectChildElement(
190                 securityHeaderElement, WSConstants.SIG_LN, WSConstants.SIG_NS
191             );
192     }
193 
194     /**
195      * This method adds references to the Signature.
196      *
197      * @param references The list of references to sign
198      * @throws WSSecurityException
199      */
200     public List<javax.xml.crypto.dsig.Reference> addReferencesToSign(
201         List<WSEncryptionPart> references
202     ) throws WSSecurityException {
203         return
204             addReferencesToSign(
205                 getDocument(),
206                 references,
207                 wsDocInfo,
208                 signatureFactory,
209                 addInclusivePrefixes,
210                 digestAlgo
211             );
212     }
213 
214     /**
215      * Compute the Signature over the references.
216      *
217      * After references are set this method computes the Signature for them.
218      * This method can be called any time after the references were set. See
219      * <code>addReferencesToSign()</code>.
220      *
221      * @throws WSSecurityException
222      */
223     public void computeSignature(
224         List<javax.xml.crypto.dsig.Reference> referenceList
225     ) throws WSSecurityException {
226         computeSignature(referenceList, true, null);
227     }
228 
229     /**
230      * Compute the Signature over the references.
231      *
232      * After references are set this method computes the Signature for them.
233      * This method can be called any time after the references were set. See
234      * <code>addReferencesToSign()</code>.
235      *
236      * @throws WSSecurityException
237      */
238     public void computeSignature(
239         List<javax.xml.crypto.dsig.Reference> referenceList,
240         boolean prepend,
241         Element siblingElement
242     ) throws WSSecurityException {
243         try {
244             java.security.Key key = getDerivedKey(sigAlgo);
245             SignatureMethod signatureMethod =
246                 signatureFactory.newSignatureMethod(sigAlgo, null);
247             SignedInfo signedInfo =
248                 signatureFactory.newSignedInfo(c14nMethod, signatureMethod, referenceList);
249 
250             sig = signatureFactory.newXMLSignature(
251                     signedInfo,
252                     keyInfo,
253                     null,
254                     getIdAllocator().createId("SIG-", null),
255                     null);
256 
257             //
258             // Figure out where to insert the signature element
259             //
260             XMLSignContext signContext = null;
261             Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
262             if (prepend) {
263                 if (siblingElement == null) {
264                     siblingElement = (Element)securityHeaderElement.getFirstChild();
265                 }
266                 if (siblingElement == null) {
267                     signContext = new DOMSignContext(key, securityHeaderElement);
268                 } else {
269                     signContext = new DOMSignContext(key, securityHeaderElement, siblingElement);
270                 }
271             } else {
272                 signContext = new DOMSignContext(key, securityHeaderElement);
273             }
274 
275             signContext.putNamespacePrefix(WSConstants.SIG_NS, WSConstants.SIG_PREFIX);
276             if (WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(canonAlgo)) {
277                 signContext.putNamespacePrefix(
278                     WSConstants.C14N_EXCL_OMIT_COMMENTS,
279                     WSConstants.C14N_EXCL_OMIT_COMMENTS_PREFIX
280                 );
281             }
282             signContext.setProperty(STRTransform.TRANSFORM_WS_DOC_INFO, wsDocInfo);
283             wsDocInfo.setCallbackLookup(callbackLookup);
284 
285             // Add the elements to sign to the Signature Context
286             wsDocInfo.setTokensOnContext((DOMSignContext)signContext);
287 
288             sig.sign(signContext);
289 
290             signatureValue = sig.getSignatureValue().getValue();
291         } catch (Exception ex) {
292             LOG.error(ex.getMessage(), ex);
293             throw new WSSecurityException(
294                 WSSecurityException.ErrorCode.FAILED_SIGNATURE, ex
295             );
296         }
297     }
298 
299     protected int getDerivedKeyLength() throws WSSecurityException {
300         return derivedKeyLength > 0 ? derivedKeyLength : KeyUtils.getKeyLength(sigAlgo);
301     }
302 
303     public void setDerivedKeyLength(int keyLength) {
304         derivedKeyLength = keyLength;
305     }
306 
307     /**
308      * Set the signature algorithm to use. The default is WSConstants.SHA1.
309      * @param algorithm the signature algorithm to use.
310      */
311     public void setSignatureAlgorithm(String algorithm) {
312         sigAlgo = algorithm;
313     }
314 
315     /**
316      * @return the signature algorithm to use
317      */
318     public String getSignatureAlgorithm() {
319         return sigAlgo;
320     }
321 
322     /**
323      * Returns the the value of wsu:Id attribute of the Signature element.
324      *
325      * @return Return the wsu:Id of this token or null if the signature has not been generated.
326      */
327     public String getSignatureId() {
328         if (sig == null) {
329             return null;
330         }
331         return sig.getId();
332     }
333 
334     /**
335      * Set the digest algorithm to use. The default is WSConstants.SHA1.
336      * @param algorithm the digest algorithm to use.
337      */
338     public void setDigestAlgorithm(String algorithm) {
339         digestAlgo = algorithm;
340     }
341 
342     /**
343      * @return the digest algorithm to use
344      */
345     public String getDigestAlgorithm() {
346         return digestAlgo;
347     }
348 
349     /**
350      * @return Returns the signatureValue.
351      */
352     public byte[] getSignatureValue() {
353         return signatureValue;
354     }
355 
356     /**
357      * Set the canonicalization method to use.
358      *
359      * If the canonicalization method is not set then the recommended Exclusive
360      * XML Canonicalization is used by default Refer to WSConstants which
361      * algorithms are supported.
362      *
363      * @param algo Is the name of the signature algorithm
364      * @see WSConstants#C14N_OMIT_COMMENTS
365      * @see WSConstants#C14N_WITH_COMMENTS
366      * @see WSConstants#C14N_EXCL_OMIT_COMMENTS
367      * @see WSConstants#C14N_EXCL_WITH_COMMENTS
368      */
369     public void setSigCanonicalization(String algo) {
370         canonAlgo = algo;
371     }
372 
373     /**
374      * Get the canonicalization method.
375      *
376      * If the canonicalization method was not set then Exclusive XML
377      * Canonicalization is used by default.
378      *
379      * @return The string describing the canonicalization algorithm.
380      */
381     public String getSigCanonicalization() {
382         return canonAlgo;
383     }
384 
385     public boolean isAddInclusivePrefixes() {
386         return addInclusivePrefixes;
387     }
388 
389     public void setAddInclusivePrefixes(boolean addInclusivePrefixes) {
390         this.addInclusivePrefixes = addInclusivePrefixes;
391     }
392 }