001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.auth.module;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.net.URL;
023: import java.security.AuthProvider;
024: import java.security.Key;
025: import java.security.KeyStore;
026: import java.security.PrivateKey;
027: import java.security.Provider;
028: import java.security.Security;
029: import java.security.cert.CertPath;
030: import java.security.cert.Certificate;
031: import java.security.cert.CertificateFactory;
032: import java.security.cert.X509Certificate;
033: import java.util.ArrayList;
034: import java.util.List;
035: import java.util.Map;
036:
037: import javax.security.auth.Subject;
038: import javax.security.auth.callback.Callback;
039: import javax.security.auth.callback.CallbackHandler;
040: import javax.security.auth.callback.NameCallback;
041: import javax.security.auth.callback.PasswordCallback;
042: import javax.security.auth.login.FailedLoginException;
043: import javax.security.auth.login.LoginException;
044: import javax.security.auth.spi.LoginModule;
045: import javax.security.auth.x500.X500Principal;
046: import javax.security.auth.x500.X500PrivateCredential;
047:
048: public class KeyStoreLoginModule implements LoginModule {
049:
050: private static final String DEFAULT_KEYSTORE_TYPE = KeyStore
051: .getDefaultType();
052:
053: private LoginModuleUtils.LoginModuleStatus status = new LoginModuleUtils.LoginModuleStatus();
054:
055: private Subject subject;
056:
057: private CallbackHandler callbackHandler;
058:
059: //private Map<String,?> sharedState;
060:
061: private Map<String, ?> options;
062:
063: private String keyStoreURL;
064:
065: private String keyStoreType;
066:
067: private Provider keyStoreProvider;
068:
069: private String keyStoreAlias;
070:
071: private CertPath certPath;
072:
073: private X500Principal principal;
074:
075: private X500PrivateCredential privateCredential;
076:
077: private char[] keyStorePassword;
078:
079: private char[] privateKeyPassword;
080:
081: private boolean needKeyStorePassword = true;
082:
083: private boolean needPrivateKeyPassword = true;
084:
085: public boolean abort() throws LoginException {
086: LoginModuleUtils.ACTION action = status.checkAbout();
087: if (action.equals(LoginModuleUtils.ACTION.no_action)) {
088: if (status.isLoggined()) {
089: return true;
090: } else {
091: return false;
092: }
093: }
094: clear();
095: return true;
096: }
097:
098: public boolean commit() throws LoginException {
099: LoginModuleUtils.ACTION action = status.checkCommit();
100: switch (action) {
101: case no_action:
102: return true;
103: case logout:
104: clear();
105: throw new LoginException("Fail to login");
106: default:
107: if (subject.isReadOnly()) {
108: clear();
109: throw new LoginException("Subject is readonly.");
110: }
111: subject.getPrincipals().add(principal);
112: subject.getPublicCredentials().add(certPath);
113: subject.getPrivateCredentials().add(privateCredential);
114: status.committed();
115: return true;
116: }
117: }
118:
119: public void initialize(Subject subject,
120: CallbackHandler callbackHandler,
121: Map<String, ?> sharedState, Map<String, ?> options) {
122: if (null == options) {
123: throw new NullPointerException();
124: }
125: this .subject = subject;
126: this .callbackHandler = callbackHandler;
127: // this.sharedState = sharedState;
128: this .options = options;
129:
130: //clear state
131: this .keyStoreAlias = null;
132: this .keyStorePassword = null;
133: this .privateKeyPassword = null;
134: status.initialized();
135: }
136:
137: public boolean login() throws LoginException {
138: LoginModuleUtils.ACTION action = status.checkLogin();
139: if (action.equals(LoginModuleUtils.ACTION.no_action)) {
140: return true;
141: }
142: getKeyStoreParameters();
143: getPrincipalsFromKeyStore();
144: status.logined();
145: return true;
146: }
147:
148: public boolean logout() throws LoginException {
149: LoginModuleUtils.ACTION action = status.checkLogout();
150: if (action.equals(LoginModuleUtils.ACTION.no_action)) {
151: return true;
152: }
153: clear();
154: return true;
155: }
156:
157: private void getKeyStoreParameters() throws LoginException {
158: // Get parameters from options.
159: keyStoreURL = (String) options.get("keyStoreURL");
160: keyStoreType = (String) options.get("keyStoreType");
161: if (null == keyStoreType) {
162: keyStoreType = DEFAULT_KEYSTORE_TYPE;
163: }
164: String keyStoreProvider = (String) options
165: .get("keyStoreProvider");
166: if (keyStoreProvider != null) {
167: this .keyStoreProvider = Security
168: .getProvider(keyStoreProvider);
169: }
170: keyStoreAlias = (String) options.get("keyStoreAlias");
171: String keyStorePasswordURL = (String) options
172: .get("keyStorePasswordURL");
173: String privateKeyPasswordURL = (String) options
174: .get("privateKeyPasswordURL");
175: boolean has_protected_authentication_path = "true"
176: .equalsIgnoreCase((String) options.get("protected"));
177:
178: if (keyStoreType != null && keyStoreType.equals("PKCS11")) {
179: if (!keyStoreURL.equals("NONE")
180: || privateKeyPasswordURL != null) {
181: throw new LoginException(
182: "PKCS11 must have NONE as keyStoreURL and privateKeyPasswordURL unset");
183: }
184: needPrivateKeyPassword = false;
185: }
186:
187: if (has_protected_authentication_path) {
188: if (keyStorePasswordURL != null
189: && privateKeyPasswordURL != null) {
190: throw new LoginException(
191: "Protected authentication path must have keyStorePasswordURL and privateKeyPasswordURL unset");
192: }
193: needKeyStorePassword = false;
194: needPrivateKeyPassword = false;
195: }
196:
197: if (this .callbackHandler != null) {
198: this .getParametersWithCallbackHandler();
199: } else {
200: this .getParametersWithoutCallbackHandler(
201: keyStorePasswordURL, privateKeyPasswordURL);
202: }
203:
204: // privateKeyPassword is empty, use keystorepassword instead.
205: if (needPrivateKeyPassword
206: && (privateKeyPassword == null || privateKeyPassword.length == 0)) {
207: privateKeyPassword = keyStorePassword;
208: }
209: }
210:
211: private void getParametersWithCallbackHandler()
212: throws LoginException {
213: ArrayList<Callback> callbacks = new ArrayList<Callback>();
214: NameCallback keyStoreAliasNameCallback = new NameCallback(
215: "KeyStore Alias");
216: callbacks.add(keyStoreAliasNameCallback);
217: PasswordCallback keyStorePasswordCallback = null;
218: if (needKeyStorePassword) {
219: keyStorePasswordCallback = new PasswordCallback(
220: "KeyStore password", false);
221: callbacks.add(keyStorePasswordCallback);
222: }
223: PasswordCallback privateKeyPasswordCallback = null;
224: if (needPrivateKeyPassword) {
225: privateKeyPasswordCallback = new PasswordCallback(
226: "PrivateKey password", false);
227: callbacks.add(privateKeyPasswordCallback);
228: }
229:
230: try {
231: callbackHandler.handle(callbacks
232: .toArray(new Callback[callbacks.size()]));
233: } catch (Exception e) {
234: throw new LoginException(e.toString());
235: }
236: keyStoreAlias = keyStoreAliasNameCallback.getName();
237: if (needKeyStorePassword) {
238: keyStorePassword = keyStorePasswordCallback.getPassword();
239: }
240: if (needPrivateKeyPassword) {
241: privateKeyPassword = privateKeyPasswordCallback
242: .getPassword();
243: }
244: }
245:
246: private void getParametersWithoutCallbackHandler(
247: String keyStorePasswordURL, String privateKeyPasswordURL)
248: throws LoginException {
249: InputStream keyStorePasswordInputStream = null;
250: InputStream privateKeyPasswordInputStream = null;
251: try {
252: if (keyStorePasswordURL != null) {
253: keyStorePasswordInputStream = new URL(
254: keyStorePasswordURL).openStream();
255: this .keyStorePassword = LoginModuleUtils
256: .getPassword(keyStorePasswordInputStream);
257: }
258:
259: if (privateKeyPasswordURL != null) {
260: privateKeyPasswordInputStream = new URL(
261: privateKeyPasswordURL).openStream();
262: privateKeyPassword = LoginModuleUtils
263: .getPassword(privateKeyPasswordInputStream);
264: }
265: } catch (Exception e) {
266:
267: } finally {
268: if (keyStorePasswordInputStream != null) {
269: try {
270: keyStorePasswordInputStream.close();
271: } catch (IOException e1) {
272: }
273: }
274: if (privateKeyPasswordInputStream != null) {
275: try {
276: privateKeyPasswordInputStream.close();
277: } catch (IOException e1) {
278: }
279: }
280: }
281:
282: if (null == keyStoreURL
283: || (needKeyStorePassword && null == keyStorePassword)) {
284: throw new LoginException(
285: "Failure to get KeyStore or KeyStore Password");
286: }
287: }
288:
289: private void getPrincipalsFromKeyStore() throws LoginException {
290:
291: InputStream keyStoreInputStream;
292: try {
293:
294: KeyStore keyStore = keyStoreProvider == null ? KeyStore
295: .getInstance(keyStoreType) : KeyStore.getInstance(
296: keyStoreType, keyStoreProvider);
297: keyStoreInputStream = keyStoreURL.equals("NONE") ? null
298: : new URL(keyStoreURL).openStream();
299:
300: keyStore.load(keyStoreInputStream, keyStorePassword);
301: Certificate[] certificates = keyStore
302: .getCertificateChain(keyStoreAlias);
303: if (null == certificates || certificates.length == 0) {
304: throw new FailedLoginException(
305: "Cannot find certificate path for "
306: + keyStoreAlias);
307: }
308: List<Certificate> list = new ArrayList<Certificate>(
309: certificates.length);
310: for (int i = 0; i < certificates.length; i++) {
311: list.add(certificates[i]);
312: }
313: CertificateFactory certificateFactory = CertificateFactory
314: .getInstance("X.509");
315: certPath = certificateFactory.generateCertPath(list);
316:
317: X509Certificate firstCertificate = (X509Certificate) certificates[0];
318: principal = new X500Principal(firstCertificate
319: .getSubjectDN().getName());
320:
321: Key privateKey = keyStore.getKey(keyStoreAlias,
322: privateKeyPassword);
323: if (null == privateKey
324: || !(privateKey instanceof PrivateKey)) {
325: throw new FailedLoginException(
326: "Cannot find private key for " + keyStoreAlias);
327: }
328: privateCredential = new X500PrivateCredential(
329: firstCertificate, (PrivateKey) privateKey,
330: keyStoreAlias);
331:
332: } catch (Exception e) {
333: if (e instanceof LoginException) {
334: throw (LoginException) e;
335: }
336: throw new LoginException(e.toString());
337: }
338: }
339:
340: private void clear() throws LoginException {
341: LoginModuleUtils.clearPassword(keyStorePassword);
342: keyStorePassword = null;
343: LoginModuleUtils.clearPassword(privateKeyPassword);
344: privateKeyPassword = null;
345:
346: if (keyStoreProvider instanceof AuthProvider) {
347: ((AuthProvider) (keyStoreProvider)).logout();
348: }
349:
350: if (principal != null) {
351: subject.getPrincipals().remove(principal);
352: principal = null;
353: }
354: if (certPath != null) {
355: subject.getPublicCredentials().remove(certPath);
356: certPath = null;
357: }
358: if (privateCredential != null) {
359: subject.getPrivateCredentials().remove(privateCredential);
360: privateCredential.destroy();
361: privateCredential = null;
362: }
363: status.logouted();
364: }
365:
366: }
|