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