/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.io.dav.http; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.security.KeyStore; import java.security.Principal; import java.security.PrivateKey; import java.security.Provider; import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.X509KeyManager; import org.tmatesoft.svn.core.SVNAuthenticationException; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.BasicAuthenticationManager; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.auth.SVNAuthentication; import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication; import org.tmatesoft.svn.core.auth.SVNSSLAuthentication; import org.tmatesoft.svn.core.internal.wc.ISVNSSLPasspharsePromptSupport; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; import org.tmatesoft.svn.util.SVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public final class HTTPSSLKeyManager implements X509KeyManager { public static KeyManager[] loadClientCertificate() throws SVNException { Provider pjacapi = Security.getProvider("CAPI"); Provider pmscapi = Security.getProvider("SunMSCAPI"); KeyManager[] result = null; SVNDebugLog.getDefaultLog().logError(SVNLogType.CLIENT,"using mscapi"); // get key store KeyStore keyStore = null; // Note: When a security manager is installed, // the following call requires SecurityPermission // "authProvider.SunMSCAPI". try { if (pmscapi != null) { pmscapi.setProperty("Signature.SHA1withRSA","sun.security.mscapi.RSASignature$SHA1"); keyStore = KeyStore.getInstance("Windows-MY",pmscapi); } else if (pjacapi != null) { keyStore = KeyStore.getInstance("CAPI"); } if (keyStore != null) { keyStore.load(null, null); } } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, "Problems, when connecting with ms capi! "+th.getMessage(), null, SVNErrorMessage.TYPE_ERROR, th), th); } KeyManagerFactory kmf = null; if (keyStore != null) { try { kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); if (kmf != null) { kmf.init(keyStore, null); result = kmf.getKeyManagers(); } } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, "MS Capi error: "+th.getMessage()), th); } } return result; } /** * @deprecated * * @param clientCertFile * @param clientCertPassword * @return * @throws SVNException */ public static KeyManager[] loadClientCertificate(File clientCertFile, String clientCertPassword) throws SVNException { return loadClientCertificate(clientCertFile, clientCertPassword != null ? clientCertPassword.toCharArray() : null); } public static KeyManager[] loadClientCertificate(byte[] clientCert, char[] clientCertPassword) throws SVNException { if (clientCertPassword == null || clientCertPassword.length == 0) { // Client certificates without an passphrase can't be received from Java Keystores. throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, "No client certificate passphrase supplied (did you forget to specify?).\n" + "Note that client certificates with empty passphrases can''t be used. In this case please re-create the certificate with a passphrase.")); } KeyManager[] result = null; KeyStore keyStore = null; final InputStream is = new ByteArrayInputStream(clientCert); try { keyStore = KeyStore.getInstance("PKCS12"); if (keyStore != null) { keyStore.load(is, clientCertPassword); } } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, th.getMessage(), null, SVNErrorMessage.TYPE_ERROR, th), th); } finally { SVNFileUtil.closeFile(is); } KeyManagerFactory kmf = null; if (keyStore != null) { try { kmf = KeyManagerFactory.getInstance("SunX509"); if (kmf != null) { kmf.init(keyStore, clientCertPassword); result = kmf.getKeyManagers(); } } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, th.getMessage()), th); } } return result; } public static KeyManager[] loadClientCertificate(File clientCertFile, char[] clientCertPassword) throws SVNException { if (clientCertPassword == null || clientCertPassword.length == 0) { // Client certificates without an passphrase can't be received from Java Keystores. throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, "No client certificate passphrase supplied (did you forget to specify?).\n" + "Note that client certificates with empty passphrases can''t be used. In this case please re-create the certificate with a passphrase.")); } KeyManager[] result = null; KeyStore keyStore = null; if (clientCertFile != null && clientCertFile.getName().startsWith(SVNSSLAuthentication.MSCAPI)) { SVNDebugLog.getDefaultLog().logError(SVNLogType.CLIENT,"using mscapi"); try { keyStore = KeyStore.getInstance("Windows-MY"); SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, "using my windows store"); if (keyStore != null) { keyStore.load(null, null); } KeyManagerFactory kmf = null; if (keyStore != null) { kmf = KeyManagerFactory.getInstance("SunX509"); if (kmf != null) { kmf.init(keyStore, clientCertPassword); result = kmf.getKeyManagers(); } } return result; } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, "loadClientCertificate ms capi with file - should not be called: "+th.getMessage(), null, SVNErrorMessage.TYPE_ERROR, th), th); } } final InputStream is = SVNFileUtil.openFileForReading(clientCertFile, SVNLogType.NETWORK); try { keyStore = KeyStore.getInstance("PKCS12"); if (keyStore != null) { keyStore.load(is, clientCertPassword); } } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, th.getMessage(), null, SVNErrorMessage.TYPE_ERROR, th), th); } finally { SVNFileUtil.closeFile(is); } KeyManagerFactory kmf = null; if (keyStore != null) { try { kmf = KeyManagerFactory.getInstance("SunX509"); if (kmf != null) { kmf.init(keyStore, clientCertPassword); result = kmf.getKeyManagers(); } } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, th.getMessage()), th); } } return result; } public KeyManager[] loadClientCertificate(SVNSSLAuthentication sslAuthentication) throws SVNException { char[] clientCertPassword = sslAuthentication.getPasswordValue(); String clientCertPath = sslAuthentication.getCertificatePath(); File clientCertFile = sslAuthentication.getCertificateFile(); byte[] clientCertData = sslAuthentication.getCertificate(); char[] passphrase = clientCertPassword == null ? new char[0] : clientCertPassword; String realm = clientCertPath; SVNAuthentication auth = null; KeyManager[] result = null; KeyStore keyStore = null; if (clientCertFile != null && clientCertFile.getName().startsWith(SVNSSLAuthentication.MSCAPI)) { SVNDebugLog.getDefaultLog().logError(SVNLogType.CLIENT,"using mscapi"); try { keyStore = KeyStore.getInstance("Windows-MY"); SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, "using my windows store"); if (keyStore != null) { keyStore.load(null, null); } KeyManagerFactory kmf = null; if (keyStore != null) { kmf = KeyManagerFactory.getInstance("SunX509"); if (kmf != null) { kmf.init(keyStore, passphrase); result = kmf.getKeyManagers(); } } return result; } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNAuthenticationException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, "loadClientCertificate ms capi with file - should not be called: "+th.getMessage(), null, SVNErrorMessage.TYPE_ERROR, th), th); } } while(true) { try { final InputStream is = clientCertData != null ? new ByteArrayInputStream(clientCertData) : SVNFileUtil.openFileForReading(clientCertFile, SVNLogType.NETWORK); try { keyStore = KeyStore.getInstance("PKCS12"); if (keyStore != null) { keyStore.load(is, passphrase); } } finally { SVNFileUtil.closeFile(is); } if (auth != null) { BasicAuthenticationManager.acknowledgeAuthentication(true, ISVNAuthenticationManager.SSL, realm, null, auth, url, authenticationManager); } else { BasicAuthenticationManager.acknowledgeAuthentication(true, ISVNAuthenticationManager.SSL, clientCertPath, null, SVNPasswordAuthentication.newInstance("", clientCertPassword, sslAuthentication.isStorageAllowed(), sslAuthentication.getURL(), false), url, authenticationManager); } break; } catch (IOException io) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, io); if (auth != null) { BasicAuthenticationManager.acknowledgeAuthentication(false, ISVNAuthenticationManager.SSL, realm, SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, io.getMessage()), auth, url, authenticationManager); auth = authenticationManager.getNextAuthentication(ISVNAuthenticationManager.SSL, realm, sslAuthentication.getURL()); } else { auth = authenticationManager.getFirstAuthentication(ISVNAuthenticationManager.SSL, realm, sslAuthentication.getURL()); } if (auth instanceof SVNPasswordAuthentication) { passphrase = ((SVNPasswordAuthentication) auth).getPasswordValue(); } else { auth = null; SVNErrorManager.cancel("authentication cancelled", SVNLogType.NETWORK); break; } continue; } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, th.getMessage(), null, SVNErrorMessage.TYPE_ERROR, th), th); } } KeyManagerFactory kmf = null; if (keyStore != null) { try { kmf = KeyManagerFactory.getInstance("SunX509"); if (kmf != null) { kmf.init(keyStore, passphrase); result = kmf.getKeyManagers(); } } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.NETWORK, th); throw new SVNAuthenticationException(SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, th.getMessage()), th); } } return result; } private final ISVNAuthenticationManager authenticationManager; private final String realm; private final SVNURL url; private KeyManager[] myKeyManagers; private SVNSSLAuthentication myAuthentication; private Exception myException; private String chooseAlias = null; private boolean myIsFirstRequest = true; public HTTPSSLKeyManager(ISVNAuthenticationManager authenticationManager, String realm, SVNURL url) { this.authenticationManager = authenticationManager; this.realm = realm; this.url = url; } public String[] getClientAliases(String location, Principal[] principals) { if (!initializeNoException()) { return null; } for (Iterator<X509KeyManager> it = getX509KeyManagers(myKeyManagers).iterator(); it.hasNext();) { final X509KeyManager keyManager = it.next(); final String[] clientAliases = keyManager.getClientAliases(location, principals); if (clientAliases != null) { return clientAliases; } } return null; } public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { if (!initializeNoException()) { return null; } if (chooseAlias != null) { return chooseAlias; } for (Iterator<X509KeyManager> it = getX509KeyManagers(myKeyManagers).iterator(); it.hasNext();) { final X509KeyManager keyManager = it.next(); final String clientAlias = keyManager.chooseClientAlias(strings, principals, socket); if (clientAlias != null) { return clientAlias; } } return null; } public String[] getServerAliases(String location, Principal[] principals) { if (!initializeNoException()) { return null; } for (Iterator<X509KeyManager> it = getX509KeyManagers(myKeyManagers).iterator(); it.hasNext();) { final X509KeyManager keyManager = it.next(); final String[] serverAliases = keyManager.getServerAliases(location, principals); if (serverAliases != null) { return serverAliases; } } return null; } public String chooseServerAlias(String location, Principal[] principals, Socket socket) { if (!initializeNoException()) { return null; } for (Iterator<X509KeyManager> it = getX509KeyManagers(myKeyManagers).iterator(); it.hasNext();) { final X509KeyManager keyManager = it.next(); final String serverAlias = keyManager.chooseServerAlias(location, principals, socket); if (serverAlias != null) { return serverAlias; } } return null; } public X509Certificate[] getCertificateChain(String location) { if (!initializeNoException()) { return null; } for (Iterator<X509KeyManager> it = getX509KeyManagers(myKeyManagers).iterator(); it.hasNext();) { final X509KeyManager keyManager = it.next(); final X509Certificate[] certificateChain = keyManager.getCertificateChain(location); if (certificateChain != null) { return certificateChain; } } return null; } public PrivateKey getPrivateKey(String string) { if (!initializeNoException()) { return null; } for (Iterator<X509KeyManager> it = getX509KeyManagers(myKeyManagers).iterator(); it.hasNext();) { final X509KeyManager keyManager = it.next(); final PrivateKey privateKey = keyManager.getPrivateKey(string); if (privateKey != null) { return privateKey; } } return null; } public Exception getException() { return myException; } public void acknowledgeAndClearAuthentication(SVNErrorMessage errorMessage) throws SVNException { if (myAuthentication != null) { BasicAuthenticationManager.acknowledgeAuthentication(errorMessage == null, ISVNAuthenticationManager.SSL, realm, errorMessage, myAuthentication, url, authenticationManager); } if (errorMessage != null) { myKeyManagers = null; chooseAlias = null; } else { myAuthentication = null; myIsFirstRequest = true; } final Exception exception = myException; myException = null; if (exception instanceof SVNException) { throw (SVNException)exception; } else if (exception != null) { throw new SVNException(SVNErrorMessage.UNKNOWN_ERROR_MESSAGE, exception); } } public boolean isInitialized() { return myKeyManagers != null; } private boolean initializeNoException() { try { final boolean result = initialize(); myException = null; return result; } catch (Exception exception) { myException = exception; return false; } } private boolean initialize() throws SVNException { if (myKeyManagers != null) { return true; } for (; ;) { if (myIsFirstRequest) { myAuthentication = (SVNSSLAuthentication)authenticationManager.getFirstAuthentication(ISVNAuthenticationManager.SSL, realm, url); myIsFirstRequest = false; } else { myAuthentication = (SVNSSLAuthentication)authenticationManager.getNextAuthentication(ISVNAuthenticationManager.SSL, realm, url); } if (myAuthentication == null) { SVNErrorManager.cancel("SSL authentication with client certificate cancelled", SVNLogType.NETWORK); } final KeyManager[] keyManagers; try { if (isMSCAPI(myAuthentication)) { keyManagers = loadClientCertificate(); chooseAlias = myAuthentication.getAlias(); } else { if (authenticationManager instanceof ISVNSSLPasspharsePromptSupport && ((ISVNSSLPasspharsePromptSupport) authenticationManager).isSSLPassphrasePromtSupported()) { keyManagers = loadClientCertificate(myAuthentication); } else { if (myAuthentication.getCertificate() != null) { keyManagers = loadClientCertificate(myAuthentication.getCertificate(), myAuthentication.getPasswordValue()); } else { keyManagers = loadClientCertificate(myAuthentication.getCertificateFile(), myAuthentication.getPasswordValue()); } } } } catch (SVNAuthenticationException authenticationException) { throw authenticationException; } catch (SVNCancelException cancel) { throw cancel; } catch (SVNException ex) { final SVNErrorMessage sslErr = SVNErrorMessage.create(SVNErrorCode.RA_NOT_AUTHORIZED, "Failed to load SSL client certificate: ''{0}''", new Object[] { ex.getMessage() }, SVNErrorMessage.TYPE_ERROR, ex.getCause()); BasicAuthenticationManager.acknowledgeAuthentication(false, ISVNAuthenticationManager.SSL, realm, sslErr, myAuthentication, url, authenticationManager); continue; } myKeyManagers = keyManagers; return true; } } private static List<X509KeyManager> getX509KeyManagers(KeyManager[] keyManagers) { final List<X509KeyManager> x509KeyManagers = new ArrayList<X509KeyManager>(); for (int index = 0; index < keyManagers.length; index++) { final KeyManager keyManager = keyManagers[index]; if (keyManager instanceof X509KeyManager) { x509KeyManagers.add((X509KeyManager) keyManager); } } return x509KeyManagers; } private static boolean isMSCAPI(SVNSSLAuthentication sslAuthentication) { return sslAuthentication != null && SVNSSLAuthentication.MSCAPI.equals(sslAuthentication.getSSLKind()); } }