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 package net.sf.jguard.ext.authentication.loginmodules;
29
30 import java.io.BufferedInputStream;
31 import java.io.DataInputStream;
32 import java.io.FileInputStream;
33 import java.io.FileNotFoundException;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.net.MalformedURLException;
37 import java.net.URL;
38 import java.net.URLConnection;
39 import java.security.InvalidAlgorithmParameterException;
40 import java.security.KeyStore;
41 import java.security.KeyStoreException;
42 import java.security.NoSuchAlgorithmException;
43 import java.security.Provider;
44 import java.security.PublicKey;
45 import java.security.Security;
46 import java.security.cert.CRL;
47 import java.security.cert.CRLException;
48 import java.security.cert.CertPath;
49 import java.security.cert.CertPathValidator;
50 import java.security.cert.CertPathValidatorException;
51 import java.security.cert.CertStore;
52 import java.security.cert.CertStoreParameters;
53 import java.security.cert.CertificateException;
54 import java.security.cert.CertificateFactory;
55 import java.security.cert.CollectionCertStoreParameters;
56 import java.security.cert.LDAPCertStoreParameters;
57 import java.security.cert.PKIXCertPathValidatorResult;
58 import java.security.cert.PKIXParameters;
59 import java.security.cert.PolicyNode;
60 import java.security.cert.TrustAnchor;
61 import java.security.cert.X509Certificate;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collection;
65 import java.util.Date;
66 import java.util.List;
67 import java.util.Map;
68 import java.util.Set;
69
70 import java.util.logging.Level;
71 import javax.security.auth.Subject;
72 import javax.security.auth.callback.CallbackHandler;
73 import javax.security.auth.login.FailedLoginException;
74 import javax.security.auth.login.LoginException;
75 import javax.security.auth.spi.LoginModule;
76
77 import net.sf.jguard.core.CoreConstants;
78 import net.sf.jguard.ext.SecurityConstants;
79 import net.sf.jguard.ext.authentication.certificates.CertUtils;
80
81 import org.bouncycastle.jce.provider.BouncyCastleProvider;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85
86
87
88
89
90
91 public class CRLLoginModule extends CertificateLoginModule implements LoginModule {
92
93 private static final String COLLECTION = "Collection";
94 private static final String LDAP = "LDAP";
95 private static final String PKIX = "PKIX";
96 private static final String X_509 = "X.509";
97
98
99 private static final Logger logger = LoggerFactory.getLogger(CRLLoginModule.class.getName());
100 private Set trustAnchors = null;
101 private String trustedCaCertsDirPath = null;
102 private CertPath certPath = null;
103 private boolean debug = false;
104 private Provider securityProvider = null;
105 private String certStoreType =CRLLoginModule.LDAP;
106 private String ldapServerName ="localhost";
107 private int ldapServerPort =389;
108
109 private Map sharedState;
110 private String fileCrlPath = null;
111 private String urlCrlPath = null;
112 private boolean anyPolicyInhibited = false;
113 private boolean explicitPolicyRequired = false;
114 private boolean policyMappingInhibited = false;
115 private boolean policyQualifierRejected = true;
116 private boolean revocationEnabled = true;
117 private String sigProvider=null;
118 private String keyStorePath;
119 private String keyStorePassword;
120 private String keyStoreType;
121 private static boolean SecurityProviderInitialized = false;
122
123
124
125
126
127
128
129
130
131 public void initialize(Subject subj, CallbackHandler cbkHandler, Map state,Map options) {
132 this.subject = subj;
133 this.callbackHandler = cbkHandler;
134 this.sharedState = state;
135
136 if(!SecurityProviderInitialized){
137 SecurityProviderInitialized = initSecurityProvider();
138 }
139
140 if((String)options.get(CoreConstants.DEBUG)!=null){
141 debug= Boolean.valueOf((String)options.get(CoreConstants.DEBUG)).booleanValue();
142 }
143
144 if((String)options.get(SecurityConstants.CERT_PATH_ANY_POLICY_INHIBITED)!=null){
145 anyPolicyInhibited = Boolean.valueOf((String)options.get(SecurityConstants.CERT_PATH_ANY_POLICY_INHIBITED)).booleanValue();
146 }
147
148 if((String)options.get(SecurityConstants.CERT_PATH_EXPLICIT_POLICY_REQUIRED)!=null){
149 explicitPolicyRequired = Boolean.valueOf((String)options.get(SecurityConstants.CERT_PATH_EXPLICIT_POLICY_REQUIRED)).booleanValue();
150 }
151
152 if((String)options.get(SecurityConstants.CERT_PATH_POLICY_MAPPING_INHIBITED)!=null){
153 policyMappingInhibited = Boolean.valueOf((String)options.get(SecurityConstants.CERT_PATH_POLICY_MAPPING_INHIBITED)).booleanValue();
154 }
155
156 if((String)options.get(SecurityConstants.CERT_PATH_POLICY_QUALIFIERS_REJECTED)!=null){
157 policyQualifierRejected = Boolean.valueOf((String)options.get(SecurityConstants.CERT_PATH_POLICY_QUALIFIERS_REJECTED)).booleanValue();
158 }
159
160 if((String)options.get(SecurityConstants.CERT_PATH_REVOCATION_ENABLED)!=null){
161 revocationEnabled = Boolean.valueOf((String)options.get(SecurityConstants.CERT_PATH_REVOCATION_ENABLED)).booleanValue();
162 }
163
164 if((String)options.get(SecurityConstants.CERT_PATH_SIG_PROVIDER)!=null){
165 sigProvider =(String)options.get(SecurityConstants.CERT_PATH_SIG_PROVIDER);
166 }
167
168 if((String)options.get(SecurityConstants.CERT_PATH_CRL_PATH)!=null){
169 fileCrlPath =(String)options.get(SecurityConstants.CERT_PATH_CRL_PATH);
170 }
171
172 if((String)options.get(SecurityConstants.CERT_PATH_URL_CRL_PATH)!=null){
173 urlCrlPath =(String)options.get(SecurityConstants.CERT_PATH_URL_CRL_PATH);
174 }
175
176 if((String)options.get(SecurityConstants.TRUSTED_CA_CERTIFICATES_DIRECTORY_PATH)!=null){
177 trustedCaCertsDirPath= (String)options.get(SecurityConstants.TRUSTED_CA_CERTIFICATES_DIRECTORY_PATH);
178 trustAnchors = CertUtils.getTrustedAnchorsFromDirectory(trustedCaCertsDirPath);
179 }
180
181 if((String)options.get(SecurityConstants.SECURITY_PROVIDER)!=null){
182 String securityProviderClassName = (String)options.get(SecurityConstants.SECURITY_PROVIDER);
183 try {
184 Class securityProviderClass = this.getClass().getClassLoader().loadClass(securityProviderClassName);
185 securityProvider = (Provider) securityProviderClass.newInstance();
186
187 } catch (ClassNotFoundException e) {
188 logger.warn(e.getMessage());
189 } catch (InstantiationException e) {
190 logger.warn(e.getMessage());
191 } catch (IllegalAccessException e) {
192 logger.warn(e.getMessage());
193 }
194 }else{
195 securityProvider = new BouncyCastleProvider();
196 }
197
198 if((String)options.get(SecurityConstants.CERT_PATH_CERTSTORE_TYPE)!=null){
199 certStoreType = (String)options.get(SecurityConstants.CERT_PATH_CERTSTORE_TYPE);
200 }
201
202 if((String)options.get(SecurityConstants.CERT_PATH_LDAP_SERVER_NAME)!=null){
203 ldapServerName = (String)options.get(SecurityConstants.CERT_PATH_LDAP_SERVER_NAME);
204 }
205
206 if((String)options.get(SecurityConstants.CERT_PATH_LDAP_SERVER_PORT)!=null){
207 ldapServerPort = Integer.parseInt((String)options.get(SecurityConstants.CERT_PATH_LDAP_SERVER_PORT));
208 }
209
210 if((String)options.get(SecurityConstants.JAVAX_NET_SSL_TRUSTSTORE)!=null){
211 String trustStoreFileName = (String)options.get(SecurityConstants.JAVAX_NET_SSL_TRUSTSTORE);
212 System.setProperty("javax.net.ssl.trustStore",trustStoreFileName);
213 }
214
215 if((String)options.get(SecurityConstants.JAVAX_NET_SSL_TRUSTSTORE_PASSWORD)!=null){
216 String trustStorePassword = (String)options.get(SecurityConstants.JAVAX_NET_SSL_TRUSTSTORE_PASSWORD);
217 System.setProperty("javax.net.ssl.trustStorePassword",trustStorePassword);
218 }
219
220 if((String)options.get(SecurityConstants.KEY_STORE_PATH)!=null){
221 keyStorePath = (String)options.get(SecurityConstants.KEY_STORE_PATH);
222 }
223
224 if((String)options.get(SecurityConstants.KEY_STORE_PASSWORD)!=null){
225 keyStorePassword = (String)options.get(SecurityConstants.KEY_STORE_PASSWORD);
226 }
227
228 if((String)options.get(SecurityConstants.KEY_STORE_TYPE)!=null){
229 keyStoreType = (String)options.get(SecurityConstants.KEY_STORE_TYPE);
230 }
231
232
233 }
234
235
236
237
238
239 public boolean login() throws LoginException {
240
241 boolean login = super.login();
242 if(!login){
243 return login;
244 }
245 certPath = buildCertPath(certChainToCheck);
246 validateCertPath(certPath);
247
248
249 sharedState.put(SecurityConstants.SKIP_PASSWORD_CHECK, "true");
250 return true;
251 }
252
253
254
255
256
257
258
259 private CertPath buildCertPath(X509Certificate[] certs){
260 CertificateFactory certFactory = null;
261 CertPath certPath = null;
262 try {
263 certFactory = CertificateFactory.getInstance(CRLLoginModule.X_509,securityProvider);
264 certPath = certFactory.generateCertPath(Arrays.asList(certs));
265 } catch (CertificateException e) {
266 logger.warn(e.getMessage());
267 }
268
269 return certPath;
270 }
271
272
273
274
275
276
277
278 private void validateCertPath(CertPath certPath) throws LoginException{
279 CertPathValidator validator = null;
280 PKIXParameters parameters = null;
281 PKIXCertPathValidatorResult result = null;
282 try {
283 validator = CertPathValidator.getInstance(CRLLoginModule.PKIX,securityProvider);
284
285 } catch (NoSuchAlgorithmException e) {
286 logger.error(" algorithm PKIX is not present "+securityProvider.getName()
287 +" "+securityProvider.getInfo()+" "+securityProvider.getVersion());
288 }
289 try {
290
291 parameters = getPKIXParameters();
292 List certStores = new ArrayList();
293 CertStore certStore = getCertStore();
294 certStores.add(certStore);
295 parameters.setCertStores(certStores);
296 parameters.setAnyPolicyInhibited(anyPolicyInhibited);
297
298 parameters.setDate(new Date());
299
300 parameters.setExplicitPolicyRequired(explicitPolicyRequired);
301 parameters.setPolicyMappingInhibited(policyMappingInhibited);
302 parameters.setPolicyQualifiersRejected(policyQualifierRejected);
303 parameters.setRevocationEnabled(revocationEnabled);
304 if(sigProvider!=null){
305 parameters.setSigProvider(sigProvider);
306 }
307
308
309
310 result =(PKIXCertPathValidatorResult)validator.validate(certPath,parameters);
311 PolicyNode policyTree = result.getPolicyTree();
312 PublicKey key = result.getPublicKey();
313 TrustAnchor anchor = result.getTrustAnchor();
314 if(debug){
315 if(policyTree!=null){
316 logger.debug("policyTree depth = "+policyTree.getDepth());
317 logger.debug("policyTree expected policies = "+policyTree.getExpectedPolicies());
318 logger.debug("policyTree policy qualifiers = "+policyTree.getPolicyQualifiers());
319 }
320 if(key!=null){
321 logger.debug("public key= "+key.toString());
322 }
323 if(anchor!=null){
324 logger.debug("TrustAnchor ca name= "+anchor.getCAName());
325 logger.debug("TrustAnchor ca public key = "+anchor.getCAPublicKey());
326 logger.debug("TrustAnchor name constraints = "+anchor.getNameConstraints());
327 logger.debug("TrustAnchor trustedCert = "+anchor.getTrustedCert());
328 }
329 }
330 } catch (InvalidAlgorithmParameterException e) {
331 logger.error(e.getMessage());
332 throw new FailedLoginException(e.getMessage());
333 } catch (CertPathValidatorException e) {
334 logger.error(e.getMessage());
335 throw new FailedLoginException(e.getMessage());
336 }
337 }
338
339 private PKIXParameters getPKIXParameters() throws LoginException {
340 PKIXParameters parameters = null;
341 if(keyStorePath!=null){
342 KeyStore keystore;
343 try {
344 keystore = CertUtils.getKeyStore(keyStorePath, keyStorePassword, keyStoreType);
345 parameters = new PKIXParameters(keystore);
346 } catch (KeyStoreException e) {
347 throw new LoginException(e.getMessage());
348 } catch (NoSuchAlgorithmException e) {
349 throw new LoginException(e.getMessage());
350 } catch (CertificateException e) {
351 throw new LoginException(e.getMessage());
352 } catch (IOException e) {
353 throw new LoginException(e.getMessage());
354 } catch (InvalidAlgorithmParameterException e) {
355 throw new LoginException(e.getMessage());
356 }
357
358 }else{
359 try {
360 parameters = new PKIXParameters(trustAnchors);
361 } catch (InvalidAlgorithmParameterException e) {
362 throw new LoginException(e.getMessage());
363 }
364 }
365 return parameters;
366 }
367
368
369
370
371
372
373
374 private CertStore getCertStore() throws LoginException{
375 CertStore certStore = null;
376
377
378 CertStoreParameters certStoreParams = null;
379 if(certStoreType.equalsIgnoreCase(CRLLoginModule.LDAP)){
380 certStoreParams = new LDAPCertStoreParameters(ldapServerName,ldapServerPort);
381 }else if(certStoreType.equalsIgnoreCase(CRLLoginModule.COLLECTION)){
382 Collection crlCollection = getCRLAndCertsCollection();
383 certStoreParams = new CollectionCertStoreParameters(crlCollection);
384 }else{
385 throw new LoginException(" invalid 'certStoreType' value : this value should be 'LDAP' or 'Collection' ");
386 }
387 try {
388
389 certStore = CertStore.getInstance(certStoreType,certStoreParams,securityProvider);
390
391 } catch (NoSuchAlgorithmException e) {
392 throw new LoginException(e.getMessage());
393 } catch (InvalidAlgorithmParameterException e) {
394 throw new LoginException(e.getMessage());
395 }
396
397 return certStore;
398 }
399
400
401
402
403
404 private Collection getCRLAndCertsCollection() {
405 Collection crlAndCerts = new ArrayList();
406 CertificateFactory certFactory = null;
407 try {
408 certFactory = CertificateFactory.getInstance(CRLLoginModule.X_509,securityProvider);
409 } catch (CertificateException e) {
410 logger.error( " X509 certificate factory cannot be retrieved with the securityProvider "+
411 securityProvider.getName()+" "+securityProvider.getInfo()+
412 " "+securityProvider.getVersion(),e);
413 }
414
415 if(fileCrlPath!=null){
416 addCRLFromPath(crlAndCerts, certFactory);
417 }
418 if(urlCrlPath!=null){
419 addCRLFromURL(crlAndCerts, certFactory);
420 }
421 return crlAndCerts;
422 }
423
424
425
426
427
428
429
430 private void addCRLFromPath(Collection crlAndCerts, CertificateFactory certFactory) {
431
432 InputStream stream = null;
433 try {
434 stream = new BufferedInputStream(new FileInputStream(fileCrlPath));
435 } catch (FileNotFoundException e) {
436 e.printStackTrace();
437 }
438 try {
439 CRL crl = certFactory.generateCRL(stream);
440 crlAndCerts.add(crl);
441 } catch (CRLException e) {
442 e.printStackTrace();
443 }finally{
444 try {
445 stream.close();
446 } catch (IOException e) {
447 e.printStackTrace();
448 }
449 }
450 }
451
452
453
454
455
456
457 private void addCRLFromURL(Collection crlAndCerts, CertificateFactory certFactory) {
458 DataInputStream data = null;
459 try {
460 URL url = new URL(urlCrlPath);
461 URLConnection connection = url.openConnection();
462
463 connection.setDoInput(true);
464
465 connection.setUseCaches(false);
466 data = new DataInputStream(connection.getInputStream());
467 try {
468 CRL crl = certFactory.generateCRL(data);
469 crlAndCerts.add(crl);
470 } catch (CRLException e) {
471 logger.error(" CRL cannot be built with the retrieved data ");
472 e.printStackTrace();
473 }
474 } catch (MalformedURLException e) {
475 logger.error( " bad uri synthax "+urlCrlPath,e);
476 } catch (IOException e) {
477 logger.error( " IOException when we wan to retrieve CRL with data ",e);
478 }finally{
479 try {
480 data.close();
481 } catch (IOException e) {
482 logger.error( " IOException when we close the DATAInputStream",e);
483 }
484 }
485 }
486
487
488
489
490
491
492 protected static boolean initSecurityProvider(){
493 if(Security.getProvider(BouncyCastleProvider.class.getName())==null){
494 try{
495 Security.addProvider(new BouncyCastleProvider());
496 return true;
497 }catch(SecurityException sex){
498 logger.error(" jGuard cannot add dynamically the JCE provider required from \n");
499 logger.error(" the BOUNCYCASTLE library .this operation is prevented by the SECURITYMANAGER \n");
500 logger.error(" to use this required provider, you must add an entry to your java.security \n");
501 logger.error(" properties file (found in $JAVA_HOME/jre/lib/security/java.security, \n");
502 logger.error(" where $JAVA_HOME is the location of your JDK/JRE distribution) \n");
503 logger.error(" security.provider.<n>=org.bouncycastle.jce.provider.BouncyCastleProvider \n");
504 logger.error(" Where <n> is the preference you want the provider at (1 being the most prefered). ");
505 return false;
506 }
507 }else{
508 return true;
509 }
510 }
511
512 }