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.security.cert.CertificateEncodingException;
25  import java.security.cert.X509Certificate;
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.List;
29  
30  import javax.xml.crypto.XMLStructure;
31  import javax.xml.crypto.dom.DOMStructure;
32  import javax.xml.crypto.dsig.CanonicalizationMethod;
33  import javax.xml.crypto.dsig.SignatureMethod;
34  import javax.xml.crypto.dsig.SignedInfo;
35  import javax.xml.crypto.dsig.XMLSignContext;
36  import javax.xml.crypto.dsig.XMLSignature;
37  import javax.xml.crypto.dsig.XMLSignatureFactory;
38  import javax.xml.crypto.dsig.dom.DOMSignContext;
39  import javax.xml.crypto.dsig.keyinfo.KeyInfo;
40  import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
41  import javax.xml.crypto.dsig.keyinfo.KeyValue;
42  import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
43  import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec;
44  
45  import org.apache.wss4j.common.WSEncryptionPart;
46  import org.apache.wss4j.common.WSS4JConstants;
47  import org.apache.wss4j.common.crypto.Crypto;
48  import org.apache.wss4j.common.crypto.CryptoType;
49  import org.apache.wss4j.common.ext.WSSecurityException;
50  import org.apache.wss4j.common.token.BinarySecurity;
51  import org.apache.wss4j.common.token.DOMX509Data;
52  import org.apache.wss4j.common.token.DOMX509IssuerSerial;
53  import org.apache.wss4j.common.token.PKIPathSecurity;
54  import org.apache.wss4j.common.token.Reference;
55  import org.apache.wss4j.common.token.SecurityTokenReference;
56  import org.apache.wss4j.common.token.X509Security;
57  import org.apache.wss4j.common.util.AttachmentUtils;
58  import org.apache.wss4j.common.util.KeyUtils;
59  import org.apache.wss4j.common.util.XMLUtils;
60  import org.apache.wss4j.dom.WSConstants;
61  import org.apache.wss4j.dom.WSDocInfo;
62  import org.apache.wss4j.dom.message.token.KerberosSecurity;
63  import org.apache.wss4j.dom.transform.STRTransform;
64  import org.apache.wss4j.dom.util.WSSecurityUtil;
65  import org.w3c.dom.Document;
66  import org.w3c.dom.Element;
67  import org.w3c.dom.Node;
68  
69  
70  /**
71   * Creates a Signature according to WS Specification, X509 profile.
72   *
73   * This class is a re-factored implementation of the previous WSS4J class
74   * <code>WSSignEnvelope</code>. This new class allows better control of
75   * the process to create a Signature and to add it to the Security header.
76   *
77   * The flexibility and fine granular control is required to implement a handler
78   * that uses WSSecurityPolicy files to control the setup of a Security header.
79   */
80  public class WSSecSignature extends WSSecSignatureBase {
81  
82      private static final org.slf4j.Logger LOG =
83          org.slf4j.LoggerFactory.getLogger(WSSecSignature.class);
84  
85      protected XMLSignatureFactory signatureFactory;
86      protected KeyInfo keyInfo;
87      protected CanonicalizationMethod c14nMethod;
88      protected XMLSignature sig;
89      protected byte[] secretKey;
90      protected String strUri;
91      protected Element bstToken;
92      protected String keyInfoUri;
93      protected String certUri;
94      protected byte[] signatureValue;
95  
96      private boolean useSingleCert = true;
97      private String sigAlgo;
98      private String canonAlgo = WSConstants.C14N_EXCL_OMIT_COMMENTS;
99      private SecurityTokenReference secRef;
100     private String customTokenValueType;
101     private String customTokenId;
102     private String encrKeySha1value;
103     private Crypto crypto;
104     private String digestAlgo = WSConstants.SHA1;
105     private X509Certificate useThisCert;
106     private boolean useCustomSecRef;
107     private boolean bstAddedToSecurityHeader;
108     private boolean includeSignatureToken;
109     private boolean addInclusivePrefixes = true;
110     private Element customKeyInfoElement;
111     private Provider signatureProvider;
112 
113     public WSSecSignature(WSSecHeader securityHeader) {
114         this(securityHeader, null);
115     }
116 
117     public WSSecSignature(WSSecHeader securityHeader, Provider provider) {
118         super(securityHeader);
119         init(provider);
120     }
121 
122     public WSSecSignature(Document doc) {
123         this(doc, null);
124     }
125 
126     public WSSecSignature(Document doc, Provider provider) {
127         super(doc);
128         init(provider);
129     }
130 
131     private void init(Provider provider) {
132         if (provider == null) {
133             // Try to install the Santuario Provider - fall back to the JDK provider if this does
134             // not work
135             try {
136                 signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig");
137             } catch (NoSuchProviderException ex) {
138                 signatureFactory = XMLSignatureFactory.getInstance("DOM");
139             }
140         } else {
141             signatureFactory = XMLSignatureFactory.getInstance("DOM", provider);
142         }
143     }
144 
145     /**
146      * Initialize a WSSec Signature.
147      *
148      * The method sets up and initializes a WSSec Signature structure after the
149      * relevant information was set. After setup of the references to elements
150      * to sign may be added. After all references are added they can be signed.
151      *
152      * This method does not add the Signature element to the security header.
153      * See <code>prependSignatureElementToHeader()</code> method.
154      *
155      * @param cr An instance of the Crypto API to handle keystore and certificates
156      * @throws WSSecurityException
157      */
158     public void prepare(Crypto cr)
159         throws WSSecurityException {
160         //
161         // Gather some info about the document to process and store it for
162         // retrieval
163         //
164         crypto = cr;
165         WSDocInfo wsDocInfo = getWsDocInfo();
166         if (wsDocInfo == null) {
167             wsDocInfo = new WSDocInfo(getDocument());
168             super.setWsDocInfo(wsDocInfo);
169         }
170         wsDocInfo.setCrypto(cr);
171 
172         //
173         // At first get the security token (certificate) according to the parameters.
174         //
175         X509Certificate[] certs = getSigningCerts();
176 
177         try {
178             C14NMethodParameterSpec c14nSpec = null;
179             if (addInclusivePrefixes && canonAlgo.equals(WSConstants.C14N_EXCL_OMIT_COMMENTS)) {
180                 Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
181                 List<String> prefixes =
182                     getInclusivePrefixes(securityHeaderElement, false);
183                 c14nSpec = new ExcC14NParameterSpec(prefixes);
184             }
185 
186            c14nMethod = signatureFactory.newCanonicalizationMethod(canonAlgo, c14nSpec);
187         } catch (Exception ex) {
188             LOG.error("", ex);
189             throw new WSSecurityException(
190                 WSSecurityException.ErrorCode.FAILED_SIGNATURE, ex, "noXMLSig"
191             );
192         }
193 
194         keyInfoUri = getIdAllocator().createSecureId("KI-", keyInfo);
195         if (!useCustomSecRef && customKeyInfoElement == null) {
196             secRef = new SecurityTokenReference(getDocument());
197             strUri = getIdAllocator().createSecureId("STR-", secRef);
198             secRef.addWSSENamespace();
199             secRef.addWSUNamespace();
200             secRef.setID(strUri);
201 
202             //
203             // Get an initialized XMLSignature element.
204             //
205 
206             //
207             // Prepare and setup the token references for this Signature
208             //
209             switch (keyIdentifierType) {
210             case WSConstants.BST_DIRECT_REFERENCE:
211                 Reference ref = new Reference(getDocument());
212                 ref.setURI("#" + certUri);
213 
214                 addBST(certs);
215                 if (!useSingleCert) {
216                     secRef.addTokenType(PKIPathSecurity.PKI_TYPE);
217                     ref.setValueType(PKIPathSecurity.PKI_TYPE);
218                 } else {
219                     ref.setValueType(X509Security.X509_V3_TYPE);
220                 }
221                 secRef.setReference(ref);
222                 break;
223 
224                 case WSConstants.ISSUER_SERIAL:
225                     addIssuerSerial(certs,false);
226                     break;
227 
228                 case WSConstants.ISSUER_SERIAL_QUOTE_FORMAT:
229                     addIssuerSerial(certs,true);
230                     break;
231 
232                 case WSConstants.X509_KEY_IDENTIFIER:
233                 secRef.setKeyIdentifier(certs[0]);
234                 break;
235 
236             case WSConstants.SKI_KEY_IDENTIFIER:
237                 secRef.setKeyIdentifierSKI(certs[0], crypto);
238 
239                 if (includeSignatureToken) {
240                     addBST(certs);
241                 }
242                 break;
243 
244             case WSConstants.THUMBPRINT_IDENTIFIER:
245                 secRef.setKeyIdentifierThumb(certs[0]);
246 
247                 if (includeSignatureToken) {
248                     addBST(certs);
249                 }
250                 break;
251 
252             case WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER:
253                 if (encrKeySha1value != null) {
254                     secRef.setKeyIdentifierEncKeySHA1(encrKeySha1value);
255                 } else {
256                     byte[] digestBytes = KeyUtils.generateDigest(secretKey);
257                     secRef.setKeyIdentifierEncKeySHA1(org.apache.xml.security.utils.XMLUtils.encodeToString(digestBytes));
258                 }
259                 secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
260                 break;
261 
262             case WSConstants.CUSTOM_SYMM_SIGNING :
263                 Reference refCust = new Reference(getDocument());
264                 if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(customTokenValueType)) {
265                     secRef.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
266                     refCust.setValueType(customTokenValueType);
267                 } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customTokenValueType)) {
268                     secRef.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
269                 } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customTokenValueType)) {
270                     secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
271                     refCust.setValueType(customTokenValueType);
272                 } else if (KerberosSecurity.isKerberosToken(customTokenValueType)) {
273                     secRef.addTokenType(customTokenValueType);
274                     refCust.setValueType(customTokenValueType);
275                 } else {
276                     refCust.setValueType(customTokenValueType);
277                 }
278                 refCust.setURI("#" + customTokenId);
279                 secRef.setReference(refCust);
280                 break;
281 
282             case WSConstants.CUSTOM_SYMM_SIGNING_DIRECT :
283                 Reference refCustd = new Reference(getDocument());
284                 if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(customTokenValueType)) {
285                     secRef.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
286                     refCustd.setValueType(customTokenValueType);
287                 } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customTokenValueType)) {
288                     secRef.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
289                 } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customTokenValueType)) {
290                     secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
291                     refCustd.setValueType(customTokenValueType);
292                 } else if (KerberosSecurity.isKerberosToken(customTokenValueType)) {
293                     secRef.addTokenType(customTokenValueType);
294                     refCustd.setValueType(customTokenValueType);
295                 } else {
296                     refCustd.setValueType(customTokenValueType);
297                 }
298                 refCustd.setURI(customTokenId);
299                 secRef.setReference(refCustd);
300                 break;
301 
302             case WSConstants.CUSTOM_KEY_IDENTIFIER:
303                 if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(customTokenValueType)) {
304                     secRef.setKeyIdentifier(customTokenValueType, customTokenId);
305                     secRef.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
306                 } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customTokenValueType)) {
307                     secRef.setKeyIdentifier(customTokenValueType, customTokenId);
308                     secRef.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
309                 } else if (WSConstants.WSS_ENC_KEY_VALUE_TYPE.equals(customTokenValueType)) {
310                     secRef.setKeyIdentifier(customTokenValueType, customTokenId, true);
311                     secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
312                 } else if (SecurityTokenReference.ENC_KEY_SHA1_URI.equals(customTokenValueType)) {
313                     secRef.setKeyIdentifier(customTokenValueType, customTokenId, true);
314                     secRef.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
315                 } else if (WSConstants.WSS_KRB_KI_VALUE_TYPE.equals(customTokenValueType)) {
316                     secRef.setKeyIdentifier(customTokenValueType, customTokenId, true);
317                     secRef.addTokenType(WSConstants.WSS_GSS_KRB_V5_AP_REQ);
318                 }
319                 break;
320 
321             case WSConstants.KEY_VALUE:
322                 java.security.PublicKey publicKey = certs[0].getPublicKey();
323 
324                 try {
325                     KeyInfoFactory keyInfoFactory = signatureFactory.getKeyInfoFactory();
326                     KeyValue keyValue = keyInfoFactory.newKeyValue(publicKey);
327                     keyInfo =
328                         keyInfoFactory.newKeyInfo(Collections.singletonList(keyValue), keyInfoUri);
329                 } catch (java.security.KeyException ex) {
330                     LOG.error("", ex);
331                     throw new WSSecurityException(
332                         WSSecurityException.ErrorCode.FAILED_SIGNATURE, ex, "noXMLSig"
333                     );
334                 }
335                 break;
336             default:
337                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unsupportedKeyId");
338             }
339         }
340 
341         if (keyIdentifierType != WSConstants.KEY_VALUE) {
342             marshalKeyInfo(wsDocInfo);
343         }
344     }
345 
346     private void addIssuerSerial(X509Certificate[] certs,boolean isCommaDelimited) throws WSSecurityException {
347         String issuer = certs[0].getIssuerX500Principal().getName();
348         java.math.BigInteger serialNumber = certs[0].getSerialNumber();
349 
350         DOMX509IssuerSerial domIssuerSerial
351                 = new DOMX509IssuerSerial(getDocument(), issuer, serialNumber, isCommaDelimited);
352         DOMX509Data domX509Data = new DOMX509Data(getDocument(), domIssuerSerial);
353         secRef.setUnknownElement(domX509Data.getElement());
354 
355         if (includeSignatureToken) {
356             addBST(certs);
357         }
358     }
359 
360     protected void marshalKeyInfo(WSDocInfo wsDocInfo) throws WSSecurityException {
361         List<XMLStructure> kiChildren = null;
362         if (customKeyInfoElement == null) {
363             XMLStructure structure = new DOMStructure(secRef.getElement());
364             wsDocInfo.addTokenElement(secRef.getElement(), false);
365             kiChildren = Collections.singletonList(structure);
366         } else {
367             Node kiChild = customKeyInfoElement.getFirstChild();
368             kiChildren = new ArrayList<>();
369             while (kiChild != null) {
370                 kiChildren.add(new DOMStructure(kiChild));
371                 kiChild = kiChild.getNextSibling();
372             }
373         }
374 
375         KeyInfoFactory keyInfoFactory = signatureFactory.getKeyInfoFactory();
376         keyInfo = keyInfoFactory.newKeyInfo(kiChildren, keyInfoUri);
377     }
378 
379     /**
380      * Builds a signed soap envelope.
381      *
382      * This is a convenience method and for backward compatibility. The method
383      * creates a Signature and puts it into the Security header. It does so by
384      * calling the single functions in order to perform a <i>one shot signature</i>.
385      *
386      * @param cr An instance of the Crypto API to handle keystore and certificates
387      * @return A signed SOAP envelope as <code>Document</code>
388      * @throws WSSecurityException
389      */
390     public Document build(Crypto cr)
391         throws WSSecurityException {
392 
393         LOG.debug("Beginning signing...");
394 
395         prepare(cr);
396         if (getParts().isEmpty()) {
397             getParts().add(WSSecurityUtil.getDefaultEncryptionPart(getDocument()));
398         } else {
399             for (WSEncryptionPart part : getParts()) {
400                 if (part.getId() == null && "STRTransform".equals(part.getName())) {
401                     part.setId(strUri);
402                 } else if ("KeyInfo".equals(part.getName()) && WSConstants.SIG_NS.equals(part.getNamespace())
403                     && part.getElement() == null) {
404                     // Special code to sign the KeyInfo
405                     part.setId(keyInfoUri);
406                 }
407             }
408         }
409 
410         List<javax.xml.crypto.dsig.Reference> referenceList = addReferencesToSign(getParts());
411 
412         computeSignature(referenceList);
413 
414         //
415         // if we have a BST prepend it in front of the Signature according to
416         // strict layout rules.
417         //
418         if (bstToken != null) {
419             prependBSTElementToHeader();
420         }
421 
422         return getDocument();
423     }
424 
425 
426     /**
427      * This method adds references to the Signature.
428      *
429      * @param references The list of references to sign
430      * @throws WSSecurityException
431      */
432     public List<javax.xml.crypto.dsig.Reference> addReferencesToSign(
433         List<WSEncryptionPart> references
434     ) throws WSSecurityException {
435         return
436             addReferencesToSign(
437                 getDocument(),
438                 references,
439                 getWsDocInfo(),
440                 signatureFactory,
441                 addInclusivePrefixes,
442                 digestAlgo
443             );
444     }
445 
446     /**
447      * Returns the SignatureElement.
448      * The method can be called any time after <code>prepare()</code>.
449      * @return The DOM Element of the signature.
450      */
451     public Element getSignatureElement() {
452         Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
453         return
454             XMLUtils.getDirectChildElement(
455                 securityHeaderElement, WSConstants.SIG_LN, WSConstants.SIG_NS
456             );
457     }
458 
459     /**
460      * Add a BinarySecurityToken
461      */
462     private void addBST(X509Certificate[] certs) throws WSSecurityException {
463         if (storeBytesInAttachment) {
464             bstToken =
465                 getDocument().createElementNS(WSS4JConstants.WSSE_NS, "wsse:BinarySecurityToken");
466             bstToken.setAttributeNS(null, "EncodingType", WSS4JConstants.BASE64_ENCODING);
467             bstToken.setAttributeNS(WSS4JConstants.WSU_NS, WSS4JConstants.WSU_PREFIX + ":Id", certUri);
468             if (addWSUNamespace) {
469                 bstToken.setAttributeNS(XMLUtils.XMLNS_NS, "xmlns:" + WSConstants.WSU_PREFIX, WSConstants.WSU_NS);
470             }
471 
472             byte[] certBytes = null;
473             if (!useSingleCert) {
474                 bstToken.setAttributeNS(null, "ValueType", PKIPathSecurity.PKI_TYPE);
475                 certBytes = crypto.getBytesFromCertificates(certs);
476             } else {
477                 bstToken.setAttributeNS(null, "ValueType", X509Security.X509_V3_TYPE);
478                 try {
479                     certBytes = certs[0].getEncoded();
480                 } catch (CertificateEncodingException e) {
481                     throw new WSSecurityException(
482                         WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, e, "encodeError"
483                     );
484                 }
485             }
486 
487             final String attachmentId = getIdAllocator().createId("", getDocument());
488             AttachmentUtils.storeBytesInAttachment(bstToken, getDocument(), attachmentId,
489                                                   certBytes, attachmentCallbackHandler);
490             getWsDocInfo().addTokenElement(bstToken, false);
491         } else {
492             BinarySecurity binarySecurity = null;
493             if (!useSingleCert) {
494                 binarySecurity = new PKIPathSecurity(getDocument());
495                 ((PKIPathSecurity) binarySecurity).setX509Certificates(certs, crypto);
496             } else {
497                 binarySecurity = new X509Security(getDocument());
498                 ((X509Security) binarySecurity).setX509Certificate(certs[0]);
499             }
500             binarySecurity.setID(certUri);
501             if (addWSUNamespace) {
502                 binarySecurity.addWSUNamespace();
503             }
504             bstToken = binarySecurity.getElement();
505             getWsDocInfo().addTokenElement(bstToken, false);
506         }
507 
508         bstAddedToSecurityHeader = false;
509     }
510 
511     /**
512      * Prepend the BinarySecurityToken to the elements already in the Security
513      * header.
514      *
515      * The method can be called any time after <code>prepare()</code>.
516      * This allows to insert the BST element at any position in the Security
517      * header.
518      */
519     public void prependBSTElementToHeader() {
520         if (bstToken != null && !bstAddedToSecurityHeader) {
521             Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
522             WSSecurityUtil.prependChildElement(securityHeaderElement, bstToken);
523             bstAddedToSecurityHeader = true;
524         }
525     }
526 
527     /**
528      * Append the BinarySecurityToken to the security header.
529      */
530     public void appendBSTElementToHeader() {
531         if (bstToken != null && !bstAddedToSecurityHeader) {
532             Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
533             securityHeaderElement.appendChild(bstToken);
534             bstAddedToSecurityHeader = true;
535         }
536     }
537 
538     /**
539      * Compute the Signature over the references. The signature element will be
540      * prepended to the security header.
541      *
542      * This method can be called any time after the references were set. See
543      * <code>addReferencesToSign()</code>.
544      *
545      * @param referenceList The list of references to sign
546      *
547      * @throws WSSecurityException
548      */
549     public void computeSignature(
550         List<javax.xml.crypto.dsig.Reference> referenceList
551     ) throws WSSecurityException {
552         computeSignature(referenceList, true, null);
553     }
554 
555     /**
556      * Compute the Signature over the references.
557      *
558      * This method can be called any time after the references were set. See
559      * <code>addReferencesToSign()</code>.
560      *
561      * @param referenceList The list of references to sign
562      * @param prepend Whether to prepend the signature element to the security header
563      * @param siblingElement If prepending, then prepend before this sibling Element
564      *
565      * @throws WSSecurityException
566      */
567     public void computeSignature(
568         List<javax.xml.crypto.dsig.Reference> referenceList,
569         boolean prepend,
570         Element siblingElement
571     ) throws WSSecurityException {
572         try {
573             java.security.Key key;
574             if (secretKey == null) {
575                 key = crypto.getPrivateKey(user, password);
576             } else {
577                 key = KeyUtils.prepareSecretKey(sigAlgo, secretKey);
578             }
579             SignatureMethod signatureMethod =
580                 signatureFactory.newSignatureMethod(sigAlgo, null);
581             SignedInfo signedInfo =
582                 signatureFactory.newSignedInfo(c14nMethod, signatureMethod, referenceList);
583 
584             sig = signatureFactory.newXMLSignature(
585                     signedInfo,
586                     keyInfo,
587                     null,
588                     getIdAllocator().createId("SIG-", null),
589                     null);
590 
591             //
592             // Figure out where to insert the signature element
593             //
594             XMLSignContext signContext = null;
595             Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
596             if (prepend) {
597                 if (siblingElement == null) {
598                     Node child = securityHeaderElement.getFirstChild();
599                     while (child != null && child.getNodeType() != Node.ELEMENT_NODE) {
600                         child = child.getNextSibling();
601                     }
602                     siblingElement = (Element)child;
603                 }
604                 if (siblingElement == null) {
605                     signContext = new DOMSignContext(key, securityHeaderElement);
606                 } else {
607                     signContext = new DOMSignContext(key, securityHeaderElement, siblingElement);
608                 }
609             } else {
610                 signContext = new DOMSignContext(key, securityHeaderElement);
611             }
612             if (signatureProvider != null) {
613                 signContext.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", signatureProvider);
614             }
615 
616             signContext.putNamespacePrefix(WSConstants.SIG_NS, WSConstants.SIG_PREFIX);
617             if (WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(canonAlgo)) {
618                 signContext.putNamespacePrefix(
619                     WSConstants.C14N_EXCL_OMIT_COMMENTS,
620                     WSConstants.C14N_EXCL_OMIT_COMMENTS_PREFIX
621                 );
622             }
623             signContext.setProperty(STRTransform.TRANSFORM_WS_DOC_INFO, getWsDocInfo());
624             getWsDocInfo().setCallbackLookup(callbackLookup);
625 
626             // Add the elements to sign to the Signature Context
627             getWsDocInfo().setTokensOnContext((DOMSignContext)signContext);
628             sig.sign(signContext);
629 
630             signatureValue = sig.getSignatureValue().getValue();
631 
632             cleanup();
633         } catch (Exception ex) {
634             LOG.error(ex.getMessage(), ex);
635             throw new WSSecurityException(
636                 WSSecurityException.ErrorCode.FAILED_SIGNATURE, ex
637             );
638         }
639     }
640 
641     /**
642      * Set the single cert flag.
643      *
644      * @param useSingleCert
645      */
646     public void setUseSingleCertificate(boolean useSingleCert) {
647         this.useSingleCert = useSingleCert;
648     }
649 
650     /**
651      * Get the single cert flag.
652      *
653      * @return A boolean if single certificate is set.
654      */
655     public boolean isUseSingleCertificate() {
656         return useSingleCert;
657     }
658 
659     /**
660      * Set the name (uri) of the signature encryption algorithm to use.
661      *
662      * If the algorithm is not set then an automatic detection of the signature
663      * algorithm to use is performed during the <code>prepare()</code>
664      * method. Refer to WSConstants which algorithms are supported.
665      *
666      * @param algo the name of the signature algorithm
667      * @see WSConstants#RSA
668      * @see WSConstants#DSA
669      */
670     public void setSignatureAlgorithm(String algo) {
671         sigAlgo = algo;
672     }
673 
674     /**
675      * Get the name (uri) of the signature algorithm that is being used.
676      *
677      * Call this method after <code>prepare</code> to get the information
678      * which signature algorithm was automatically detected if no signature
679      * algorithm was preset.
680      *
681      * @return the identifier URI of the signature algorithm
682      */
683     public String getSignatureAlgorithm() {
684         return sigAlgo;
685     }
686 
687     /**
688      * Set the canonicalization method to use.
689      *
690      * If the canonicalization method is not set then the recommended Exclusive
691      * XML Canonicalization is used by default. Refer to WSConstants which
692      * algorithms are supported.
693      *
694      * @param algo Is the name of the signature algorithm
695      * @see WSConstants#C14N_OMIT_COMMENTS
696      * @see WSConstants#C14N_WITH_COMMENTS
697      * @see WSConstants#C14N_EXCL_OMIT_COMMENTS
698      * @see WSConstants#C14N_EXCL_WITH_COMMENTS
699      */
700     public void setSigCanonicalization(String algo) {
701         canonAlgo = algo;
702     }
703 
704     /**
705      * Get the canonicalization method.
706      *
707      * If the canonicalization method was not set then Exclusive XML
708      * Canonicalization is used by default.
709      *
710      * @return The string describing the canonicalization algorithm.
711      */
712     public String getSigCanonicalization() {
713         return canonAlgo;
714     }
715 
716     /**
717      * @return the digest algorithm to use
718      */
719     public String getDigestAlgo() {
720         return digestAlgo;
721     }
722 
723     /**
724      * Set the string that defines which digest algorithm to use.
725      * The default is WSConstants.SHA1.
726      *
727      * @param digestAlgo the digestAlgo to set
728      */
729     public void setDigestAlgo(String digestAlgo) {
730         this.digestAlgo = digestAlgo;
731     }
732 
733 
734     /**
735      * Returns the computed Signature value.
736      *
737      * Call this method after <code>computeSignature()</code> or <code>build()</code>
738      * methods were called.
739      *
740      * @return Returns the signatureValue.
741      */
742     public byte[] getSignatureValue() {
743         return signatureValue;
744     }
745 
746     /**
747      * Get the id generated during <code>prepare()</code>.
748      *
749      * Returns the the value of wsu:Id attribute of the Signature element.
750      *
751      * @return Return the wsu:Id of this token or null if <code>prepare()</code>
752      *         was not called before.
753      */
754     public String getId() {
755         if (sig == null) {
756             return null;
757         }
758         return sig.getId();
759     }
760 
761     /**
762      * Get the id of the BST generated  during <code>prepare()</code>.
763      *
764      * @return Returns the the value of wsu:Id attribute of the
765      * BinaruSecurityToken element.
766      */
767     public String getBSTTokenId() {
768         if (bstToken == null) {
769             return null;
770         }
771         return bstToken.getAttributeNS(WSS4JConstants.WSU_NS, "Id");
772     }
773 
774     /**
775      * Set the secret key to use
776      * @param secretKey the secret key to use
777      */
778     public void setSecretKey(byte[] secretKey) {
779         this.secretKey = secretKey;
780     }
781 
782     /**
783      * Set the custom token value type to use
784      * @param customTokenValueType the custom token value type to use
785      */
786     public void setCustomTokenValueType(String customTokenValueType) {
787         this.customTokenValueType = customTokenValueType;
788     }
789 
790     /**
791      * Set the custom token id
792      * @param customTokenId the custom token id
793      */
794     public void setCustomTokenId(String customTokenId) {
795         this.customTokenId = customTokenId;
796     }
797 
798     public String getCustomTokenId() {
799         return this.customTokenId;
800     }
801 
802     /**
803      * Set the encrypted key sha1 value
804      * @param encrKeySha1value the encrypted key sha1 value
805      */
806     public void setEncrKeySha1value(String encrKeySha1value) {
807         this.encrKeySha1value = encrKeySha1value;
808     }
809 
810     /**
811      * Set the X509 Certificate to use
812      * @param cer the X509 Certificate to use
813      */
814     public void setX509Certificate(X509Certificate cer) {
815         this.useThisCert = cer;
816     }
817 
818     /**
819      * Returns the BST Token element.
820      * The method can be called any time after <code>prepare()</code>.
821      * @return the BST Token element
822      */
823     public Element getBinarySecurityTokenElement() {
824         return bstToken;
825     }
826 
827     /**
828      * @return the URI associated with the SecurityTokenReference
829      * (must be called after {@link #prepare(Document, Crypto)}
830      */
831     public String getSecurityTokenReferenceURI() {
832         return strUri;
833     }
834 
835     /**
836      * Get the SecurityTokenReference to be used in the KeyInfo element.
837      */
838     public SecurityTokenReference getSecurityTokenReference() {
839         return secRef;
840     }
841 
842     /**
843      * Set the SecurityTokenReference to be used in the KeyInfo element. If this
844      * method is not called, a SecurityTokenRefence will be generated.
845      */
846     public void setSecurityTokenReference(SecurityTokenReference secRef) {
847         useCustomSecRef = true;
848         this.secRef = secRef;
849     }
850 
851     /**
852      * Set up the X509 Certificate(s) for signing.
853      */
854     private X509Certificate[] getSigningCerts() throws WSSecurityException {
855         X509Certificate[] certs = null;
856         if (!(keyIdentifierType == WSConstants.CUSTOM_SYMM_SIGNING
857             || keyIdentifierType == WSConstants.CUSTOM_SYMM_SIGNING_DIRECT
858             || keyIdentifierType == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER
859             || keyIdentifierType == WSConstants.CUSTOM_KEY_IDENTIFIER)) {
860             if (useThisCert == null) {
861                 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
862                 cryptoType.setAlias(user);
863                 if (crypto == null) {
864                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noSigCryptoFile");
865                 }
866                 certs = crypto.getX509Certificates(cryptoType);
867             } else {
868                 certs = new X509Certificate[] {useThisCert};
869             }
870             if (certs == null || certs.length <= 0) {
871                 throw new WSSecurityException(
872                         WSSecurityException.ErrorCode.FAILURE,
873                         "noUserCertsFound",
874                         new Object[] {user, "signature"});
875             }
876             certUri = getIdAllocator().createSecureId("X509-", certs[0]);
877             //
878             // If no signature algorithm was set try to detect it according to the
879             // data stored in the certificate.
880             //
881             if (sigAlgo == null) {
882                 String pubKeyAlgo = certs[0].getPublicKey().getAlgorithm();
883                 LOG.debug("Automatic signature algorithm detection: {}", pubKeyAlgo);
884                 if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
885                     sigAlgo = WSConstants.DSA;
886                 } else if (pubKeyAlgo.equalsIgnoreCase("RSA")) {
887                     sigAlgo = WSConstants.RSA;
888                 } else if (pubKeyAlgo.equalsIgnoreCase("EC")) {
889                     sigAlgo = WSConstants.ECDSA_SHA256;
890                 } else {
891                     throw new WSSecurityException(
892                         WSSecurityException.ErrorCode.FAILURE,
893                         "unknownSignatureAlgorithm",
894                         new Object[] {pubKeyAlgo});
895                 }
896             }
897         }
898         return certs;
899     }
900 
901     public boolean isIncludeSignatureToken() {
902         return includeSignatureToken;
903     }
904 
905     public void setIncludeSignatureToken(boolean includeSignatureToken) {
906         this.includeSignatureToken = includeSignatureToken;
907     }
908 
909     public boolean isAddInclusivePrefixes() {
910         return addInclusivePrefixes;
911     }
912 
913     public void setAddInclusivePrefixes(boolean addInclusivePrefixes) {
914         this.addInclusivePrefixes = addInclusivePrefixes;
915     }
916 
917     public void setCustomKeyInfoElement(Element keyInfoElement) {
918         this.customKeyInfoElement = keyInfoElement;
919     }
920 
921     public Element getCustomKeyInfoElement() {
922         return customKeyInfoElement;
923     }
924 
925     public Provider getSignatureProvider() {
926         return signatureProvider;
927     }
928 
929     public void setSignatureProvider(Provider signatureProvider) {
930         this.signatureProvider = signatureProvider;
931     }
932 
933     public String getKeyInfoUri() {
934         return keyInfoUri;
935     }
936 }