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