1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.ws.soap.security.wss4j;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.security.Principal;
23 import java.security.cert.X509Certificate;
24 import java.util.Vector;
25 import javax.security.auth.callback.Callback;
26 import javax.security.auth.callback.CallbackHandler;
27 import javax.security.auth.callback.UnsupportedCallbackException;
28 import javax.xml.soap.MessageFactory;
29
30 import org.apache.axiom.soap.SOAPEnvelope;
31 import org.apache.axiom.soap.SOAPFactory;
32 import org.apache.axiom.soap.SOAPMessage;
33 import org.apache.ws.security.WSConstants;
34 import org.apache.ws.security.WSSecurityEngine;
35 import org.apache.ws.security.WSSecurityEngineResult;
36 import org.apache.ws.security.WSSecurityException;
37 import org.apache.ws.security.WSUsernameTokenPrincipal;
38 import org.apache.ws.security.components.crypto.Crypto;
39 import org.apache.ws.security.handler.RequestData;
40 import org.apache.ws.security.handler.WSHandlerConstants;
41 import org.apache.ws.security.handler.WSHandlerResult;
42 import org.apache.ws.security.message.token.Timestamp;
43 import org.apache.ws.security.util.WSSecurityUtil;
44 import org.w3c.dom.Document;
45
46 import org.springframework.beans.factory.InitializingBean;
47 import org.springframework.util.Assert;
48 import org.springframework.util.CollectionUtils;
49 import org.springframework.util.StringUtils;
50 import org.springframework.ws.context.DefaultMessageContext;
51 import org.springframework.ws.context.MessageContext;
52 import org.springframework.ws.soap.SoapMessage;
53 import org.springframework.ws.soap.axiom.AxiomSoapMessage;
54 import org.springframework.ws.soap.axiom.support.AxiomUtils;
55 import org.springframework.ws.soap.saaj.SaajSoapMessage;
56 import org.springframework.ws.soap.saaj.SaajSoapMessageException;
57 import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
58 import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
59 import org.springframework.ws.soap.security.WsSecuritySecurementException;
60 import org.springframework.ws.soap.security.WsSecurityValidationException;
61 import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
62 import org.springframework.ws.soap.security.callback.CleanupCallback;
63 import org.springframework.ws.soap.security.wss4j.callback.UsernameTokenPrincipalCallback;
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor implements InitializingBean {
95
96 public static final String SECUREMENT_USER_PROPERTY_NAME = "Wss4jSecurityInterceptor.securementUser";
97
98 private int securementAction;
99
100 private String securementActions;
101
102 private Vector securementActionsVector;
103
104 private String securementUsername;
105
106 private CallbackHandler validationCallbackHandler;
107
108 private int validationAction;
109
110 private String validationActions;
111
112 private Vector validationActionsVector;
113
114 private String validationActor;
115
116 private Crypto validationDecryptionCrypto;
117
118 private Crypto validationSignatureCrypto;
119
120 private boolean timestampStrict = true;
121
122 private boolean enableSignatureConfirmation;
123
124 private int validationTimeToLive = 300;
125
126 private int securementTimeToLive = 300;
127
128 private final Wss4jHandler handler = new Wss4jHandler();
129
130 private final WSSecurityEngine securityEngine = WSSecurityEngine.getInstance();
131
132 public void setSecurementActions(String securementActions) {
133 this.securementActions = securementActions;
134 securementActionsVector = new Vector();
135 try {
136 securementAction = WSSecurityUtil.decodeAction(securementActions, securementActionsVector);
137 }
138 catch (WSSecurityException ex) {
139 throw new IllegalArgumentException(ex);
140 }
141 }
142
143
144
145
146
147
148
149
150 public void setSecurementActor(String securementActor) {
151 handler.setOption(WSHandlerConstants.ACTOR, securementActor);
152 }
153
154
155
156
157
158
159 public void setSecurementCallbackHandler(CallbackHandler securementCallbackHandler) {
160 handler.setSecurementCallbackHandler(securementCallbackHandler);
161 }
162
163
164
165
166
167
168 public void setSecurementCallbackHandlers(CallbackHandler[] securementCallbackHandler) {
169 handler.setSecurementCallbackHandler(new CallbackHandlerChain(securementCallbackHandler));
170 }
171
172 public void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
173 handler.setSecurementEncryptionCrypto(securementEncryptionCrypto);
174 }
175
176
177 public void setSecurementEncryptionEmbeddedKeyName(String securementEncryptionEmbeddedKeyName) {
178 handler.setOption(WSHandlerConstants.ENC_KEY_NAME, securementEncryptionEmbeddedKeyName);
179 }
180
181
182
183
184
185
186
187
188 public void setSecurementEncryptionKeyIdentifier(String securementEncryptionKeyIdentifier) {
189 handler.setOption(WSHandlerConstants.ENC_KEY_ID, securementEncryptionKeyIdentifier);
190 }
191
192
193
194
195
196 public void setSecurementEncryptionKeyTransportAlgorithm(String securementEncryptionKeyTransportAlgorithm) {
197 handler.setOption(WSHandlerConstants.ENC_KEY_TRANSPORT, securementEncryptionKeyTransportAlgorithm);
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 public void setSecurementEncryptionParts(String securementEncryptionParts) {
232 handler.setOption(WSHandlerConstants.ENCRYPTION_PARTS, securementEncryptionParts);
233 }
234
235
236
237
238
239
240
241 public void setSecurementEncryptionSymAlgorithm(String securementEncryptionSymAlgorithm) {
242 this.handler.setOption(WSHandlerConstants.ENC_SYM_ALGO, securementEncryptionSymAlgorithm);
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 public void setSecurementEncryptionUser(String securementEncryptionUser) {
264 handler.setOption(WSHandlerConstants.ENCRYPTION_USER, securementEncryptionUser);
265 }
266
267 public void setSecurementPassword(String securementPassword) {
268 this.handler.setSecurementPassword(securementPassword);
269 }
270
271
272
273
274
275
276
277
278 public void setSecurementPasswordType(String securementUsernameTokenPasswordType) {
279 handler.setOption(WSHandlerConstants.PASSWORD_TYPE, securementUsernameTokenPasswordType);
280 }
281
282
283
284
285
286
287 public void setSecurementSignatureAlgorithm(String securementSignatureAlgorithm) {
288 handler.setOption(WSHandlerConstants.SIG_ALGO, securementSignatureAlgorithm);
289 }
290
291 public void setSecurementSignatureCrypto(Crypto securementSignatureCrypto) {
292 handler.setSecurementSignatureCrypto(securementSignatureCrypto);
293 }
294
295
296
297
298
299
300
301 public void setSecurementSignatureKeyIdentifier(String securementSignatureKeyIdentifier) {
302 handler.setOption(WSHandlerConstants.SIG_KEY_ID, securementSignatureKeyIdentifier);
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328 public void setSecurementSignatureParts(String securementSignatureParts) {
329 handler.setOption(WSHandlerConstants.SIGNATURE_PARTS, securementSignatureParts);
330 }
331
332
333
334
335
336
337
338
339
340
341
342 public void setSecurementSignatureUser(String securementSignatureUser) {
343 handler.setOption(WSHandlerConstants.SIGNATURE_USER, securementSignatureUser);
344 }
345
346
347 public void setSecurementUsername(String securementUsername) {
348 this.securementUsername = securementUsername;
349 }
350
351
352 public void setSecurementTimeToLive(int securementTimeToLive) {
353 if (securementTimeToLive <= 0) {
354 throw new IllegalArgumentException("timeToLive must be positive");
355 }
356 this.securementTimeToLive = securementTimeToLive;
357 }
358
359
360 public void setValidationTimeToLive(int validationTimeToLive) {
361 if (validationTimeToLive <= 0) {
362 throw new IllegalArgumentException("timeToLive must be positive");
363 }
364 this.validationTimeToLive = validationTimeToLive;
365 }
366
367
368
369
370 public void setTimeToLive(int timeToLive) {
371 setValidationTimeToLive(timeToLive);
372 }
373
374
375 public void setValidationActions(String actions) {
376 this.validationActions = actions;
377 try {
378 validationActionsVector = new Vector();
379 validationAction = WSSecurityUtil.decodeAction(actions, validationActionsVector);
380 }
381 catch (WSSecurityException ex) {
382 throw new IllegalArgumentException(ex);
383 }
384 }
385
386 public void setValidationActor(String validationActor) {
387 this.validationActor = validationActor;
388 }
389
390
391
392
393
394
395 public void setValidationCallbackHandler(CallbackHandler callbackHandler) {
396 this.validationCallbackHandler = callbackHandler;
397 }
398
399
400
401
402
403
404 public void setValidationCallbackHandlers(CallbackHandler[] callbackHandler) {
405 this.validationCallbackHandler = new CallbackHandlerChain(callbackHandler);
406 }
407
408
409 public void setValidationDecryptionCrypto(Crypto decryptionCrypto) {
410 this.validationDecryptionCrypto = decryptionCrypto;
411 }
412
413
414 public void setValidationSignatureCrypto(Crypto signatureCrypto) {
415 this.validationSignatureCrypto = signatureCrypto;
416 }
417
418
419 public void setEnableSignatureConfirmation(boolean enableSignatureConfirmation) {
420 handler.setOption(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, enableSignatureConfirmation);
421 this.enableSignatureConfirmation = enableSignatureConfirmation;
422 }
423
424
425 public void setTimestampPrecisionInMilliseconds(boolean timestampPrecisionInMilliseconds) {
426 handler.setOption(WSHandlerConstants.TIMESTAMP_PRECISION, timestampPrecisionInMilliseconds);
427 }
428
429
430 public void setTimestampStrict(boolean timestampStrict) {
431 this.timestampStrict = timestampStrict;
432 }
433
434
435
436
437
438 public void setSecurementMustUnderstand(boolean securementMustUnderstand) {
439 handler.setOption(WSHandlerConstants.MUST_UNDERSTAND, securementMustUnderstand);
440 }
441
442
443
444
445
446
447
448
449
450
451
452 public void setSecurementUsernameTokenElements(String securementUsernameTokenElements) {
453 handler.setOption(WSHandlerConstants.ADD_UT_ELEMENTS, securementUsernameTokenElements);
454 }
455
456 public void afterPropertiesSet() throws Exception {
457 Assert.isTrue(validationActions != null || securementActions != null,
458 "validationActions or securementActions are required");
459 if (validationActions != null) {
460 if ((validationAction & WSConstants.UT) != 0) {
461 Assert.notNull(validationCallbackHandler, "validationCallbackHandler is required");
462 }
463
464 if ((validationAction & WSConstants.SIGN) != 0) {
465 Assert.notNull(validationSignatureCrypto, "validationSignatureCrypto is required");
466 }
467 }
468
469
470
471
472 securityEngine.getWssConfig().setAllowNamespaceQualifiedPasswordTypes(true);
473 }
474
475 protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
476 throws WsSecuritySecurementException {
477 if (securementAction == WSConstants.NO_SECURITY && !enableSignatureConfirmation) {
478 return;
479 }
480 if (logger.isDebugEnabled()) {
481 logger.debug("Securing message [" + soapMessage + "] with actions [" + securementActions + "]");
482 }
483 RequestData requestData = initializeRequestData(messageContext);
484
485 Document envelopeAsDocument = toDocument(soapMessage, messageContext);
486 try {
487
488
489
490 if (securementAction == WSConstants.NO_SECURITY) {
491 securementActionsVector = new Vector(0);
492 }
493
494 handler.doSenderAction(securementAction, envelopeAsDocument, requestData, securementActionsVector, false);
495 }
496 catch (WSSecurityException ex) {
497 throw new Wss4jSecuritySecurementException(ex.getMessage(), ex);
498 }
499
500 replaceMessage(soapMessage, envelopeAsDocument);
501 }
502
503
504 private RequestData initializeRequestData(MessageContext messageContext) {
505 RequestData requestData = new RequestData();
506 requestData.setMsgContext(messageContext);
507
508
509
510 String contextUsername = (String) messageContext.getProperty(SECUREMENT_USER_PROPERTY_NAME);
511 if (StringUtils.hasLength(contextUsername)) {
512 requestData.setUsername(contextUsername);
513 }
514 else {
515 requestData.setUsername(securementUsername);
516 }
517
518 requestData.setTimeToLive(securementTimeToLive);
519
520 return requestData;
521 }
522
523 protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
524 throws WsSecurityValidationException {
525 if (logger.isDebugEnabled()) {
526 logger.debug("Validating message [" + soapMessage + "] with actions [" + validationActions + "]");
527 }
528
529 if (validationAction == WSConstants.NO_SECURITY) {
530 return;
531 }
532
533 Document envelopeAsDocument = toDocument(soapMessage, messageContext);
534
535
536
537 try {
538 Vector results = securityEngine
539 .processSecurityHeader(envelopeAsDocument, validationActor, validationCallbackHandler,
540 validationSignatureCrypto, validationDecryptionCrypto);
541
542
543 if (CollectionUtils.isEmpty(results)) {
544 throw new Wss4jSecurityValidationException("No WS-Security header found");
545 }
546
547 checkResults(results, validationActionsVector);
548
549
550
551 updateContextWithResults(messageContext, results);
552
553 verifyCertificateTrust(results);
554
555 verifyTimestamp(results);
556
557 processPrincipal(results);
558 }
559 catch (WSSecurityException ex) {
560 throw new Wss4jSecurityValidationException(ex.getMessage(), ex);
561 }
562
563 replaceMessage(soapMessage, envelopeAsDocument);
564
565 soapMessage.getEnvelope().getHeader().removeHeaderElement(WS_SECURITY_NAME);
566 }
567
568
569
570
571
572
573
574
575 protected void checkResults(Vector results, Vector validationActionsVector)
576 throws Wss4jSecurityValidationException {
577 if (!handler.checkReceiverResultsAnyOrder(results, validationActionsVector)) {
578 throw new Wss4jSecurityValidationException("Security processing failed (actions mismatch)");
579 }
580 }
581
582
583
584
585
586 private void updateContextWithResults(MessageContext messageContext, Vector results) {
587 Vector handlerResults;
588 if ((handlerResults = (Vector) messageContext.getProperty(WSHandlerConstants.RECV_RESULTS)) == null) {
589 handlerResults = new Vector();
590 messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
591 }
592 WSHandlerResult rResult = new WSHandlerResult(validationActor, results);
593 handlerResults.add(0, rResult);
594 messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
595 }
596
597
598 protected void verifyCertificateTrust(Vector results) throws WSSecurityException {
599 RequestData requestData = new RequestData();
600 requestData.setSigCrypto(validationSignatureCrypto);
601 WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
602
603 if (actionResult != null) {
604 X509Certificate returnCert = (X509Certificate) actionResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE)
605 ;
606 if (!handler.verifyTrust(returnCert, requestData)) {
607 throw new Wss4jSecurityValidationException("The certificate used for the signature is not trusted");
608 }
609 }
610 }
611
612
613 protected void verifyTimestamp(Vector results) throws WSSecurityException {
614 WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.TS);
615
616 if (actionResult != null) {
617 Timestamp timestamp = (Timestamp) actionResult.get(WSSecurityEngineResult.TAG_TIMESTAMP);
618 if (timestamp != null && timestampStrict) {
619 if (!handler.verifyTimestamp(timestamp, validationTimeToLive)) {
620 throw new Wss4jSecurityValidationException("Invalid timestamp : " + timestamp.getID());
621 }
622 }
623 }
624 }
625
626 private void processPrincipal(Vector results) {
627 WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.UT);
628
629 if (actionResult != null) {
630 Principal principal = (Principal) actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
631 if (principal != null && principal instanceof WSUsernameTokenPrincipal) {
632 WSUsernameTokenPrincipal usernameTokenPrincipal = (WSUsernameTokenPrincipal) principal;
633 UsernameTokenPrincipalCallback callback = new UsernameTokenPrincipalCallback(usernameTokenPrincipal);
634 try {
635 validationCallbackHandler.handle(new Callback[]{callback});
636 }
637 catch (IOException ex) {
638 logger.warn("Principal callback resulted in IOException", ex);
639 }
640 catch (UnsupportedCallbackException ex) {
641
642 }
643 }
644 }
645 }
646
647
648 private Document toDocument(SoapMessage soapMessage, MessageContext messageContext) {
649 if (soapMessage instanceof SaajSoapMessage) {
650 SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage;
651
652 Assert.isInstanceOf(DefaultMessageContext.class, messageContext);
653 DefaultMessageContext defaultMessageContext = (DefaultMessageContext) messageContext;
654 Assert.isInstanceOf(SaajSoapMessageFactory.class, defaultMessageContext.getMessageFactory());
655 MessageFactory messageFactory =
656 ((SaajSoapMessageFactory) defaultMessageContext.getMessageFactory()).getMessageFactory();
657 try {
658 ByteArrayOutputStream bos = new ByteArrayOutputStream();
659 saajSoapMessage.writeTo(bos);
660 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
661 javax.xml.soap.SOAPMessage saajMessage =
662 messageFactory.createMessage(saajSoapMessage.getSaajMessage().getMimeHeaders(), bis);
663 saajSoapMessage.setSaajMessage(saajMessage);
664 return saajMessage.getSOAPPart();
665 }
666 catch (Exception ex) {
667 throw new SaajSoapMessageException("Could not save changes", ex);
668 }
669 }
670 else if (soapMessage instanceof AxiomSoapMessage) {
671 AxiomSoapMessage axiomMessage = (AxiomSoapMessage) soapMessage;
672 return AxiomUtils.toDocument(axiomMessage.getAxiomMessage().getSOAPEnvelope());
673 }
674 else {
675 throw new IllegalArgumentException("Message type not supported [" + soapMessage + "]");
676 }
677 }
678
679
680
681
682
683 private void replaceMessage(SoapMessage soapMessage, Document envelope) {
684 if (soapMessage instanceof AxiomSoapMessage) {
685
686 AxiomSoapMessage axiomMessage = (AxiomSoapMessage) soapMessage;
687
688 String soapAction = axiomMessage.getSoapAction();
689 SOAPEnvelope envelopeFromDOMDocument = AxiomUtils.toEnvelope(envelope);
690 SOAPFactory factory = (SOAPFactory) axiomMessage.getAxiomMessage().getOMFactory();
691 SOAPMessage newMessage = factory.createSOAPMessage();
692 newMessage.setSOAPEnvelope(envelopeFromDOMDocument);
693
694
695 axiomMessage.setAxiomMessage(newMessage);
696
697 axiomMessage.setSoapAction(soapAction);
698 }
699 }
700
701 protected void cleanUp() {
702 if (validationCallbackHandler != null) {
703 try {
704 CleanupCallback cleanupCallback = new CleanupCallback();
705 validationCallbackHandler.handle(new Callback[]{cleanupCallback});
706 }
707 catch (IOException ex) {
708 logger.warn("Cleanup callback resulted in IOException", ex);
709 }
710 catch (UnsupportedCallbackException ex) {
711
712 }
713 }
714 }
715 }