feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
@@ -0,0 +1,939 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.security.auth.module;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
import javax.security.auth.Destroyable;
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.x500.*;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.ConfirmationCallback;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.TextOutputCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.FailedLoginException;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
import sun.security.util.Password;
|
||||
|
||||
/**
|
||||
* Provides a JAAS login module that prompts for a key store alias and
|
||||
* populates the subject with the alias's principal and credentials. Stores
|
||||
* an <code>X500Principal</code> for the subject distinguished name of the
|
||||
* first certificate in the alias's credentials in the subject's principals,
|
||||
* the alias's certificate path in the subject's public credentials, and a
|
||||
* <code>X500PrivateCredential</code> whose certificate is the first
|
||||
* certificate in the alias's certificate path and whose private key is the
|
||||
* alias's private key in the subject's private credentials. <p>
|
||||
*
|
||||
* Recognizes the following options in the configuration file:
|
||||
* <dl>
|
||||
*
|
||||
* <dt> <code>keyStoreURL</code> </dt>
|
||||
* <dd> A URL that specifies the location of the key store. Defaults to
|
||||
* a URL pointing to the .keystore file in the directory specified by the
|
||||
* <code>user.home</code> system property. The input stream from this
|
||||
* URL is passed to the <code>KeyStore.load</code> method.
|
||||
* "NONE" may be specified if a <code>null</code> stream must be
|
||||
* passed to the <code>KeyStore.load</code> method.
|
||||
* "NONE" should be specified if the KeyStore resides
|
||||
* on a hardware token device, for example.</dd>
|
||||
*
|
||||
* <dt> <code>keyStoreType</code> </dt>
|
||||
* <dd> The key store type. If not specified, defaults to the result of
|
||||
* calling <code>KeyStore.getDefaultType()</code>.
|
||||
* If the type is "PKCS11", then keyStoreURL must be "NONE"
|
||||
* and privateKeyPasswordURL must not be specified.</dd>
|
||||
*
|
||||
* <dt> <code>keyStoreProvider</code> </dt>
|
||||
* <dd> The key store provider. If not specified, uses the standard search
|
||||
* order to find the provider. </dd>
|
||||
*
|
||||
* <dt> <code>keyStoreAlias</code> </dt>
|
||||
* <dd> The alias in the key store to login as. Required when no callback
|
||||
* handler is provided. No default value. </dd>
|
||||
*
|
||||
* <dt> <code>keyStorePasswordURL</code> </dt>
|
||||
* <dd> A URL that specifies the location of the key store password. Required
|
||||
* when no callback handler is provided and
|
||||
* <code>protected</code> is false.
|
||||
* No default value. </dd>
|
||||
*
|
||||
* <dt> <code>privateKeyPasswordURL</code> </dt>
|
||||
* <dd> A URL that specifies the location of the specific private key password
|
||||
* needed to access the private key for this alias.
|
||||
* The keystore password
|
||||
* is used if this value is needed and not specified. </dd>
|
||||
*
|
||||
* <dt> <code>protected</code> </dt>
|
||||
* <dd> This value should be set to "true" if the KeyStore
|
||||
* has a separate, protected authentication path
|
||||
* (for example, a dedicated PIN-pad attached to a smart card).
|
||||
* Defaults to "false". If "true" keyStorePasswordURL and
|
||||
* privateKeyPasswordURL must not be specified.</dd>
|
||||
*
|
||||
* </dl>
|
||||
*/
|
||||
@jdk.Exported
|
||||
public class KeyStoreLoginModule implements LoginModule {
|
||||
|
||||
private static final ResourceBundle rb = AccessController.doPrivileged(
|
||||
new PrivilegedAction<ResourceBundle>() {
|
||||
public ResourceBundle run() {
|
||||
return ResourceBundle.getBundle(
|
||||
"sun.security.util.AuthResources");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/* -- Fields -- */
|
||||
|
||||
private static final int UNINITIALIZED = 0;
|
||||
private static final int INITIALIZED = 1;
|
||||
private static final int AUTHENTICATED = 2;
|
||||
private static final int LOGGED_IN = 3;
|
||||
|
||||
private static final int PROTECTED_PATH = 0;
|
||||
private static final int TOKEN = 1;
|
||||
private static final int NORMAL = 2;
|
||||
|
||||
private static final String NONE = "NONE";
|
||||
private static final String P11KEYSTORE = "PKCS11";
|
||||
|
||||
private static final TextOutputCallback bannerCallback =
|
||||
new TextOutputCallback
|
||||
(TextOutputCallback.INFORMATION,
|
||||
rb.getString("Please.enter.keystore.information"));
|
||||
private final ConfirmationCallback confirmationCallback =
|
||||
new ConfirmationCallback
|
||||
(ConfirmationCallback.INFORMATION,
|
||||
ConfirmationCallback.OK_CANCEL_OPTION,
|
||||
ConfirmationCallback.OK);
|
||||
|
||||
private Subject subject;
|
||||
private CallbackHandler callbackHandler;
|
||||
private Map<String, Object> sharedState;
|
||||
private Map<String, ?> options;
|
||||
|
||||
private char[] keyStorePassword;
|
||||
private char[] privateKeyPassword;
|
||||
private KeyStore keyStore;
|
||||
|
||||
private String keyStoreURL;
|
||||
private String keyStoreType;
|
||||
private String keyStoreProvider;
|
||||
private String keyStoreAlias;
|
||||
private String keyStorePasswordURL;
|
||||
private String privateKeyPasswordURL;
|
||||
private boolean debug;
|
||||
private javax.security.auth.x500.X500Principal principal;
|
||||
private Certificate[] fromKeyStore;
|
||||
private java.security.cert.CertPath certP = null;
|
||||
private X500PrivateCredential privateCredential;
|
||||
private int status = UNINITIALIZED;
|
||||
private boolean nullStream = false;
|
||||
private boolean token = false;
|
||||
private boolean protectedPath = false;
|
||||
|
||||
/* -- Methods -- */
|
||||
|
||||
/**
|
||||
* Initialize this <code>LoginModule</code>.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @param subject the <code>Subject</code> to be authenticated. <p>
|
||||
*
|
||||
* @param callbackHandler a <code>CallbackHandler</code> for communicating
|
||||
* with the end user (prompting for usernames and
|
||||
* passwords, for example),
|
||||
* which may be <code>null</code>. <p>
|
||||
*
|
||||
* @param sharedState shared <code>LoginModule</code> state. <p>
|
||||
*
|
||||
* @param options options specified in the login
|
||||
* <code>Configuration</code> for this particular
|
||||
* <code>LoginModule</code>.
|
||||
*/
|
||||
// Unchecked warning from (Map<String, Object>)sharedState is safe
|
||||
// since javax.security.auth.login.LoginContext passes a raw HashMap.
|
||||
@SuppressWarnings("unchecked")
|
||||
public void initialize(Subject subject,
|
||||
CallbackHandler callbackHandler,
|
||||
Map<String,?> sharedState,
|
||||
Map<String,?> options)
|
||||
{
|
||||
this.subject = subject;
|
||||
this.callbackHandler = callbackHandler;
|
||||
this.sharedState = (Map<String, Object>)sharedState;
|
||||
this.options = options;
|
||||
|
||||
processOptions();
|
||||
status = INITIALIZED;
|
||||
}
|
||||
|
||||
private void processOptions() {
|
||||
keyStoreURL = (String) options.get("keyStoreURL");
|
||||
if (keyStoreURL == null) {
|
||||
keyStoreURL =
|
||||
"file:" +
|
||||
System.getProperty("user.home").replace(
|
||||
File.separatorChar, '/') +
|
||||
'/' + ".keystore";
|
||||
} else if (NONE.equals(keyStoreURL)) {
|
||||
nullStream = true;
|
||||
}
|
||||
keyStoreType = (String) options.get("keyStoreType");
|
||||
if (keyStoreType == null) {
|
||||
keyStoreType = KeyStore.getDefaultType();
|
||||
}
|
||||
if (P11KEYSTORE.equalsIgnoreCase(keyStoreType)) {
|
||||
token = true;
|
||||
}
|
||||
|
||||
keyStoreProvider = (String) options.get("keyStoreProvider");
|
||||
|
||||
keyStoreAlias = (String) options.get("keyStoreAlias");
|
||||
|
||||
keyStorePasswordURL = (String) options.get("keyStorePasswordURL");
|
||||
|
||||
privateKeyPasswordURL = (String) options.get("privateKeyPasswordURL");
|
||||
|
||||
protectedPath = "true".equalsIgnoreCase((String)options.get
|
||||
("protected"));
|
||||
|
||||
debug = "true".equalsIgnoreCase((String) options.get("debug"));
|
||||
if (debug) {
|
||||
debugPrint(null);
|
||||
debugPrint("keyStoreURL=" + keyStoreURL);
|
||||
debugPrint("keyStoreType=" + keyStoreType);
|
||||
debugPrint("keyStoreProvider=" + keyStoreProvider);
|
||||
debugPrint("keyStoreAlias=" + keyStoreAlias);
|
||||
debugPrint("keyStorePasswordURL=" + keyStorePasswordURL);
|
||||
debugPrint("privateKeyPasswordURL=" + privateKeyPasswordURL);
|
||||
debugPrint("protectedPath=" + protectedPath);
|
||||
debugPrint(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user.
|
||||
*
|
||||
* <p> Get the Keystore alias and relevant passwords.
|
||||
* Retrieve the alias's principal and credentials from the Keystore.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @exception FailedLoginException if the authentication fails. <p>
|
||||
*
|
||||
* @return true in all cases (this <code>LoginModule</code>
|
||||
* should not be ignored).
|
||||
*/
|
||||
|
||||
public boolean login() throws LoginException {
|
||||
switch (status) {
|
||||
case UNINITIALIZED:
|
||||
default:
|
||||
throw new LoginException("The login module is not initialized");
|
||||
case INITIALIZED:
|
||||
case AUTHENTICATED:
|
||||
|
||||
if (token && !nullStream) {
|
||||
throw new LoginException
|
||||
("if keyStoreType is " + P11KEYSTORE +
|
||||
" then keyStoreURL must be " + NONE);
|
||||
}
|
||||
|
||||
if (token && privateKeyPasswordURL != null) {
|
||||
throw new LoginException
|
||||
("if keyStoreType is " + P11KEYSTORE +
|
||||
" then privateKeyPasswordURL must not be specified");
|
||||
}
|
||||
|
||||
if (protectedPath &&
|
||||
(keyStorePasswordURL != null ||
|
||||
privateKeyPasswordURL != null)) {
|
||||
throw new LoginException
|
||||
("if protected is true then keyStorePasswordURL and " +
|
||||
"privateKeyPasswordURL must not be specified");
|
||||
}
|
||||
|
||||
// get relevant alias and password info
|
||||
|
||||
if (protectedPath) {
|
||||
getAliasAndPasswords(PROTECTED_PATH);
|
||||
} else if (token) {
|
||||
getAliasAndPasswords(TOKEN);
|
||||
} else {
|
||||
getAliasAndPasswords(NORMAL);
|
||||
}
|
||||
|
||||
// log into KeyStore to retrieve data,
|
||||
// then clear passwords
|
||||
|
||||
try {
|
||||
getKeyStoreInfo();
|
||||
} finally {
|
||||
if (privateKeyPassword != null &&
|
||||
privateKeyPassword != keyStorePassword) {
|
||||
Arrays.fill(privateKeyPassword, '\0');
|
||||
privateKeyPassword = null;
|
||||
}
|
||||
if (keyStorePassword != null) {
|
||||
Arrays.fill(keyStorePassword, '\0');
|
||||
keyStorePassword = null;
|
||||
}
|
||||
}
|
||||
status = AUTHENTICATED;
|
||||
return true;
|
||||
case LOGGED_IN:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the alias and passwords to use for looking up in the KeyStore. */
|
||||
@SuppressWarnings("fallthrough")
|
||||
private void getAliasAndPasswords(int env) throws LoginException {
|
||||
if (callbackHandler == null) {
|
||||
|
||||
// No callback handler - check for alias and password options
|
||||
|
||||
switch (env) {
|
||||
case PROTECTED_PATH:
|
||||
checkAlias();
|
||||
break;
|
||||
case TOKEN:
|
||||
checkAlias();
|
||||
checkStorePass();
|
||||
break;
|
||||
case NORMAL:
|
||||
checkAlias();
|
||||
checkStorePass();
|
||||
checkKeyPass();
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Callback handler available - prompt for alias and passwords
|
||||
|
||||
NameCallback aliasCallback;
|
||||
if (keyStoreAlias == null || keyStoreAlias.length() == 0) {
|
||||
aliasCallback = new NameCallback(
|
||||
rb.getString("Keystore.alias."));
|
||||
} else {
|
||||
aliasCallback =
|
||||
new NameCallback(rb.getString("Keystore.alias."),
|
||||
keyStoreAlias);
|
||||
}
|
||||
|
||||
PasswordCallback storePassCallback = null;
|
||||
PasswordCallback keyPassCallback = null;
|
||||
|
||||
switch (env) {
|
||||
case PROTECTED_PATH:
|
||||
break;
|
||||
case NORMAL:
|
||||
keyPassCallback = new PasswordCallback
|
||||
(rb.getString("Private.key.password.optional."), false);
|
||||
// fall thru
|
||||
case TOKEN:
|
||||
storePassCallback = new PasswordCallback
|
||||
(rb.getString("Keystore.password."), false);
|
||||
break;
|
||||
}
|
||||
prompt(aliasCallback, storePassCallback, keyPassCallback);
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
debugPrint("alias=" + keyStoreAlias);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAlias() throws LoginException {
|
||||
if (keyStoreAlias == null) {
|
||||
throw new LoginException
|
||||
("Need to specify an alias option to use " +
|
||||
"KeyStoreLoginModule non-interactively.");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStorePass() throws LoginException {
|
||||
if (keyStorePasswordURL == null) {
|
||||
throw new LoginException
|
||||
("Need to specify keyStorePasswordURL option to use " +
|
||||
"KeyStoreLoginModule non-interactively.");
|
||||
}
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new URL(keyStorePasswordURL).openStream();
|
||||
keyStorePassword = Password.readPassword(in);
|
||||
} catch (IOException e) {
|
||||
LoginException le = new LoginException
|
||||
("Problem accessing keystore password \"" +
|
||||
keyStorePasswordURL + "\"");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ioe) {
|
||||
LoginException le = new LoginException(
|
||||
"Problem closing the keystore password stream");
|
||||
le.initCause(ioe);
|
||||
throw le;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkKeyPass() throws LoginException {
|
||||
if (privateKeyPasswordURL == null) {
|
||||
privateKeyPassword = keyStorePassword;
|
||||
} else {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new URL(privateKeyPasswordURL).openStream();
|
||||
privateKeyPassword = Password.readPassword(in);
|
||||
} catch (IOException e) {
|
||||
LoginException le = new LoginException
|
||||
("Problem accessing private key password \"" +
|
||||
privateKeyPasswordURL + "\"");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ioe) {
|
||||
LoginException le = new LoginException(
|
||||
"Problem closing the private key password stream");
|
||||
le.initCause(ioe);
|
||||
throw le;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prompt(NameCallback aliasCallback,
|
||||
PasswordCallback storePassCallback,
|
||||
PasswordCallback keyPassCallback)
|
||||
throws LoginException {
|
||||
|
||||
if (storePassCallback == null) {
|
||||
|
||||
// only prompt for alias
|
||||
|
||||
try {
|
||||
callbackHandler.handle(
|
||||
new Callback[] {
|
||||
bannerCallback, aliasCallback, confirmationCallback
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LoginException le = new LoginException
|
||||
("Problem retrieving keystore alias");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (UnsupportedCallbackException e) {
|
||||
throw new LoginException(
|
||||
"Error: " + e.getCallback().toString() +
|
||||
" is not available to retrieve authentication " +
|
||||
" information from the user");
|
||||
}
|
||||
|
||||
int confirmationResult = confirmationCallback.getSelectedIndex();
|
||||
|
||||
if (confirmationResult == ConfirmationCallback.CANCEL) {
|
||||
throw new LoginException("Login cancelled");
|
||||
}
|
||||
|
||||
saveAlias(aliasCallback);
|
||||
|
||||
} else if (keyPassCallback == null) {
|
||||
|
||||
// prompt for alias and key store password
|
||||
|
||||
try {
|
||||
callbackHandler.handle(
|
||||
new Callback[] {
|
||||
bannerCallback, aliasCallback,
|
||||
storePassCallback, confirmationCallback
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LoginException le = new LoginException
|
||||
("Problem retrieving keystore alias and password");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (UnsupportedCallbackException e) {
|
||||
throw new LoginException(
|
||||
"Error: " + e.getCallback().toString() +
|
||||
" is not available to retrieve authentication " +
|
||||
" information from the user");
|
||||
}
|
||||
|
||||
int confirmationResult = confirmationCallback.getSelectedIndex();
|
||||
|
||||
if (confirmationResult == ConfirmationCallback.CANCEL) {
|
||||
throw new LoginException("Login cancelled");
|
||||
}
|
||||
|
||||
saveAlias(aliasCallback);
|
||||
saveStorePass(storePassCallback);
|
||||
|
||||
} else {
|
||||
|
||||
// prompt for alias, key store password, and key password
|
||||
|
||||
try {
|
||||
callbackHandler.handle(
|
||||
new Callback[] {
|
||||
bannerCallback, aliasCallback,
|
||||
storePassCallback, keyPassCallback,
|
||||
confirmationCallback
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LoginException le = new LoginException
|
||||
("Problem retrieving keystore alias and passwords");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (UnsupportedCallbackException e) {
|
||||
throw new LoginException(
|
||||
"Error: " + e.getCallback().toString() +
|
||||
" is not available to retrieve authentication " +
|
||||
" information from the user");
|
||||
}
|
||||
|
||||
int confirmationResult = confirmationCallback.getSelectedIndex();
|
||||
|
||||
if (confirmationResult == ConfirmationCallback.CANCEL) {
|
||||
throw new LoginException("Login cancelled");
|
||||
}
|
||||
|
||||
saveAlias(aliasCallback);
|
||||
saveStorePass(storePassCallback);
|
||||
saveKeyPass(keyPassCallback);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAlias(NameCallback cb) {
|
||||
keyStoreAlias = cb.getName();
|
||||
}
|
||||
|
||||
private void saveStorePass(PasswordCallback c) {
|
||||
keyStorePassword = c.getPassword();
|
||||
if (keyStorePassword == null) {
|
||||
/* Treat a NULL password as an empty password */
|
||||
keyStorePassword = new char[0];
|
||||
}
|
||||
c.clearPassword();
|
||||
}
|
||||
|
||||
private void saveKeyPass(PasswordCallback c) {
|
||||
privateKeyPassword = c.getPassword();
|
||||
if (privateKeyPassword == null || privateKeyPassword.length == 0) {
|
||||
/*
|
||||
* Use keystore password if no private key password is
|
||||
* specified.
|
||||
*/
|
||||
privateKeyPassword = keyStorePassword;
|
||||
}
|
||||
c.clearPassword();
|
||||
}
|
||||
|
||||
/** Get the credentials from the KeyStore. */
|
||||
private void getKeyStoreInfo() throws LoginException {
|
||||
|
||||
/* Get KeyStore instance */
|
||||
try {
|
||||
if (keyStoreProvider == null) {
|
||||
keyStore = KeyStore.getInstance(keyStoreType);
|
||||
} else {
|
||||
keyStore =
|
||||
KeyStore.getInstance(keyStoreType, keyStoreProvider);
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
LoginException le = new LoginException
|
||||
("The specified keystore type was not available");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (NoSuchProviderException e) {
|
||||
LoginException le = new LoginException
|
||||
("The specified keystore provider was not available");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
}
|
||||
|
||||
/* Load KeyStore contents from file */
|
||||
InputStream in = null;
|
||||
try {
|
||||
if (nullStream) {
|
||||
// if using protected auth path, keyStorePassword will be null
|
||||
keyStore.load(null, keyStorePassword);
|
||||
} else {
|
||||
in = new URL(keyStoreURL).openStream();
|
||||
keyStore.load(in, keyStorePassword);
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
LoginException le = new LoginException
|
||||
("Incorrect keyStoreURL option");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (GeneralSecurityException e) {
|
||||
LoginException le = new LoginException
|
||||
("Error initializing keystore");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (IOException e) {
|
||||
LoginException le = new LoginException
|
||||
("Error initializing keystore");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ioe) {
|
||||
LoginException le = new LoginException
|
||||
("Error initializing keystore");
|
||||
le.initCause(ioe);
|
||||
throw le;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get certificate chain and create a certificate path */
|
||||
try {
|
||||
fromKeyStore =
|
||||
keyStore.getCertificateChain(keyStoreAlias);
|
||||
if (fromKeyStore == null
|
||||
|| fromKeyStore.length == 0
|
||||
|| !(fromKeyStore[0] instanceof X509Certificate))
|
||||
{
|
||||
throw new FailedLoginException(
|
||||
"Unable to find X.509 certificate chain in keystore");
|
||||
} else {
|
||||
LinkedList<Certificate> certList = new LinkedList<>();
|
||||
for (int i=0; i < fromKeyStore.length; i++) {
|
||||
certList.add(fromKeyStore[i]);
|
||||
}
|
||||
CertificateFactory certF=
|
||||
CertificateFactory.getInstance("X.509");
|
||||
certP =
|
||||
certF.generateCertPath(certList);
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
LoginException le = new LoginException("Error using keystore");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (CertificateException ce) {
|
||||
LoginException le = new LoginException
|
||||
("Error: X.509 Certificate type unavailable");
|
||||
le.initCause(ce);
|
||||
throw le;
|
||||
}
|
||||
|
||||
/* Get principal and keys */
|
||||
try {
|
||||
X509Certificate certificate = (X509Certificate)fromKeyStore[0];
|
||||
principal = new javax.security.auth.x500.X500Principal
|
||||
(certificate.getSubjectDN().getName());
|
||||
|
||||
// if token, privateKeyPassword will be null
|
||||
Key privateKey = keyStore.getKey(keyStoreAlias, privateKeyPassword);
|
||||
if (privateKey == null
|
||||
|| !(privateKey instanceof PrivateKey))
|
||||
{
|
||||
throw new FailedLoginException(
|
||||
"Unable to recover key from keystore");
|
||||
}
|
||||
|
||||
privateCredential = new X500PrivateCredential(
|
||||
certificate, (PrivateKey) privateKey, keyStoreAlias);
|
||||
} catch (KeyStoreException e) {
|
||||
LoginException le = new LoginException("Error using keystore");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LoginException le = new LoginException("Error using keystore");
|
||||
le.initCause(e);
|
||||
throw le;
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
FailedLoginException fle = new FailedLoginException
|
||||
("Unable to recover key from keystore");
|
||||
fle.initCause(e);
|
||||
throw fle;
|
||||
}
|
||||
if (debug) {
|
||||
debugPrint("principal=" + principal +
|
||||
"\n certificate="
|
||||
+ privateCredential.getCertificate() +
|
||||
"\n alias =" + privateCredential.getAlias());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method to commit the authentication process (phase 2).
|
||||
*
|
||||
* <p> This method is called if the LoginContext's
|
||||
* overall authentication succeeded
|
||||
* (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
|
||||
* succeeded).
|
||||
*
|
||||
* <p> If this LoginModule's own authentication attempt
|
||||
* succeeded (checked by retrieving the private state saved by the
|
||||
* <code>login</code> method), then this method associates a
|
||||
* <code>X500Principal</code> for the subject distinguished name of the
|
||||
* first certificate in the alias's credentials in the subject's
|
||||
* principals,the alias's certificate path in the subject's public
|
||||
* credentials, and a<code>X500PrivateCredential</code> whose certificate
|
||||
* is the first certificate in the alias's certificate path and whose
|
||||
* private key is the alias's private key in the subject's private
|
||||
* credentials. If this LoginModule's own
|
||||
* authentication attempted failed, then this method removes
|
||||
* any state that was originally saved.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @exception LoginException if the commit fails
|
||||
*
|
||||
* @return true if this LoginModule's own login and commit
|
||||
* attempts succeeded, or false otherwise.
|
||||
*/
|
||||
|
||||
public boolean commit() throws LoginException {
|
||||
switch (status) {
|
||||
case UNINITIALIZED:
|
||||
default:
|
||||
throw new LoginException("The login module is not initialized");
|
||||
case INITIALIZED:
|
||||
logoutInternal();
|
||||
throw new LoginException("Authentication failed");
|
||||
case AUTHENTICATED:
|
||||
if (commitInternal()) {
|
||||
return true;
|
||||
} else {
|
||||
logoutInternal();
|
||||
throw new LoginException("Unable to retrieve certificates");
|
||||
}
|
||||
case LOGGED_IN:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean commitInternal() throws LoginException {
|
||||
/* If the subject is not readonly add to the principal and credentials
|
||||
* set; otherwise just return true
|
||||
*/
|
||||
if (subject.isReadOnly()) {
|
||||
throw new LoginException ("Subject is set readonly");
|
||||
} else {
|
||||
subject.getPrincipals().add(principal);
|
||||
subject.getPublicCredentials().add(certP);
|
||||
subject.getPrivateCredentials().add(privateCredential);
|
||||
status = LOGGED_IN;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> This method is called if the LoginContext's
|
||||
* overall authentication failed.
|
||||
* (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
|
||||
* did not succeed).
|
||||
*
|
||||
* <p> If this LoginModule's own authentication attempt
|
||||
* succeeded (checked by retrieving the private state saved by the
|
||||
* <code>login</code> and <code>commit</code> methods),
|
||||
* then this method cleans up any state that was originally saved.
|
||||
*
|
||||
* <p> If the loaded KeyStore's provider extends
|
||||
* <code>java.security.AuthProvider</code>,
|
||||
* then the provider's <code>logout</code> method is invoked.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @exception LoginException if the abort fails.
|
||||
*
|
||||
* @return false if this LoginModule's own login and/or commit attempts
|
||||
* failed, and true otherwise.
|
||||
*/
|
||||
|
||||
public boolean abort() throws LoginException {
|
||||
switch (status) {
|
||||
case UNINITIALIZED:
|
||||
default:
|
||||
return false;
|
||||
case INITIALIZED:
|
||||
return false;
|
||||
case AUTHENTICATED:
|
||||
logoutInternal();
|
||||
return true;
|
||||
case LOGGED_IN:
|
||||
logoutInternal();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Logout a user.
|
||||
*
|
||||
* <p> This method removes the Principals, public credentials and the
|
||||
* private credentials that were added by the <code>commit</code> method.
|
||||
*
|
||||
* <p> If the loaded KeyStore's provider extends
|
||||
* <code>java.security.AuthProvider</code>,
|
||||
* then the provider's <code>logout</code> method is invoked.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @exception LoginException if the logout fails.
|
||||
*
|
||||
* @return true in all cases since this <code>LoginModule</code>
|
||||
* should not be ignored.
|
||||
*/
|
||||
|
||||
public boolean logout() throws LoginException {
|
||||
if (debug)
|
||||
debugPrint("Entering logout " + status);
|
||||
switch (status) {
|
||||
case UNINITIALIZED:
|
||||
throw new LoginException
|
||||
("The login module is not initialized");
|
||||
case INITIALIZED:
|
||||
case AUTHENTICATED:
|
||||
default:
|
||||
// impossible for LoginModule to be in AUTHENTICATED
|
||||
// state
|
||||
// assert status != AUTHENTICATED;
|
||||
return false;
|
||||
case LOGGED_IN:
|
||||
logoutInternal();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void logoutInternal() throws LoginException {
|
||||
if (debug) {
|
||||
debugPrint("Entering logoutInternal");
|
||||
}
|
||||
|
||||
// assumption is that KeyStore.load did a login -
|
||||
// perform explicit logout if possible
|
||||
LoginException logoutException = null;
|
||||
Provider provider = keyStore.getProvider();
|
||||
if (provider instanceof AuthProvider) {
|
||||
AuthProvider ap = (AuthProvider)provider;
|
||||
try {
|
||||
ap.logout();
|
||||
if (debug) {
|
||||
debugPrint("logged out of KeyStore AuthProvider");
|
||||
}
|
||||
} catch (LoginException le) {
|
||||
// save but continue below
|
||||
logoutException = le;
|
||||
}
|
||||
}
|
||||
|
||||
if (subject.isReadOnly()) {
|
||||
// attempt to destroy the private credential
|
||||
// even if the Subject is read-only
|
||||
principal = null;
|
||||
certP = null;
|
||||
status = INITIALIZED;
|
||||
// destroy the private credential
|
||||
Iterator<Object> it = subject.getPrivateCredentials().iterator();
|
||||
while (it.hasNext()) {
|
||||
Object obj = it.next();
|
||||
if (privateCredential.equals(obj)) {
|
||||
privateCredential = null;
|
||||
try {
|
||||
((Destroyable)obj).destroy();
|
||||
if (debug)
|
||||
debugPrint("Destroyed private credential, " +
|
||||
obj.getClass().getName());
|
||||
break;
|
||||
} catch (DestroyFailedException dfe) {
|
||||
LoginException le = new LoginException
|
||||
("Unable to destroy private credential, "
|
||||
+ obj.getClass().getName());
|
||||
le.initCause(dfe);
|
||||
throw le;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// throw an exception because we can not remove
|
||||
// the principal and public credential from this
|
||||
// read-only Subject
|
||||
throw new LoginException
|
||||
("Unable to remove Principal ("
|
||||
+ "X500Principal "
|
||||
+ ") and public credential (certificatepath) "
|
||||
+ "from read-only Subject");
|
||||
}
|
||||
if (principal != null) {
|
||||
subject.getPrincipals().remove(principal);
|
||||
principal = null;
|
||||
}
|
||||
if (certP != null) {
|
||||
subject.getPublicCredentials().remove(certP);
|
||||
certP = null;
|
||||
}
|
||||
if (privateCredential != null) {
|
||||
subject.getPrivateCredentials().remove(privateCredential);
|
||||
privateCredential = null;
|
||||
}
|
||||
|
||||
// throw pending logout exception if there is one
|
||||
if (logoutException != null) {
|
||||
throw logoutException;
|
||||
}
|
||||
status = INITIALIZED;
|
||||
}
|
||||
|
||||
private void debugPrint(String message) {
|
||||
// we should switch to logging API
|
||||
if (message == null) {
|
||||
System.err.println();
|
||||
} else {
|
||||
System.err.println("Debug KeyStoreLoginModule: " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user