1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 package org.ogf.graap.wsag.security.core.keystore;
36
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.PrivateKey;
43 import java.security.cert.Certificate;
44 import java.security.cert.CertificateException;
45 import java.security.cert.X509Certificate;
46 import java.text.MessageFormat;
47 import java.util.Map;
48 import java.util.Properties;
49
50 import javax.security.auth.Subject;
51 import javax.security.auth.callback.Callback;
52 import javax.security.auth.callback.CallbackHandler;
53 import javax.security.auth.callback.UnsupportedCallbackException;
54 import javax.security.auth.login.LoginException;
55 import javax.security.auth.spi.LoginModule;
56 import javax.security.auth.x500.X500Principal;
57 import javax.security.auth.x500.X500PrivateCredential;
58
59 import org.apache.log4j.Logger;
60 import org.apache.ws.security.WSSecurityException;
61 import org.apache.ws.security.components.crypto.Crypto;
62 import org.apache.ws.security.components.crypto.CryptoBase;
63 import org.ogf.graap.wsag.api.configuration.WSAG4JConfiguration;
64 import org.ogf.graap.wsag.security.core.SecurityConstants;
65 import org.ogf.graap.wsag.security.core.server.Merlin;
66
67
68
69
70
71
72
73 public class KeystoreLoginModule implements LoginModule
74 {
75
76 private static final Logger LOG = Logger.getLogger( KeystoreLoginModule.class );
77
78 private Subject klmSubject;
79
80 private CallbackHandler cbHandler;
81
82
83
84 @SuppressWarnings( "rawtypes" )
85 private Map klmOptions;
86
87
88 private KeyStore keystore;
89
90 private String keystoreType;
91
92 private String keystoreFile;
93
94 private String keystorePassword;
95
96 private String alias;
97
98 private String privateKeyPassword;
99
100 private String truststoreType;
101
102 private String truststoreFile;
103
104 private String truststorePassword;
105
106
107 private Crypto userCrypto;
108
109 private X500Principal userPrincipal;
110
111
112 private boolean login = false;
113
114 private boolean commit = false;
115
116
117
118
119
120
121
122 @SuppressWarnings( "rawtypes" )
123 public void initialize( Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options )
124 {
125 this.klmSubject = subject;
126 this.cbHandler = callbackHandler;
127
128 this.klmOptions = options;
129
130 initializeOptions();
131 }
132
133 private void initializeOptions()
134 {
135
136 keystoreFile = (String) klmOptions.get( "keyStoreURL" );
137 keystoreType = (String) klmOptions.get( "keyStoreType" );
138 alias = (String) klmOptions.get( "keyStoreAlias" );
139
140 truststoreFile = (String) klmOptions.get( "trustStoreURL" );
141 truststoreType = (String) klmOptions.get( "trustStoreType" );
142
143
144
145
146
147
148
149 keystoreType = ( keystoreType == null ) ? "JKS" : keystoreType;
150 truststoreType = ( truststoreType == null ) ? "JKS" : truststoreType;
151 }
152
153
154
155
156
157
158 public boolean login() throws LoginException
159 {
160 KeystoreCallback ksCallback = new KeystoreCallback();
161 Callback[] callbacks = new Callback[] { ksCallback };
162
163
164 try
165 {
166 cbHandler.handle( callbacks );
167 }
168 catch ( IOException e )
169 {
170 String message = "IO error during login";
171 LoginException le = new LoginException( message );
172 le.initCause( e );
173 throw le;
174 }
175 catch ( UnsupportedCallbackException e )
176 {
177 String message = "Invalid callback handler. Callback not supported.";
178 LoginException le = new LoginException( message );
179 le.initCause( e );
180 throw le;
181 }
182
183 keystorePassword = ksCallback.getKeystorePassword();
184
185 truststorePassword = ksCallback.getTruststorePassword();
186
187 privateKeyPassword = ksCallback.getPrivateKeyPassword();
188
189
190
191
192
193
194
195
196 if ( ( keystoreFile == null ) || ( keystorePassword == null ) || ( privateKeyPassword == null ) )
197 {
198
199 String message =
200 "Missing required parameter. "
201 + "The KeystoreLoginModule requires the following parameters: "
202 + "[keystoreFilename, keystorePassword, alias, privateKeyPassword]";
203
204 throw new LoginException( message );
205 }
206
207 loadKeyStore();
208
209 login = true;
210
211 return true;
212 }
213
214
215
216
217
218
219 public boolean commit() throws LoginException
220 {
221 if ( !login )
222 {
223 return false;
224 }
225
226 PrivateKey userKey;
227 X500PrivateCredential userCredential;
228 X509Certificate[] userCertificateChain;
229
230 try
231 {
232 userCertificateChain = getCertificates( alias );
233 userKey = (PrivateKey) keystore.getKey( alias, privateKeyPassword.toCharArray() );
234 }
235 catch ( KeyStoreException e )
236 {
237
238 String message = "Could not get default certificate from KeyStoreManager";
239 LoginException le = new LoginException( message );
240 le.initCause( e );
241 throw le;
242 }
243 catch ( Exception e )
244 {
245
246 String message = "Could not get private key from KeyStoreManager";
247 LoginException le = new LoginException( message );
248 le.initCause( e );
249 throw le;
250 }
251
252 if ( userCertificateChain == null )
253 {
254 Object[] filler = new Object[] { alias };
255 String message = MessageFormat.format( "No certificates found for user {0}", filler );
256 throw new LoginException( message );
257 }
258
259 userCredential = new X500PrivateCredential( userCertificateChain[0], userKey );
260
261 userCrypto = loadUserCrypto( userCredential, userCertificateChain, keystore );
262 userPrincipal =
263 new X500Principal( userCredential.getCertificate().getSubjectX500Principal().getName() );
264
265 klmSubject.getPrivateCredentials().add( userCrypto );
266 klmSubject.getPrivateCredentials().add( userCredential );
267 klmSubject.getPrincipals().add( userPrincipal );
268
269 commit = true;
270
271 return true;
272 }
273
274
275
276
277
278
279 public boolean abort() throws LoginException
280 {
281 if ( !login )
282 {
283 return false;
284 }
285 if ( ( login ) && ( !commit ) )
286 {
287
288 login = false;
289
290 klmSubject.getPrincipals().remove( userPrincipal );
291 klmSubject.getPublicCredentials().remove( userCrypto );
292
293 userCrypto = null;
294 userPrincipal = null;
295
296 keystore = null;
297 keystoreFile = null;
298 keystorePassword = null;
299 keystoreType = null;
300
301 alias = null;
302 privateKeyPassword = null;
303 }
304 else
305 {
306
307
308 logout();
309 }
310
311 return true;
312 }
313
314
315
316
317
318
319 public boolean logout() throws LoginException
320 {
321 klmSubject.getPrincipals().remove( userCrypto );
322 klmSubject.getPrincipals().remove( userPrincipal );
323
324 login = false;
325 commit = false;
326
327 userCrypto = null;
328 userPrincipal = null;
329
330 keystore = null;
331 keystoreFile = null;
332 keystorePassword = null;
333 keystoreType = null;
334
335 alias = null;
336 privateKeyPassword = null;
337
338 return true;
339 }
340
341 private synchronized KeyStore getKeystore() throws LoginException
342 {
343 if ( keystore == null )
344 {
345 loadKeyStore();
346 }
347 return keystore;
348 }
349
350 private void loadKeyStore() throws LoginException
351 {
352 try
353 {
354 String actualKSType = ( keystoreType == null ) ? KeyStore.getDefaultType() : keystoreType;
355
356 keystore = KeyStore.getInstance( actualKSType );
357
358 if ( keystoreFile == null )
359 {
360 throw new IOException( "No keystore specified by user." );
361 }
362
363 InputStream ksInput = WSAG4JConfiguration.findResource( keystoreFile );
364 keystore.load( ksInput, keystorePassword.toCharArray() );
365
366 }
367 catch ( KeyStoreException e )
368 {
369 throw new LoginException( e.getMessage() );
370 }
371 catch ( IOException e )
372 {
373 throw new LoginException( e.getMessage() );
374 }
375 catch ( CertificateException e )
376 {
377 throw new LoginException( e.getMessage() );
378 }
379 catch ( NoSuchAlgorithmException e )
380 {
381 throw new LoginException( e.getMessage() );
382 }
383
384 }
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402 private X509Certificate[] getCertificates( String ksAlias ) throws KeyStoreException, LoginException
403 {
404 Certificate[] certs = null;
405 Certificate cert = null;
406
407 KeyStore store = getKeystore();
408
409 if ( store != null )
410 {
411
412 certs = store.getCertificateChain( ksAlias );
413 if ( certs == null || certs.length == 0 )
414 {
415
416
417 cert = store.getCertificate( ksAlias );
418 }
419 }
420
421 if ( cert != null )
422 {
423 certs = new Certificate[] { cert };
424 }
425 else if ( certs == null )
426 {
427
428 return null;
429 }
430
431 X509Certificate[] x509certs = new X509Certificate[certs.length];
432 for ( int i = 0; i < certs.length; i++ )
433 {
434 x509certs[i] = (X509Certificate) certs[i];
435 }
436 return x509certs;
437 }
438
439 private Crypto loadUserCrypto( final X500PrivateCredential privCredential,
440 final X509Certificate[] pubCredential, final KeyStore store )
441 {
442
443
444
445
446 Properties properties = new Properties();
447 properties.setProperty( SecurityConstants.PROP_CRYPTO_PROVIDER, Merlin.class.getName() );
448
449 properties.setProperty( SecurityConstants.PROP_KEYSTORE_TYPE, keystoreType );
450 properties.setProperty( SecurityConstants.PROP_KEYSTORE_PASS, keystorePassword );
451 properties.setProperty( SecurityConstants.PROP_KEYSTORE_ALIAS, alias );
452 properties.setProperty( SecurityConstants.PROP_KEYSTORE_ALIAS_PASS, privateKeyPassword );
453 properties.setProperty( SecurityConstants.PROP_KEYSTORE_FILE, keystoreFile );
454
455 if ( truststoreFile != null )
456 {
457 properties.setProperty( SecurityConstants.PROP_TRUSTSTORE_FILE, truststoreFile );
458 }
459 if ( truststorePassword != null )
460 {
461 properties.setProperty( SecurityConstants.PROP_TRUSTSTORE_PASS, truststorePassword );
462 }
463 if ( truststoreType != null )
464 {
465 properties.setProperty( SecurityConstants.PROP_TRUSTSTORE_TYPE, truststoreType );
466 }
467
468 try
469 {
470 return new Merlin( properties );
471 }
472 catch ( Exception e )
473 {
474 Object[] filler = new Object[] { e.getMessage() };
475 String message = MessageFormat.format( "Could not load user crypto. Reason: {0}", filler );
476 LOG.error( message );
477 LOG.error( "Try fallback... Does eventually not work for PKCS12 keystore." );
478 }
479
480
481
482
483 CryptoBase crypto = new CryptoBase()
484 {
485
486 protected String getCryptoProvider()
487 {
488 return null;
489 }
490
491 public String getDefaultX509Alias()
492 {
493
494
495
496
497
498 return SecurityConstants.DEFAULT_ALIAS;
499 }
500
501
502
503
504 public PrivateKey getPrivateKey( String ksAlias, String password ) throws Exception
505 {
506
507
508
509
510 if ( SecurityConstants.DEFAULT_ALIAS.equals( ksAlias ) )
511 {
512 return privCredential.getPrivateKey();
513 }
514 return (PrivateKey) keystore.getKey( ksAlias, password.toCharArray() );
515 }
516
517
518
519
520 public X509Certificate[] getCertificates( String ksAlias ) throws WSSecurityException
521 {
522
523
524
525
526 if ( SecurityConstants.DEFAULT_ALIAS.equals( ksAlias ) )
527 {
528 if ( pubCredential == null )
529 {
530 LOG.warn( "No certificate chain not provided in the login context." );
531 return new X509Certificate[] { privCredential.getCertificate() };
532 }
533
534 return pubCredential;
535 }
536 else
537 {
538 return super.getCertificates( ksAlias );
539 }
540 }
541
542 };
543
544 crypto.setKeyStore( store );
545
546 return crypto;
547 }
548
549 }