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