157
157
* asserting party, IDP, verification certificates.
158
158
* </p>
159
159
*
160
+ * @author Ryan Cassar
160
161
* @since 5.2
161
162
* @see <a href=
162
163
* "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38">SAML 2
@@ -211,6 +212,32 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi
211
212
212
213
private Converter <Saml2AuthenticationToken , Decrypter > decrypterConverter = new DecrypterConverter ();
213
214
215
+ private Consumer <ResponseToken > assertionDecrypter = (responseToken ) -> {
216
+ List <Assertion > assertions = new ArrayList <>();
217
+ for (EncryptedAssertion encryptedAssertion : responseToken .getResponse ().getEncryptedAssertions ()) {
218
+ try {
219
+ Decrypter decrypter = this .decrypterConverter .convert (responseToken .getToken ());
220
+ Assertion assertion = decrypter .decrypt (encryptedAssertion );
221
+ assertions .add (assertion );
222
+ }
223
+ catch (DecryptionException ex ) {
224
+ throw createAuthenticationException (Saml2ErrorCodes .DECRYPTION_ERROR , ex .getMessage (), ex );
225
+ }
226
+ }
227
+ responseToken .getResponse ().getAssertions ().addAll (assertions );
228
+ };
229
+
230
+ private Consumer <ResponseToken > principalDecrypter = (responseToken ) -> {
231
+ try {
232
+ Decrypter decrypter = this .decrypterConverter .convert (responseToken .getToken ());
233
+ Assertion assertion = CollectionUtils .firstElement (responseToken .getResponse ().getAssertions ());
234
+ assertion .getSubject ().setNameID ((NameID ) decrypter .decrypt (assertion .getSubject ().getEncryptedID ()));
235
+ }
236
+ catch (DecryptionException ex ) {
237
+ throw createAuthenticationException (Saml2ErrorCodes .DECRYPTION_ERROR , ex .getMessage (), ex );
238
+ }
239
+ };
240
+
214
241
/**
215
242
* Creates an {@link OpenSamlAuthenticationProvider}
216
243
*/
@@ -332,6 +359,52 @@ public void setResponseTimeValidationSkew(Duration responseTimeValidationSkew) {
332
359
this .responseTimeValidationSkew = responseTimeValidationSkew ;
333
360
}
334
361
362
+ /**
363
+ * Sets the assertion response custom decrypter.
364
+ *
365
+ * You can use this method like so:
366
+ *
367
+ * <pre>
368
+ * YourDecrypter decrypter = // ... your custom decrypter
369
+ *
370
+ * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
371
+ * provider.setAssertionDecrypter((responseToken) -> {
372
+ * Response response = responseToken.getResponse();
373
+ * EncryptedAssertion encrypted = response.getEncryptedAssertions().get(0);
374
+ * Assertion assertion = decrypter.decrypt(encrypted);
375
+ * response.getAssertions().add(assertion);
376
+ * });
377
+ * </pre>
378
+ * @param assertionDecrypter response token consumer
379
+ */
380
+ public void setAssertionDecrypter (Consumer <ResponseToken > assertionDecrypter ) {
381
+ Assert .notNull (assertionDecrypter , "Consumer<ResponseToken> required" );
382
+ this .assertionDecrypter = assertionDecrypter ;
383
+ }
384
+
385
+ /**
386
+ * Sets the principal custom decrypter.
387
+ *
388
+ * You can use this method like so:
389
+ *
390
+ * <pre>
391
+ * YourDecrypter decrypter = // ... your custom decrypter
392
+ *
393
+ * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
394
+ * provider.setAssertionDecrypter((responseToken) -> {
395
+ * Assertion assertion = CollectionUtils.firstElement(responseToken.getResponse().getAssertions());
396
+ * EncryptedID encrypted = assertion.getSubject().getEncryptedID();
397
+ * NameID name = decrypter.decrypt(encrypted);
398
+ * assertion.getSubject().setNameID(name)
399
+ * });
400
+ * </pre>
401
+ * @param principalDecrypter response token consumer
402
+ */
403
+ public void setPrincipalDecrypter (Consumer <ResponseToken > principalDecrypter ) {
404
+ Assert .notNull (principalDecrypter , "Consumer<ResponseToken> required" );
405
+ this .principalDecrypter = principalDecrypter ;
406
+ }
407
+
335
408
/**
336
409
* Construct a default strategy for validating each SAML 2.0 Assertion and associated
337
410
* {@link Authentication} token
@@ -429,8 +502,8 @@ private void process(Saml2AuthenticationToken token, Response response) {
429
502
boolean responseSigned = response .isSigned ();
430
503
Saml2ResponseValidatorResult result = validateResponse (token , response );
431
504
432
- Decrypter decrypter = this . decrypterConverter . convert ( token );
433
- List <Assertion > assertions = decryptAssertions (decrypter , response );
505
+ ResponseToken responseToken = new ResponseToken ( response , token );
506
+ List <Assertion > assertions = decryptAssertions (responseToken );
434
507
if (!isSigned (responseSigned , assertions )) {
435
508
String description = "Either the response or one of the assertions is unsigned. "
436
509
+ "Please either sign the response or all of the assertions." ;
@@ -439,7 +512,7 @@ private void process(Saml2AuthenticationToken token, Response response) {
439
512
result = result .concat (validateAssertions (token , response ));
440
513
441
514
Assertion firstAssertion = CollectionUtils .firstElement (response .getAssertions ());
442
- NameID nameId = decryptPrincipal (decrypter , firstAssertion );
515
+ NameID nameId = decryptPrincipal (responseToken );
443
516
if (nameId == null || nameId .getValue () == null ) {
444
517
Saml2Error error = new Saml2Error (Saml2ErrorCodes .SUBJECT_NOT_FOUND ,
445
518
"Assertion [" + firstAssertion .getID () + "] is missing a subject" );
@@ -511,19 +584,9 @@ private Saml2ResponseValidatorResult validateResponse(Saml2AuthenticationToken t
511
584
return Saml2ResponseValidatorResult .failure (errors );
512
585
}
513
586
514
- private List <Assertion > decryptAssertions (Decrypter decrypter , Response response ) {
515
- List <Assertion > assertions = new ArrayList <>();
516
- for (EncryptedAssertion encryptedAssertion : response .getEncryptedAssertions ()) {
517
- try {
518
- Assertion assertion = decrypter .decrypt (encryptedAssertion );
519
- assertions .add (assertion );
520
- }
521
- catch (DecryptionException ex ) {
522
- throw createAuthenticationException (Saml2ErrorCodes .DECRYPTION_ERROR , ex .getMessage (), ex );
523
- }
524
- }
525
- response .getAssertions ().addAll (assertions );
526
- return response .getAssertions ();
587
+ private List <Assertion > decryptAssertions (ResponseToken response ) {
588
+ this .assertionDecrypter .accept (response );
589
+ return response .getResponse ().getAssertions ();
527
590
}
528
591
529
592
private Saml2ResponseValidatorResult validateAssertions (Saml2AuthenticationToken token , Response response ) {
@@ -567,21 +630,16 @@ private boolean isSigned(boolean responseSigned, List<Assertion> assertions) {
567
630
return true ;
568
631
}
569
632
570
- private NameID decryptPrincipal (Decrypter decrypter , Assertion assertion ) {
633
+ private NameID decryptPrincipal (ResponseToken responseToken ) {
634
+ Assertion assertion = CollectionUtils .firstElement (responseToken .getResponse ().getAssertions ());
571
635
if (assertion .getSubject () == null ) {
572
636
return null ;
573
637
}
574
638
if (assertion .getSubject ().getEncryptedID () == null ) {
575
639
return assertion .getSubject ().getNameID ();
576
640
}
577
- try {
578
- NameID nameId = (NameID ) decrypter .decrypt (assertion .getSubject ().getEncryptedID ());
579
- assertion .getSubject ().setNameID (nameId );
580
- return nameId ;
581
- }
582
- catch (DecryptionException ex ) {
583
- throw createAuthenticationException (Saml2ErrorCodes .DECRYPTION_ERROR , ex .getMessage (), ex );
584
- }
641
+ this .principalDecrypter .accept (responseToken );
642
+ return assertion .getSubject ().getNameID ();
585
643
}
586
644
587
645
private static Map <String , List <Object >> getAssertionAttributes (Assertion assertion ) {
0 commit comments