1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.ws.security.saml;
21
22 import java.security.PublicKey;
23 import java.security.cert.X509Certificate;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 import javax.xml.crypto.XMLStructure;
28 import javax.xml.crypto.dom.DOMStructure;
29 import javax.xml.crypto.dsig.SignatureMethod;
30 import javax.xml.crypto.dsig.SignedInfo;
31 import javax.xml.crypto.dsig.XMLSignContext;
32 import javax.xml.crypto.dsig.dom.DOMSignContext;
33 import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
34 import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec;
35
36 import org.apache.ws.security.WSConstants;
37 import org.apache.ws.security.WSDocInfo;
38 import org.apache.ws.security.WSEncryptionPart;
39 import org.apache.ws.security.WSSConfig;
40 import org.apache.ws.security.WSSecurityException;
41 import org.apache.ws.security.components.crypto.Crypto;
42 import org.apache.ws.security.components.crypto.CryptoType;
43 import org.apache.ws.security.handler.RequestData;
44 import org.apache.ws.security.message.WSSecHeader;
45 import org.apache.ws.security.message.WSSecSignature;
46 import org.apache.ws.security.message.token.DOMX509Data;
47 import org.apache.ws.security.message.token.DOMX509IssuerSerial;
48 import org.apache.ws.security.message.token.Reference;
49 import org.apache.ws.security.message.token.SecurityTokenReference;
50 import org.apache.ws.security.message.token.X509Security;
51 import org.apache.ws.security.saml.ext.AssertionWrapper;
52 import org.apache.ws.security.saml.ext.OpenSAMLUtil;
53 import org.apache.ws.security.transform.STRTransform;
54 import org.apache.ws.security.util.WSSecurityUtil;
55 import org.w3c.dom.Document;
56 import org.w3c.dom.Element;
57
58 public class WSSecSignatureSAML extends WSSecSignature {
59
60 private static org.apache.commons.logging.Log log =
61 org.apache.commons.logging.LogFactory.getLog(WSSecSignatureSAML.class);
62 private boolean senderVouches = false;
63 private SecurityTokenReference secRefSaml = null;
64 private String secRefID = null;
65 private Element samlToken = null;
66 private Crypto userCrypto = null;
67 private Crypto issuerCrypto = null;
68 private String issuerKeyName = null;
69 private String issuerKeyPW = null;
70 private boolean useDirectReferenceToAssertion = false;
71
72
73
74
75 public WSSecSignatureSAML() {
76 super();
77 doDebug = log.isDebugEnabled();
78 }
79
80
81
82 public WSSecSignatureSAML(WSSConfig config) {
83 super(config);
84 doDebug = log.isDebugEnabled();
85 }
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 public Document build(
113 Document doc, Crypto uCrypto, AssertionWrapper assertion,
114 Crypto iCrypto, String iKeyName, String iKeyPW, WSSecHeader secHeader
115 ) throws WSSecurityException {
116
117 prepare(doc, uCrypto, assertion, iCrypto, iKeyName, iKeyPW, secHeader);
118
119 String soapNamespace = WSSecurityUtil.getSOAPNamespace(doc.getDocumentElement());
120 if (parts == null) {
121 parts = new ArrayList<WSEncryptionPart>(1);
122 WSEncryptionPart encP =
123 new WSEncryptionPart(WSConstants.ELEM_BODY, soapNamespace, "Content");
124 parts.add(encP);
125 } else {
126 for (WSEncryptionPart part : parts) {
127 if ("STRTransform".equals(part.getName()) && part.getId() == null) {
128 part.setId(strUri);
129 }
130 }
131 }
132
133
134
135
136
137 if (secRefID != null) {
138 WSEncryptionPart encP =
139 new WSEncryptionPart("STRTransform", soapNamespace, "Content");
140 encP.setId(secRefID);
141 parts.add(encP);
142 }
143
144 List<javax.xml.crypto.dsig.Reference> referenceList =
145 addReferencesToSign(parts, secHeader);
146
147 prependSAMLElementsToHeader(secHeader);
148
149 if (senderVouches) {
150 computeSignature(referenceList, secHeader, secRefSaml.getElement());
151 } else {
152 computeSignature(referenceList, secHeader, samlToken);
153 }
154
155
156
157
158
159 if (bstToken != null) {
160 prependBSTElementToHeader(secHeader);
161 }
162
163 return doc;
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194 public void prepare(
195 Document doc, Crypto uCrypto, AssertionWrapper assertion, Crypto iCrypto,
196 String iKeyName, String iKeyPW, WSSecHeader secHeader
197 ) throws WSSecurityException {
198
199 if (doDebug) {
200 log.debug("Beginning ST signing...");
201 }
202
203 userCrypto = uCrypto;
204 issuerCrypto = iCrypto;
205 document = doc;
206 issuerKeyName = iKeyName;
207 issuerKeyPW = iKeyPW;
208
209 samlToken = (Element) assertion.toDOM(doc);
210
211
212
213
214
215
216
217 String confirmMethod = null;
218 List<String> methods = assertion.getConfirmationMethods();
219 if (methods != null && methods.size() > 0) {
220 confirmMethod = methods.get(0);
221 }
222 if (OpenSAMLUtil.isMethodSenderVouches(confirmMethod)) {
223 senderVouches = true;
224 }
225
226
227
228
229 wsDocInfo = new WSDocInfo(doc);
230
231
232 X509Certificate[] certs = null;
233 PublicKey publicKey = null;
234
235 if (senderVouches) {
236 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
237 cryptoType.setAlias(issuerKeyName);
238 certs = issuerCrypto.getX509Certificates(cryptoType);
239 wsDocInfo.setCrypto(issuerCrypto);
240 }
241
242
243
244
245
246 else {
247 if (userCrypto == null || !assertion.isSigned()) {
248 throw new WSSecurityException(
249 WSSecurityException.FAILURE,
250 "invalidSAMLsecurity",
251 new Object[] { "for SAML Signature (Key Holder)" }
252 );
253 }
254 if (secretKey == null) {
255 RequestData data = new RequestData();
256 data.setSigCrypto(userCrypto);
257 data.setWssConfig(getWsConfig());
258 SAMLKeyInfo samlKeyInfo =
259 SAMLUtil.getCredentialFromSubject(
260 assertion, data, wsDocInfo, getWsConfig().isWsiBSPCompliant()
261 );
262 publicKey = samlKeyInfo.getPublicKey();
263 certs = samlKeyInfo.getCerts();
264 wsDocInfo.setCrypto(userCrypto);
265 }
266 }
267 if ((certs == null || certs.length == 0 || certs[0] == null)
268 && publicKey == null && secretKey == null) {
269 throw new WSSecurityException(
270 WSSecurityException.FAILURE,
271 "noCertsFound",
272 new Object[] { "SAML signature" }
273 );
274 }
275
276 if (sigAlgo == null) {
277 PublicKey key = null;
278 if (certs != null && certs[0] != null) {
279 key = certs[0].getPublicKey();
280 } else if (publicKey != null) {
281 key = publicKey;
282 } else {
283 throw new WSSecurityException(
284 WSSecurityException.FAILURE, "unknownSignatureAlgorithm"
285 );
286 }
287
288 String pubKeyAlgo = key.getAlgorithm();
289 log.debug("automatic sig algo detection: " + pubKeyAlgo);
290 if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
291 sigAlgo = WSConstants.DSA;
292 } else if (pubKeyAlgo.equalsIgnoreCase("RSA")) {
293 sigAlgo = WSConstants.RSA;
294 } else {
295 throw new WSSecurityException(
296 WSSecurityException.FAILURE,
297 "unknownSignatureAlgorithm",
298 new Object[] {
299 pubKeyAlgo
300 }
301 );
302 }
303 }
304 sig = null;
305
306 try {
307 C14NMethodParameterSpec c14nSpec = null;
308 if (getWsConfig().isWsiBSPCompliant() && canonAlgo.equals(WSConstants.C14N_EXCL_OMIT_COMMENTS)) {
309 List<String> prefixes =
310 getInclusivePrefixes(secHeader.getSecurityHeader(), false);
311 c14nSpec = new ExcC14NParameterSpec(prefixes);
312 }
313
314 c14nMethod = signatureFactory.newCanonicalizationMethod(canonAlgo, c14nSpec);
315 } catch (Exception ex) {
316 log.error("", ex);
317 throw new WSSecurityException(
318 WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null, ex
319 );
320 }
321
322 keyInfoUri = getWsConfig().getIdAllocator().createSecureId("KeyId-", keyInfo);
323 secRef = new SecurityTokenReference(doc);
324 strUri = getWsConfig().getIdAllocator().createSecureId("STRId-", secRef);
325 secRef.setID(strUri);
326
327 if (certs != null && certs.length != 0) {
328 certUri = getWsConfig().getIdAllocator().createSecureId("CertId-", certs[0]);
329 }
330
331
332
333
334
335
336
337
338
339 try {
340 if (senderVouches) {
341 secRefSaml = new SecurityTokenReference(doc);
342 secRefID = getWsConfig().getIdAllocator().createSecureId("STRSAMLId-", secRefSaml);
343 secRefSaml.setID(secRefID);
344
345 if (useDirectReferenceToAssertion) {
346 Reference ref = new Reference(doc);
347 ref.setURI("#" + assertion.getId());
348 if (assertion.getSaml1() != null) {
349 ref.setValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
350 secRefSaml.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
351 } else if (assertion.getSaml2() != null) {
352 secRefSaml.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
353 }
354 secRefSaml.setReference(ref);
355 } else {
356 Element keyId = doc.createElementNS(WSConstants.WSSE_NS, "wsse:KeyIdentifier");
357 String valueType = null;
358 if (assertion.getSaml1() != null) {
359 valueType = WSConstants.WSS_SAML_KI_VALUE_TYPE;
360 secRefSaml.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
361 } else if (assertion.getSaml2() != null) {
362 valueType = WSConstants.WSS_SAML2_KI_VALUE_TYPE;
363 secRefSaml.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
364 }
365 keyId.setAttributeNS(
366 null, "ValueType", valueType
367 );
368 keyId.appendChild(doc.createTextNode(assertion.getId()));
369 Element elem = secRefSaml.getElement();
370 elem.appendChild(keyId);
371 }
372 wsDocInfo.addTokenElement(secRefSaml.getElement(), false);
373 }
374 } catch (Exception ex) {
375 throw new WSSecurityException(
376 WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null, ex
377 );
378 }
379
380 if (senderVouches) {
381 switch (keyIdentifierType) {
382 case WSConstants.BST_DIRECT_REFERENCE:
383 Reference ref = new Reference(doc);
384 ref.setURI("#" + certUri);
385 bstToken = new X509Security(doc);
386 ((X509Security) bstToken).setX509Certificate(certs[0]);
387 bstToken.setID(certUri);
388 wsDocInfo.addTokenElement(bstToken.getElement(), false);
389 ref.setValueType(bstToken.getValueType());
390 secRef.setReference(ref);
391 break;
392
393 case WSConstants.X509_KEY_IDENTIFIER :
394 secRef.setKeyIdentifier(certs[0]);
395 break;
396
397 case WSConstants.SKI_KEY_IDENTIFIER:
398 secRef.setKeyIdentifierSKI(certs[0], iCrypto != null ? iCrypto : uCrypto);
399 break;
400
401 case WSConstants.THUMBPRINT_IDENTIFIER:
402 secRef.setKeyIdentifierThumb(certs[0]);
403 break;
404
405 case WSConstants.ISSUER_SERIAL:
406 final String issuer = certs[0].getIssuerDN().getName();
407 final java.math.BigInteger serialNumber = certs[0].getSerialNumber();
408 final DOMX509IssuerSerial domIssuerSerial =
409 new DOMX509IssuerSerial(document, issuer, serialNumber);
410 final DOMX509Data domX509Data = new DOMX509Data(document, domIssuerSerial);
411 secRef.setX509Data(domX509Data);
412 break;
413
414 default:
415 throw new WSSecurityException(
416 WSSecurityException.FAILURE, "unsupportedKeyId", new Object[]{}
417 );
418 }
419 } else if (useDirectReferenceToAssertion) {
420 Reference ref = new Reference(doc);
421 ref.setURI("#" + assertion.getId());
422 if (assertion.getSaml1() != null) {
423 ref.setValueType(WSConstants.WSS_SAML_KI_VALUE_TYPE);
424 secRef.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
425 } else if (assertion.getSaml2() != null) {
426 secRef.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
427 }
428 secRef.setReference(ref);
429 } else {
430 Element keyId = doc.createElementNS(WSConstants.WSSE_NS, "wsse:KeyIdentifier");
431 String valueType = null;
432 if (assertion.getSaml1() != null) {
433 valueType = WSConstants.WSS_SAML_KI_VALUE_TYPE;
434 secRef.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
435 } else if (assertion.getSaml2() != null) {
436 valueType = WSConstants.WSS_SAML2_KI_VALUE_TYPE;
437 secRef.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
438 }
439 keyId.setAttributeNS(
440 null, "ValueType", valueType
441 );
442 keyId.appendChild(doc.createTextNode(assertion.getId()));
443 Element elem = secRef.getElement();
444 elem.appendChild(keyId);
445 }
446 XMLStructure structure = new DOMStructure(secRef.getElement());
447 wsDocInfo.addTokenElement(secRef.getElement(), false);
448
449 keyInfo =
450 keyInfoFactory.newKeyInfo(
451 java.util.Collections.singletonList(structure), keyInfoUri
452 );
453
454 wsDocInfo.addTokenElement(samlToken, false);
455 }
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470 public void prependSAMLElementsToHeader(WSSecHeader secHeader) {
471 if (senderVouches) {
472 WSSecurityUtil.prependChildElement(
473 secHeader.getSecurityHeader(), secRefSaml.getElement()
474 );
475 }
476
477 WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), samlToken);
478 }
479
480
481
482
483
484
485
486
487
488
489
490 public void computeSignature(
491 List<javax.xml.crypto.dsig.Reference> referenceList,
492 WSSecHeader secHeader,
493 Element siblingElement
494 ) throws WSSecurityException {
495 try {
496 java.security.Key key;
497 if (senderVouches) {
498 key = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPW);
499 } else if (secretKey != null) {
500 key = WSSecurityUtil.prepareSecretKey(sigAlgo, secretKey);
501 } else {
502 key = userCrypto.getPrivateKey(user, password);
503 }
504 SignatureMethod signatureMethod =
505 signatureFactory.newSignatureMethod(sigAlgo, null);
506 SignedInfo signedInfo =
507 signatureFactory.newSignedInfo(c14nMethod, signatureMethod, referenceList);
508
509 sig = signatureFactory.newXMLSignature(
510 signedInfo,
511 keyInfo,
512 null,
513 getWsConfig().getIdAllocator().createId("SIG-", null),
514 null);
515
516 org.w3c.dom.Element securityHeaderElement = secHeader.getSecurityHeader();
517
518
519
520 XMLSignContext signContext = null;
521 if (siblingElement != null && siblingElement.getNextSibling() != null) {
522 signContext =
523 new DOMSignContext(key, securityHeaderElement, siblingElement.getNextSibling());
524 } else {
525 signContext = new DOMSignContext(key, securityHeaderElement);
526 }
527 signContext.putNamespacePrefix(WSConstants.SIG_NS, WSConstants.SIG_PREFIX);
528 if (WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(canonAlgo)) {
529 signContext.putNamespacePrefix(
530 WSConstants.C14N_EXCL_OMIT_COMMENTS,
531 WSConstants.C14N_EXCL_OMIT_COMMENTS_PREFIX
532 );
533 }
534 signContext.setProperty(STRTransform.TRANSFORM_WS_DOC_INFO, wsDocInfo);
535 wsDocInfo.setCallbackLookup(callbackLookup);
536
537
538 wsDocInfo.setTokensOnContext((DOMSignContext)signContext);
539
540 if (secRefSaml != null && secRefSaml.getElement() != null) {
541 WSSecurityUtil.storeElementInContext((DOMSignContext)signContext, secRefSaml.getElement());
542 }
543 if (secRef != null && secRef.getElement() != null) {
544 WSSecurityUtil.storeElementInContext((DOMSignContext)signContext, secRef.getElement());
545 }
546 sig.sign(signContext);
547
548 signatureValue = sig.getSignatureValue().getValue();
549 } catch (Exception ex) {
550 log.error(ex);
551 throw new WSSecurityException(
552 WSSecurityException.FAILED_SIGNATURE, null, null, ex
553 );
554 }
555 }
556
557
558
559
560
561
562 public boolean isUseDirectReferenceToAssertion() {
563 return useDirectReferenceToAssertion;
564 }
565
566
567
568
569
570
571
572 public void setUseDirectReferenceToAssertion(boolean useDirectReferenceToAssertion) {
573 this.useDirectReferenceToAssertion = useDirectReferenceToAssertion;
574 }
575
576 }