/*
* Copyright (c) 2000, 2011, 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 javax.security.auth.x500.X500Principal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AuthProvider;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.UnrecoverableKeyException;
import java.security.cert.*;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.ResourceBundle;
import javax.security.auth.Destroyable;
import javax.security.auth.DestroyFailedException;
import javax.security.auth.Subject;
import javax.security.auth.x500.*;
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.AuthResources;
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>
*/
public class KeyStoreLoginModule implements LoginModule {
static final java.util.ResourceBundle rb =
java.util.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 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>.
*/
public void initialize(Subject subject,
CallbackHandler callbackHandler,
Map<String,?> sharedState,
Map<String,?> options)
{
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = 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. */
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);
}
}
}