17
17
package org .springframework .boot .autoconfigure .security .saml2 ;
18
18
19
19
import java .io .InputStream ;
20
+ import java .security .GeneralSecurityException ;
21
+ import java .security .Key ;
22
+ import java .security .KeyStore ;
23
+ import java .security .PrivateKey ;
24
+ import java .security .cert .Certificate ;
20
25
import java .security .cert .CertificateFactory ;
21
26
import java .security .cert .X509Certificate ;
22
27
import java .security .interfaces .RSAPrivateKey ;
25
30
import java .util .Map ;
26
31
import java .util .function .Consumer ;
27
32
33
+ import org .springframework .beans .factory .ObjectProvider ;
28
34
import org .springframework .boot .autoconfigure .condition .ConditionalOnMissingBean ;
29
35
import org .springframework .boot .autoconfigure .security .saml2 .Saml2RelyingPartyProperties .AssertingParty ;
30
36
import org .springframework .boot .autoconfigure .security .saml2 .Saml2RelyingPartyProperties .AssertingParty .Verification ;
37
+ import org .springframework .boot .autoconfigure .security .saml2 .Saml2RelyingPartyProperties .Bundle ;
31
38
import org .springframework .boot .autoconfigure .security .saml2 .Saml2RelyingPartyProperties .Decryption ;
32
39
import org .springframework .boot .autoconfigure .security .saml2 .Saml2RelyingPartyProperties .Registration ;
33
- import org .springframework .boot .autoconfigure .security .saml2 .Saml2RelyingPartyProperties .Registration .Signing ;
40
+ import org .springframework .boot .autoconfigure .security .saml2 .Saml2RelyingPartyProperties .Registration .Signing . Credential ;
34
41
import org .springframework .boot .context .properties .PropertyMapper ;
42
+ import org .springframework .boot .ssl .SslBundle ;
43
+ import org .springframework .boot .ssl .SslBundleKey ;
44
+ import org .springframework .boot .ssl .SslBundles ;
35
45
import org .springframework .context .annotation .Bean ;
36
46
import org .springframework .context .annotation .Conditional ;
37
47
import org .springframework .context .annotation .Configuration ;
57
67
* @author Moritz Halbritter
58
68
* @author Lasse Lindqvist
59
69
* @author Lasse Wulff
70
+ * @author Scott Frederick
60
71
*/
61
72
@ Configuration (proxyBeanMethods = false )
62
73
@ Conditional (RegistrationConfiguredCondition .class )
63
74
@ ConditionalOnMissingBean (RelyingPartyRegistrationRepository .class )
64
75
class Saml2RelyingPartyRegistrationConfiguration {
65
76
66
77
@ Bean
67
- RelyingPartyRegistrationRepository relyingPartyRegistrationRepository (Saml2RelyingPartyProperties properties ) {
78
+ RelyingPartyRegistrationRepository relyingPartyRegistrationRepository (Saml2RelyingPartyProperties properties ,
79
+ ObjectProvider <SslBundles > sslBundles ) {
68
80
List <RelyingPartyRegistration > registrations = properties .getRegistration ()
69
81
.entrySet ()
70
82
.stream ()
71
- .map (this :: asRegistration )
83
+ .map (( entry ) -> asRegistration ( entry , sslBundles . getIfAvailable ()) )
72
84
.toList ();
73
85
return new InMemoryRelyingPartyRegistrationRepository (registrations );
74
86
}
75
87
76
- private RelyingPartyRegistration asRegistration (Map .Entry <String , Registration > entry ) {
77
- return asRegistration (entry .getKey (), entry .getValue ());
88
+ private RelyingPartyRegistration asRegistration (Map .Entry <String , Registration > entry , SslBundles sslBundles ) {
89
+ return asRegistration (entry .getKey (), entry .getValue (), sslBundles );
78
90
}
79
91
80
- private RelyingPartyRegistration asRegistration (String id , Registration properties ) {
92
+ private RelyingPartyRegistration asRegistration (String id , Registration properties , SslBundles sslBundles ) {
81
93
boolean usingMetadata = StringUtils .hasText (properties .getAssertingparty ().getMetadataUri ());
82
94
Builder builder = (!usingMetadata ) ? RelyingPartyRegistration .withRegistrationId (id )
83
95
: createBuilderUsingMetadata (properties .getAssertingparty ()).registrationId (id );
@@ -87,19 +99,19 @@ private RelyingPartyRegistration asRegistration(String id, Registration properti
87
99
builder .signingX509Credentials ((credentials ) -> properties .getSigning ()
88
100
.getCredentials ()
89
101
.stream ()
90
- .map (this :: asSigningCredential )
102
+ .map (( signing ) -> asSigningCredential ( signing , sslBundles ) )
91
103
.forEach (credentials ::add ));
92
104
builder .decryptionX509Credentials ((credentials ) -> properties .getDecryption ()
93
105
.getCredentials ()
94
106
.stream ()
95
- .map (this :: asDecryptionCredential )
107
+ .map (( decryption ) -> asDecryptionCredential ( decryption , sslBundles ) )
96
108
.forEach (credentials ::add ));
97
109
builder .assertingPartyDetails (
98
110
(details ) -> details .verificationX509Credentials ((credentials ) -> properties .getAssertingparty ()
99
111
.getVerification ()
100
112
.getCredentials ()
101
113
.stream ()
102
- .map (this :: asVerificationCredential )
114
+ .map (( verification ) -> asVerificationCredential ( verification , sslBundles ) )
103
115
.forEach (credentials ::add )));
104
116
builder .singleLogoutServiceLocation (properties .getSinglelogout ().getUrl ());
105
117
builder .singleLogoutServiceResponseLocation (properties .getSinglelogout ().getResponseUrl ());
@@ -150,19 +162,37 @@ private void validateSigningCredentials(Registration properties, boolean signReq
150
162
}
151
163
}
152
164
153
- private Saml2X509Credential asSigningCredential (Signing .Credential properties ) {
165
+ private Saml2X509Credential asSigningCredential (Credential properties , SslBundles sslBundles ) {
166
+ Bundle sslBundle = properties .getBundle ();
167
+ if (sslBundle != null ) {
168
+ PrivateKey privateKey = getPrivateKey (sslBundle .getName (), sslBundles );
169
+ X509Certificate certificate = getCertificate (sslBundle , sslBundles );
170
+ return new Saml2X509Credential (privateKey , certificate , Saml2X509CredentialType .SIGNING );
171
+ }
154
172
RSAPrivateKey privateKey = readPrivateKey (properties .getPrivateKeyLocation ());
155
173
X509Certificate certificate = readCertificate (properties .getCertificateLocation ());
156
174
return new Saml2X509Credential (privateKey , certificate , Saml2X509CredentialType .SIGNING );
157
175
}
158
176
159
- private Saml2X509Credential asDecryptionCredential (Decryption .Credential properties ) {
177
+ private Saml2X509Credential asDecryptionCredential (Decryption .Credential properties , SslBundles sslBundles ) {
178
+ Bundle sslBundle = properties .getBundle ();
179
+ if (sslBundle != null ) {
180
+ PrivateKey privateKey = getPrivateKey (sslBundle .getName (), sslBundles );
181
+ X509Certificate certificate = getCertificate (sslBundle , sslBundles );
182
+ return new Saml2X509Credential (privateKey , certificate , Saml2X509CredentialType .DECRYPTION );
183
+ }
160
184
RSAPrivateKey privateKey = readPrivateKey (properties .getPrivateKeyLocation ());
161
185
X509Certificate certificate = readCertificate (properties .getCertificateLocation ());
162
186
return new Saml2X509Credential (privateKey , certificate , Saml2X509CredentialType .DECRYPTION );
163
187
}
164
188
165
- private Saml2X509Credential asVerificationCredential (Verification .Credential properties ) {
189
+ private Saml2X509Credential asVerificationCredential (Verification .Credential properties , SslBundles sslBundles ) {
190
+ Bundle sslBundle = properties .getBundle ();
191
+ if (sslBundle != null ) {
192
+ X509Certificate certificate = getCertificate (sslBundle , sslBundles );
193
+ return new Saml2X509Credential (certificate , Saml2X509Credential .Saml2X509CredentialType .ENCRYPTION ,
194
+ Saml2X509Credential .Saml2X509CredentialType .VERIFICATION );
195
+ }
166
196
X509Certificate certificate = readCertificate (properties .getCertificateLocation ());
167
197
return new Saml2X509Credential (certificate , Saml2X509Credential .Saml2X509CredentialType .ENCRYPTION ,
168
198
Saml2X509Credential .Saml2X509CredentialType .VERIFICATION );
@@ -190,4 +220,35 @@ private X509Certificate readCertificate(Resource location) {
190
220
}
191
221
}
192
222
223
+ private PrivateKey getPrivateKey (String sslBundle , SslBundles sslBundles ) {
224
+ try {
225
+ SslBundle bundle = sslBundles .getBundle (sslBundle );
226
+ SslBundleKey key = bundle .getKey ();
227
+ KeyStore keyStore = bundle .getStores ().getKeyStore ();
228
+ Key privateKey = keyStore .getKey (key .getAlias (), key .getPassword ().toCharArray ());
229
+ Assert .notNull (privateKey ,
230
+ "Private key with alias '" + key .getAlias () + "' was not found in SSL bundle '" + sslBundle + "'" );
231
+ Assert .isInstanceOf (PrivateKey .class , privateKey );
232
+ return (PrivateKey ) privateKey ;
233
+ }
234
+ catch (GeneralSecurityException ex ) {
235
+ throw new IllegalStateException ("Error getting private key from SSL bundle '" + sslBundle + "'" , ex );
236
+ }
237
+ }
238
+
239
+ private X509Certificate getCertificate (Bundle sslBundle , SslBundles sslBundles ) {
240
+ try {
241
+ SslBundle bundle = sslBundles .getBundle (sslBundle .getName ());
242
+ KeyStore keyStore = bundle .getStores ().getKeyStore ();
243
+ Certificate certificate = keyStore .getCertificate (sslBundle .getAlias ());
244
+ Assert .notNull (certificate , "Certificate with alias '" + sslBundle .getAlias ()
245
+ + "' was not found in SSL bundle '" + sslBundle + "'" );
246
+ Assert .isInstanceOf (X509Certificate .class , certificate );
247
+ return (X509Certificate ) certificate ;
248
+ }
249
+ catch (GeneralSecurityException ex ) {
250
+ throw new IllegalStateException ("Error getting certificate from SSL bundle '" + sslBundle + "'" , ex );
251
+ }
252
+ }
253
+
193
254
}
0 commit comments