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