View Javadoc

1   /*
2    * The MIT License
3    *
4    * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
5    *
6    * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   *
26   * $HeadURL: https://svn.softwareborsen.dk/sosi/trunk/modules/seal/src/main/java/dk/sosi/seal/model/SignatureUtil.java $
27   * $Id: SignatureUtil.java 9704 2012-01-10 11:55:16Z chg@lakeside.dk $
28   */
29  package dk.sosi.seal.model;
30  
31  import dk.sosi.seal.SOSIFactory;
32  import dk.sosi.seal.model.constants.*;
33  import dk.sosi.seal.modelbuilders.SignatureInvalidModelBuildException;
34  import dk.sosi.seal.pki.*;
35  import dk.sosi.seal.transform.internal.STRTransform;
36  import dk.sosi.seal.vault.CredentialPair;
37  import dk.sosi.seal.vault.CredentialVault;
38  import dk.sosi.seal.vault.CredentialVaultException;
39  import dk.sosi.seal.xml.CertificateParser;
40  import dk.sosi.seal.xml.XmlUtil;
41  import org.apache.ws.security.WSDocInfo;
42  import org.apache.ws.security.WSDocInfoStore;
43  import org.apache.xml.security.c14n.CanonicalizationException;
44  import org.apache.xml.security.c14n.Canonicalizer;
45  import org.apache.xml.security.c14n.InvalidCanonicalizerException;
46  import org.apache.xml.security.exceptions.AlgorithmAlreadyRegisteredException;
47  import org.apache.xml.security.exceptions.XMLSecurityException;
48  import org.apache.xml.security.keys.KeyInfo;
49  import org.apache.xml.security.keys.keyresolver.KeyResolverException;
50  import org.apache.xml.security.signature.XMLSignature;
51  import org.apache.xml.security.signature.XMLSignatureException;
52  import org.apache.xml.security.transforms.Transform;
53  import org.apache.xml.security.transforms.TransformationException;
54  import org.apache.xml.security.transforms.Transforms;
55  import org.w3c.dom.*;
56  
57  import java.io.BufferedOutputStream;
58  import java.io.ByteArrayOutputStream;
59  import java.security.KeyStore;
60  import java.security.Provider;
61  import java.security.Security;
62  import java.security.cert.CertificateEncodingException;
63  import java.security.cert.X509Certificate;
64  import java.util.Iterator;
65  import java.util.LinkedList;
66  import java.util.List;
67  import java.util.Properties;
68  import java.util.logging.Level;
69  import java.util.logging.Logger;
70  
71  /**
72   * Utility class for creating and validating XML Digital Signatures
73   *
74   * @author kkj
75   * @author $LastChangedBy: chg@lakeside.dk $
76   * @since 1.0
77   */
78  public class SignatureUtil {
79  
80  	// used for testing - we need to be sure BC isnt installed in java.security
81  	static final boolean bcAddedInJavaSecurity = Security.getProvider(SOSIFactory.PROPERTYVALUE_SOSI_CRYPTOPROVIDER_BOUNCYCASTLE) != null; //NOPMD
82  
83      static {
84          // Initialize the Apache XML Signature API
85          org.apache.xml.security.Init.init();
86          if (! Boolean.getBoolean(SOSIFactory.PROPERTYNAME_SOSI_DO_NOT_REGISTER_STR_TRANSFORM)) {
87              try {
88                  Transform.register(STRTransform.implementedTransformURI, STRTransform.class.getName());
89              } catch (AlgorithmAlreadyRegisteredException e) {
90                  throw new RuntimeException("Unable to register STR-Transform " + STRTransform.class.getName(), e);
91              }
92          }
93  
94          Logger.getLogger("org.apache.xml.security.signature.Reference").setLevel(Level.OFF);
95  	}
96  
97      // Temporary inner class - to be deleted along the deprecated method that uses it
98      private static class CredentialPairAdapterVault implements CredentialVault {
99  
100         private final CredentialPair credentialPair;
101 
102         public CredentialPairAdapterVault(CredentialPair credentialPair) {
103             this.credentialPair = credentialPair;
104         }
105 
106         public boolean isTrustedCertificate(X509Certificate certificate) throws CredentialVaultException {
107             throw new UnsupportedOperationException();
108         }
109 
110         public CredentialPair getSystemCredentialPair() throws CredentialVaultException {
111             return credentialPair;
112         }
113 
114         public void setSystemCredentialPair(CredentialPair credentialPair) throws CredentialVaultException {
115             throw new UnsupportedOperationException();
116         }
117 
118         public KeyStore getKeyStore() {
119             throw new UnsupportedOperationException();
120         }
121     }
122 
123     //Temporary inner class - to be deleted again when LDAP certificate references are no longer to be supported
124     private static class CertificateFederation extends Federation {
125 
126         private final X509Certificate certificate;
127 
128         public CertificateFederation(X509Certificate certificate) {
129             super(System.getProperties(), null);
130             this.certificate = certificate;
131         }
132 
133         @Override
134         protected boolean subjectDistinguishedNameMatches(DistinguishedName subjectDistinguishedName) {
135             return false;
136         }
137 
138         @Override
139         public X509Certificate getFederationCertificate(FederationCertificateReference reference) {
140             return certificate;
141         }
142     }
143 
144 	/**
145 	 * Create a <ds:SignedInfo> element with the c14n hashes for the elements, pointed to by the ids in the referenceUris array in the supplied
146 	 * Document. This method is useful for situations where the actual signature will be created externally e.g. in the situation where an actual user
147 	 * has to be prompted. The actual signature can be computed by: <p/>
148 	 * <ol>
149 	 * <li>Encrypting the bytes with the private key of the user</li>
150 	 * <li>Base64 encoding the signed byte array</li>
151 	 * </ol>
152 	 * <p/>
153 	 *
154 	 * @param referenceUris
155 	 *            An array of ID values to sign
156 	 * @param doc
157 	 *            The document that contains IDs pointed to in referenceURIs
158 	 * @param signatureParentID
159 	 * @return A <code>String</code> containing the Base64 encoded SHA-1 digest of the c14n &lt;ds:SignedInfo&gt; element
160      * @deprecated Use {@link #getSignedInfoBytes(org.w3c.dom.Document, SignatureConfiguration)} instead
161 	 */
162 	@Deprecated
163 	 public static byte[] getSignedInfoBytes(String[] referenceUris, Document doc, String signatureParentID) {
164         SignatureConfiguration configuration = new SignatureConfiguration(referenceUris, signatureParentID, IDValues.id);
165         return getSignedInfoBytes(doc, configuration);
166 	}
167 
168     public static byte[] getSignedInfoBytes(Document doc, SignatureConfiguration configuration) {
169         XMLSignature xmlSignature = initXmlSignature(doc, configuration);
170 
171 		try {
172 			xmlSignature.getSignedInfo().generateDigestValues();
173 		} catch (XMLSignatureException e) {
174 			throw new ModelException("Unable to digest values", e);
175 		}
176 
177 		tidyXML(xmlSignature.getElement());
178 
179 		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
180 
181 		try {
182 			xmlSignature.getSignedInfo().signInOctectStream(new BufferedOutputStream(byteArrayOutputStream));
183 		} catch (XMLSecurityException e) {
184 			throw new ModelException("Unable to generate c14n of SignedInfo", e);
185 		}
186 
187 		return byteArrayOutputStream.toByteArray();
188     }
189 
190 	/**
191 	 * Sign elements in the supplied Document, pointed to by the ids in the referenceUris array using the private key from the credentialPair and
192 	 * attaching the corresponding certificate. Signature is enveloped and stored with the signatureParentLocation element or after the root, if that
193 	 * variable is null. Keys are exepcted to be RSA, and digest will be SHA1. Canonicalization is fixed to C14N_OMIT_COMMENTS.
194 	 *
195 	 * @param referenceUris
196 	 *            An array of URIs referenceing id's in doc that will be signed. URIs MUST NOT be prepended with a #
197 	 * @param credentialPair
198 	 *            Certificate and corresponding private key used for signing
199 	 * @param doc
200 	 *            The document that contains elements to be signed
201 	 * @param signatureSiblingLocationID
202 	 *            The parent node that will hold the ds:Signature element that was generated.
203      * @deprecated Use {@link #sign(dk.sosi.seal.vault.CredentialPair, org.w3c.dom.Document, SignatureConfiguration)} instead
204 	 */
205 	@Deprecated
206     public static void sign(String[] referenceUris, CredentialPair credentialPair, Document doc, String signatureSiblingLocationID) {
207         SignatureConfiguration signatureConfiguration = new SignatureConfiguration(referenceUris, signatureSiblingLocationID, IDValues.id);
208         CredentialVault vault = new CredentialPairAdapterVault(credentialPair);
209         sign(new CredentialVaultSignatureProvider(vault), doc, signatureConfiguration);
210 	}
211 	
212 
213     /**
214      * Sign elements in the supplied Document, pointed to by the ids in the referenceUris array using the private key from the credentialPair and
215      * attaching the corresponding certificate. Signature is enveloped and stored with the signatureParentLocation element or after the root, if that
216      * variable is null. Keys are exepcted to be RSA, and digest will be SHA1. Canonicalization is fixed to C14N_OMIT_COMMENTS.
217      *
218      * @param credentialPair
219      *            Certificate and corresponding private key used for signing
220      * @param doc
221      *            The document that contains elements to be signed
222      * @param configuration
223      *          Configuration parameters for the Signature to be created
224      * @deprecated Use {@link #sign(dk.sosi.seal.pki.SignatureProvider, org.w3c.dom.Document, SignatureConfiguration)} instead
225 	 */
226 	@Deprecated
227 	public static void sign(CredentialPair credentialPair, Document doc, SignatureConfiguration configuration) {
228         CredentialVault vault = new CredentialPairAdapterVault(credentialPair);
229         sign(new CredentialVaultSignatureProvider(vault), doc, configuration);
230     }
231 
232     public static void sign(SignatureProvider provider, Document doc, SignatureConfiguration configuration) {
233         WSDocInfo wsDocInfo = new WSDocInfo(doc);
234         WSDocInfoStore.store(wsDocInfo);
235 
236         try {
237 
238             byte[] bytes = getSignedInfoBytes(doc, configuration);
239             SignatureProvider.SignatureResult result = provider.sign(bytes);
240             injectSignature(doc, result.getSignature(), configuration, result.getCertificate(), false);
241 
242         } finally {
243             WSDocInfoStore.delete(wsDocInfo);
244         }
245     }
246 
247 	/**
248 	 * Validate the supplied ds:signature node. The signature is assumed to be enveloped
249 	 *
250 	 * @param signatureToValidate
251 	 * @return true if the signature validates, false otherwise
252 	 * @throws ModelException
253 	 *             if the signature could not be validated for any reason
254 	 */
255 	public static boolean validate(Node signatureToValidate, Federation federation, CredentialVault vault, boolean checkTrust) {
256 		return internalValidate(signatureToValidate, federation, vault, checkTrust);
257 	}
258 
259 	/**
260 	 * Get the X509Certificate that is embedded within the supplied signatureElement
261 	 *
262 	 * @param signatureElement
263 	 *            &lt;ds:Signature&gt; which has an X509Certificate in it.
264 	 * @return The certificate
265 	 * @throws ModelException
266 	 *             if the X509Certificate could not be found or decoding failed
267 	 */
268 	public static X509Certificate getCertificateFromSignature(Node signatureElement) throws ModelException {
269 		NodeList nodeList = ((Element) signatureElement).getElementsByTagNameNS(NameSpaces.DSIG_SCHEMA, DSTags.X509CERTIFICATE);
270 		Node x509Certificate = nodeList.getLength() > 0 ? nodeList.item(0) : null;
271 
272 		if (x509Certificate == null) {
273 			throw new ModelException("No " + NameSpaces.DSIG_SCHEMA + ":" + DSTags.X509CERTIFICATE + " element in signature " + signatureElement);
274 		}
275 		String certValue = XmlUtil.getTextNodeValue(x509Certificate);
276 		// strip whitespace
277 		certValue = certValue.replaceAll("\\s", "");
278 		byte[] base64decodedCertValue = XmlUtil.fromBase64(certValue);
279         return CertificateParser.asCertificate(base64decodedCertValue);
280 
281     }
282 
283 	/**
284 	 * Create a digest of the supplied certificate using SHA-1. If the passed certificate is <code>null</code> this methods returns
285 	 * <code>null</code>.
286 	 *
287 	 * @param certificate
288 	 *            The certificate to hash
289 	 * @return A base64 encoded string representing the hash
290 	 * @throws ModelException
291 	 *             If certificate is invalid or digesting failed
292 	 */
293 	public static String getDigestOfCertificate(X509Certificate certificate) throws ModelException {
294 
295 		if (certificate == null)
296 			return null; // NOPMD
297 		byte[] certAsBytes;
298 		try {
299 			certAsBytes = certificate.getEncoded();
300 		} catch (CertificateEncodingException e) {
301 			throw new ModelException("Unable to convert certificate to byte array", e);
302 		}
303 
304 		byte[] hash = XmlUtil.getSha1Digest(certAsBytes);
305 
306 		return XmlUtil.toBase64(hash);
307 	}
308 
309 	/**
310 	 * Injects an externally created signature into the document.
311 	 *
312 	 * @param document
313 	 *            The document to inject into
314 	 * @param signature
315 	 *            The signature value (base 64 encoded)
316 	 * @param signatureParentElementID
317 	 *            The ID of the signature element
318 	 * @param certificate
319 	 *            The certificate used to create the signature
320      * @deprecated Use {@link #injectSignature(org.w3c.dom.Document, String, SignatureConfiguration, java.security.cert.X509Certificate, boolean)} instead
321 	 */
322 	@Deprecated
323 	public static void injectSignature(Document document, String signature, String signatureParentElementID, X509Certificate certificate) {
324         SignatureConfiguration configuration = new SignatureConfiguration((SignatureConfiguration.Reference[]) null, signatureParentElementID, null);
325         injectSignature(document, signature, configuration, certificate, true);
326 	}
327 
328     public static void injectSignature(Document document, String signature, SignatureConfiguration configuration, X509Certificate certificate, boolean validateSignature) {
329         if (certificate == null) {
330             throw new ModelException("X509Certifcate cannot be <null>");
331         }
332 
333         Element signatureParentElement = (Element) XmlUtil.getElementByIdExtended(document, configuration.getSignatureParentID());
334 
335         List<Element> signatureElements = XmlUtil.getChildElementsNS(signatureParentElement, NameSpaces.DSIG_SCHEMA, DSTags.SIGNATURE);
336         if (signatureElements.size() != 1) {
337             throw new ModelException("Expected 1 but found " + signatureElements.size() + " Signature elements");
338         }
339         Element elmSignature = signatureElements.get(0);
340 
341         List<Element> signatureValueElements = XmlUtil.getChildElementsNS(elmSignature, NameSpaces.DSIG_SCHEMA, DSTags.SIGNATURE_VALUE);
342         if (signatureValueElements.size() != 1) {
343             throw new ModelException("Expected 1 but found " + signatureValueElements.size() + " SignatureValue elements");
344         }
345         signatureValueElements.get(0).appendChild(document.createTextNode(signature));
346 
347         addKeyInfo(signatureParentElement, configuration, certificate);
348 
349         // Validate the signature before return
350         if (validateSignature && !internalValidateIgnoreTrust(elmSignature, new CertificateFederation(certificate))) {
351             throw new ModelException("The signature does not validate with the supplied certificate. "
352                     + "Either the signature or the certificate is wrong.");
353         }
354     }
355 
356 	private static void addKeyInfo(Element signatureParentElement, SignatureConfiguration configuration, X509Certificate certificate) {
357         Document doc = signatureParentElement.getOwnerDocument();
358 
359         List<Element> signatureElements = XmlUtil.getChildElementsNS(signatureParentElement, NameSpaces.DSIG_SCHEMA, DSTags.SIGNATURE);
360         if (signatureElements.size() != 1) {
361             throw new ModelException("Expected 1 but found " + signatureElements.size() + " Signature elements");
362         }
363         Element elmSignature = signatureElements.get(0);
364 
365 
366         Element elmKeyInfo = doc.createElementNS(NameSpaces.DSIG_SCHEMA, DSTags.KEY_INFO_PREFIXED);
367         elmSignature.appendChild(elmKeyInfo);
368 
369         if (configuration.getKeyInfoId() != null) {
370             elmKeyInfo.setAttribute(IDValues.Id, configuration.getKeyInfoId());
371         }
372 
373         if (configuration.isAddCertificateAsReference()) {
374             Element elmKeyName = doc.createElementNS(NameSpaces.DSIG_SCHEMA, DSTags.KEY_NAME_PREFIXED);
375             elmKeyInfo.appendChild(elmKeyName);
376             elmKeyName.setTextContent(new FederationCertificateReference(certificate).toString());
377         } else {
378             Element elmX509Data = doc.createElementNS(NameSpaces.DSIG_SCHEMA, DSTags.X509DATA_PREFIXED);
379             elmKeyInfo.appendChild(elmX509Data);
380 
381             Element elmX509Certificate = doc.createElementNS(NameSpaces.DSIG_SCHEMA, DSTags.X509CERTIFICATE_PREFIXED);
382             elmX509Data.appendChild(elmX509Certificate);
383 
384             String encodedCert;
385             try {
386                 encodedCert = XmlUtil.toBase64(certificate.getEncoded());
387             } catch (CertificateEncodingException e) {
388                 throw new ModelException("Unable to encode certificate", e);
389             }
390             elmX509Certificate.appendChild(doc.createTextNode(encodedCert));
391         }
392     }
393 
394 	public static void validateAllSignatures(Message message, NodeList signatures, Federation federation, CredentialVault credentialVault,
395 			boolean checkTrust) throws SignatureInvalidModelBuildException {
396 
397 		for (int i = 0; i < signatures.getLength(); i++) {
398 			if (!SignatureUtil.validate(signatures.item(i), federation, credentialVault, checkTrust)) {
399 
400                 Properties props = (federation == null) ? System.getProperties() : federation.getProperties();
401 				SOSIFactory.getAuditEventHandler(props).onInformationalAuditingEvent(
402 						AuditEventHandler.EVENT_TYPE_ERROR_VALIDATING_SOSI_MESSAGE,
403 						new Object[]{message}
404 						);
405 				throw new SignatureInvalidModelBuildException("Signature could not be validated", message.getMessageID(), message.getFlowID(), message.getDGWSVersion());
406 			}
407 		}
408 	}
409 
410 	/**
411 	 * This method gets the Cryptoprovider even if properties is null. It defaults to the hardcoded value of
412 	 * SOSIFactory.PROPERTYVALUE_SOSI_CRYPTOPROVIDER_BOUNCYCASTLE
413 	 *
414 	 * It also adds org.bouncycastle.jce.provider.BouncyCastleProvider as provider if it is nessesary
415 	 *
416 	 * @param properties <code>Properties</code> to read from.
417 	 * @param key Key to read.
418 	 * @return The identified crypto provider key. 
419 	 */
420 	public static String getCryptoProvider(Properties properties, String key) {
421 		String cryptoProvider = SOSIFactory.PROPERTYVALUE_SOSI_CRYPTOPROVIDER_BOUNCYCASTLE;
422 		if (properties != null && properties.containsKey(key)){
423 			cryptoProvider = properties.getProperty(key);
424 		}
425 		if(cryptoProvider.equals(SOSIFactory.PROPERTYVALUE_SOSI_CRYPTOPROVIDER_BOUNCYCASTLE)
426 				&& Security.getProvider(SOSIFactory.PROPERTYVALUE_SOSI_CRYPTOPROVIDER_BOUNCYCASTLE) == null) {
427 
428 			try {
429 				Provider provider = (Provider) Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance();
430 				Security.addProvider(provider);
431 			} catch (InstantiationException e) {
432 				throw new ModelException("Provider could not be set", e);
433 			} catch (IllegalAccessException e) {
434 				throw new ModelException("Provider could not be set", e);
435 			} catch (ClassNotFoundException e) {
436 				throw new ModelException("Provider could not be set", e);
437 			}
438 		}
439 		return cryptoProvider;
440 	}
441 
442 	/**
443 	 * Returns a Properties object containing a default setup of
444 	 * cryptoproviders based on the running vm
445 	 *
446 	 * Only IBM, SUN 1.4+ is supported by now
447 	 */
448 	public static Properties setupCryptoProviderForJVM() {
449 		Properties properties = new Properties();
450 		if("IBM Corporation".equals(System.getProperty("java.vm.vendor"))) {
451 			properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_PKCS12, "BC");
452 			properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_X509, "BC");
453 			properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_RSA, "IBMJCE");
454 			properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_SHA1WITHRSA, "IBMJCE");
455 		} else { // else SUN
456 			if ("1.4".equals(System.getProperty("java.specification.version"))) {
457 				properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_PKCS12, "BC");
458 			} else { // else 1.5+
459 				properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_PKCS12, "SunJSSE");
460 			}
461 			if ("1.6".equals(System.getProperty("java.specification.version"))) {
462 				properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_X509, "BC");
463 			} else if ("1.4".equals(System.getProperty("java.specification.version"))) {
464                 properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_X509, "BC");
465             } else { // else 1.5
466 				properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_X509, "SUN");
467 			}
468 			properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_RSA, "SunRsaSign");
469 			properties.put(SOSIFactory.PROPERTYNAME_SOSI_CRYPTOPROVIDER_SHA1WITHRSA, "SunRsaSign");
470 		}
471 		return properties;
472 	}
473 
474 	/**
475 	 * Returns the exclusive canonical XML for the specified DOM element (see @link{http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/} for further details).
476 	 *
477 	 * @param domElement the element to canonicalize
478 	 */
479 	public static String getC14NString(Element domElement) 	throws CanonicalizationException, InvalidCanonicalizerException,	XMLSecurityException {
480 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
481 		Canonicalizer c14nizer = Canonicalizer.getInstance("http://www.w3.org/2001/10/xml-exc-c14n#");
482 	    c14nizer.setWriter(new BufferedOutputStream(baos));
483 	     c14nizer.canonicalizeSubtree(domElement);
484 		return baos.toString();
485 	}
486 
487 	// ====================================
488 	// Private stuff
489 	// ====================================
490 	/**
491 	 * Internal utility for initializing an XMLSignature structure with the supplied referenceURIs. The XMLSignature will use RSAwithSHA1 as signature
492 	 * algorithm and C14n_OMIT_COMMENTS as the digest algorithm.
493 	 *
494 	 * @param doc
495      * @param config
496      * @return An initialized XMLSignature
497 	 */
498 	 private static XMLSignature initXmlSignature(Document doc, SignatureConfiguration config) {
499          String baseURI = doc.getDocumentElement().getNamespaceURI();
500 
501          XMLSignature xmlSignature;
502          try {
503              xmlSignature = new XMLSignature(doc, baseURI, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1, Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
504          } catch (XMLSecurityException e) {
505              throw new ModelException("Unable to get XMLSignature object", e);
506          }
507 
508          SignatureConfiguration.Reference[] references = config.getReferences();
509          for (int i = 0; i < references.length; i++) {
510              processReference(doc, xmlSignature, references[i]);
511          }
512 
513          Element xmlSigElement = xmlSignature.getElement();
514          if (config.getIdAttributeName() != null) {
515             xmlSigElement.setAttribute(config.getIdAttributeName(), IDValues.OCES_SIGNATURE);
516          }
517 
518          Element signatureParentLocation = (Element) XmlUtil.getElementByIdExtended(doc, config.getSignatureParentID());
519 
520          // Create a DOMSignContext and specify the RSA PrivateKey and
521          // location of the resulting XMLSignature's parent element
522          if (signatureParentLocation == null) {
523              signatureParentLocation = doc.getDocumentElement();
524          }
525 
526          // Make sure to add the signature DOM element
527          if (config.getSignatureSiblingNode() != null) {
528              signatureParentLocation.insertBefore(xmlSigElement, config.getSignatureSiblingNode());
529          } else {
530              signatureParentLocation.appendChild(xmlSigElement);
531          }
532 
533          return xmlSignature;
534      }
535 
536     private static void processReference(Document doc, XMLSignature xmlSignature, SignatureConfiguration.Reference reference) {
537         String referenceURI = reference.getURI();
538         Transforms transforms = new Transforms(doc);
539         try {
540             switch (reference.getType()) {
541                 case DIRECT_REFERENCE:
542                     transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
543                     transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
544                     break;
545                 case SECURITY_TOKEN_REFERENCE:
546                     transforms.addTransform(STRTransform.implementedTransformURI, createSTRParameters(doc));
547                     break;
548             }
549         } catch (TransformationException e) {
550             throw new ModelException("Unable to add c14n omit comments gctransform to reference " + referenceURI, e);
551         }
552 
553         try {
554             xmlSignature.addDocument("#" + referenceURI, transforms);
555         } catch (XMLSignatureException e) {
556             throw new ModelException("Unable to add transforms for reference " + referenceURI, e);
557         }
558     }
559 
560     private static Element createSTRParameters(Document doc) {
561         Element parameters = doc.createElementNS(NameSpaces.WSSE_SCHEMA, WSSETags.TRANSFORMATION_PARAMETERS_PREFIXED);
562         Element canonicalization = doc.createElementNS(NameSpaces.DSIG_SCHEMA, DSTags.CANONICALIZATION_METHOD_PREFIXED);
563         //canonicalization.setAttribute(DSAttributes.ALGORITHM, Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
564         canonicalization.setAttribute(DSAttributes.ALGORITHM, Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
565         parameters.appendChild(canonicalization);
566         return parameters;
567     }
568 
569     /**
570 	 * Remove superfluous xmlns:ds declarations on the signature generated by Apache. Removes "\n" Text nodes in the signature element.
571 	 *
572 	 * @param element
573 	 *            The XmlSignature element
574 	 */
575 	private static void tidyXML(Element element) {
576 
577 		NamedNodeMap attributes = element.getAttributes();
578 		for (int i = 0; i < attributes.getLength(); i++) {
579 			Attr item = (Attr) attributes.item(i);
580 			if (item.getNodeName().startsWith("xmlns:"))
581 				element.removeAttributeNode(item);
582 		}
583 
584 		NodeList children = element.getChildNodes();
585 		List<Text> lfElements = new LinkedList<Text>();
586 		for (int i = 0; i < children.getLength(); i++) {
587 			Node item = children.item(i);
588 			if (item.getNodeType() == Node.ELEMENT_NODE) {
589 				tidyXML((Element) item);
590 			} else if (item.getNodeType() == Node.TEXT_NODE) {
591 				// lfElements.add(item);
592 				Text textElement = ((Text) item);
593 				String value = ((Text) item).getData();
594 				if (value.indexOf('\n') >= 0) {
595 					if (value.length() == 1) {
596 						// Schedule this element for removal
597 						lfElements.add(textElement);
598 					} else {
599 						// Just remove the newline
600 						textElement.setData(value.replaceAll("\n", ""));
601 					}
602 				}
603 			}
604 		}
605 
606 		// Remove all linefeed elements
607 		for (Iterator<Text> iter = lfElements.iterator(); iter.hasNext();) {
608 			element.removeChild(iter.next());
609 		}
610 	}
611 
612 	private static boolean internalValidateIgnoreTrust(Node signatureToValidate, Federation federation) {
613         return internalValidate(signatureToValidate, federation, null, false);
614 	}
615 
616 	private static boolean internalValidate(Node signatureToValidate, Federation federation, CredentialVault vault, boolean checkForTrustedCertificates) {
617         WSDocInfo wsDocInfo = new WSDocInfo(signatureToValidate.getOwnerDocument());
618         WSDocInfoStore.store(wsDocInfo);
619         try {
620 
621             String baseUri = signatureToValidate.getOwnerDocument().getDocumentElement().getNamespaceURI();
622 
623             if (baseUri == null) {
624                 baseUri = "";
625             }
626 
627             if (signatureToValidate.getNodeType() != Node.ELEMENT_NODE) {
628                 throw new ModelException("The signature to validate must be a ds:Signature Element!");
629             }
630 
631             XMLSignature xmlSignature;
632             try {
633                 xmlSignature = new XMLSignature((Element) signatureToValidate, baseUri);
634             } catch (XMLSecurityException e) {
635                 throw new ModelException("Unable to get XMLSignature element", e);
636             }
637 
638             X509Certificate cert = resolveCertificate(xmlSignature, federation);
639 
640             // Check that the certificate used for validation is trusted. If a Federation has been specified
641             // the signature must have been created by the STS. If no federation is specified, the
642             // certificate must be trusted in the CredentialVault.
643             if (checkForTrustedCertificates) {
644                 boolean trusted = false;
645                 if (federation != null) {
646                     trusted = federation.isValidSTSCertificate(cert);
647                 } else if (vault != null) {
648                     trusted = vault.isTrustedCertificate(cert);
649                 }
650                 if (!trusted) {
651                     throw new ModelException("The certificate that signed the security token is not trusted!");
652                 }
653 
654             }
655 
656             // Make sure that the ID elements references from the Reference elements
657             // can be looked up!
658             XmlUtil.ensureEnvelopedIds((Element) signatureToValidate);
659 
660             try {
661                 return xmlSignature.checkSignatureValue(cert);
662             } catch (XMLSignatureException e) {
663                 throw new ModelException("Unable to validate the xmlSignature", e);
664             }
665         } finally {
666             WSDocInfoStore.delete(wsDocInfo);
667         }
668     }
669 
670     private static X509Certificate resolveCertificate(XMLSignature xmlSignature, Federation federation) {
671         KeyInfo keyInfo = xmlSignature.getKeyInfo();
672         if (keyInfo.containsKeyName()) {
673             String keyName = resolveSingleKeyName(federation, keyInfo);
674             FederationCertificateReference federationCertificateReference = new FederationCertificateReference(keyName);
675             return federation.getFederationCertificate(federationCertificateReference);
676         } else {
677             try {
678                 return keyInfo.getX509Certificate();
679             } catch (KeyResolverException e) {
680                 throw new ModelException("Unable to get certificate from dom", e);
681             }
682         }
683     }
684 
685     private static String resolveSingleKeyName(Federation federation, KeyInfo keyInfo) {
686         int keyNameCount = keyInfo.lengthKeyName();
687         if (keyNameCount > 1) {
688             throw new ModelException("Unable to handle more than one keyname");
689         }
690         if (keyNameCount > 0 && federation == null) {
691             throw new ModelException("Will need federation to lookup certificate by keyName");
692         }
693         try {
694             return keyInfo.itemKeyName(0).getKeyName();
695         } catch (XMLSecurityException e) {
696             throw new ModelException("Unable to lookup keyName", e);
697         }
698     }
699 
700 }