package org.tmatesoft.svn.core.internal.wc;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.net.ssl.X509TrustManager;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider;
import org.tmatesoft.svn.core.internal.util.SVNBase64;
import org.tmatesoft.svn.core.internal.util.SVNSSLUtil;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @author TMate Software Ltd.
* @version 1.2.0
*/
public class DefaultSVNSSLTrustManager implements X509TrustManager {
private SVNURL myURL;
private DefaultSVNAuthenticationManager myAuthManager;
private X509Certificate[] myTrustedCerts;
private String myRealm;
private File myAuthDirectory;
private boolean myIsUseKeyStore;
private File[] myServerCertFiles;
public DefaultSVNSSLTrustManager(File authDir, SVNURL url, File[] serverCertFiles, boolean useKeyStore, DefaultSVNAuthenticationManager authManager) {
myURL = url;
myAuthDirectory = authDir;
myRealm = "https://" + url.getHost() + ":" + url.getPort();
myAuthManager = authManager;
myIsUseKeyStore = useKeyStore;
myServerCertFiles = serverCertFiles;
}
private void init() {
if (myTrustedCerts != null) {
return;
}
Collection trustedCerts = new ArrayList();
// load trusted certs from files.
for (int i = 0; i < myServerCertFiles.length; i++) {
X509Certificate cert = loadCertificate(myServerCertFiles[i]);
if (cert != null) {
trustedCerts.add(cert);
}
}
// load from 'default' keystore
if (myIsUseKeyStore) {
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
if (keyStore != null) {
String path = System.getProperty("java.home") + "/lib/security/cacerts";
path = path.replace('/', File.separatorChar);
File file = new File(path);
InputStream is = null;
try {
if (file.isFile() && file.canRead()) {
is = SVNFileUtil.openFileForReading(file, SVNLogType.WC);
}
keyStore.load(is, null);
}
catch (NoSuchAlgorithmException e) {
}
catch (CertificateException e) {
}
catch (IOException e) {
}
catch (SVNException e) {
}
finally {
SVNFileUtil.closeFile(is);
}
PKIXParameters params = new PKIXParameters(keyStore);
for (Iterator anchors = params.getTrustAnchors().iterator(); anchors.hasNext();) {
TrustAnchor ta = (TrustAnchor)anchors.next();
X509Certificate cert = ta.getTrustedCert();
if (cert != null) {
trustedCerts.add(cert);
}
}
}
}
catch (KeyStoreException e) {
}
catch (InvalidAlgorithmParameterException e) {
}
}
myTrustedCerts = (X509Certificate[])trustedCerts.toArray(new X509Certificate[trustedCerts.size()]);
}
public X509Certificate[] getAcceptedIssuers() {
init();
return myTrustedCerts;
}
public void checkClientTrusted(X509Certificate[] certs, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] certs, String algorithm) throws CertificateException {
if (certs != null && certs.length > 0 && certs[0] != null) {
String data = SVNBase64.byteArrayToBase64(certs[0].getEncoded());
String stored = (String) myAuthManager.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm);
if (data.equals(stored)) {
return;
}
stored = getStoredServerCertificate(myRealm);
if (data.equals(stored)) {
return;
}
ISVNAuthenticationProvider authProvider = myAuthManager.getAuthenticationProvider();
int failures = SVNSSLUtil.getServerCertificateFailures(certs[0], myURL.getHost());
// compose bit mask.
// 8 is default
// check dates for 1 and 2
// check host name for 4
if (authProvider != null) {
boolean store = myAuthManager.isAuthStorageEnabled();
int result = authProvider.acceptServerAuthentication(myURL, myRealm, certs[0], store);
if (result == ISVNAuthenticationProvider.ACCEPTED && store) {
try {
storeServerCertificate(myRealm, data, failures);
} catch (SVNException e) {
throw new SVNSSLUtil.CertificateNotTrustedException("svn: Server SSL ceritificate for '" + myRealm + "' cannot be saved");
}
}
if (result != ISVNAuthenticationProvider.REJECTED) {
myAuthManager.getRuntimeAuthStorage().putData("svn.ssl.server", myRealm, data);
return;
}
throw new SVNSSLUtil.CertificateNotTrustedException("svn: Server SSL ceritificate for '" + myRealm + "' rejected");
}
// like as tmp. accepted.
return;
}
}
private String getStoredServerCertificate(String realm) {
File file = new File(myAuthDirectory, SVNFileUtil.computeChecksum(realm));
if (!file.isFile()) {
return null;
}
SVNWCProperties props = new SVNWCProperties(file, "");
try {
String storedRealm = props.getPropertyValue("svn:realmstring");
if (!realm.equals(storedRealm)) {
return null;
}
return props.getPropertyValue("ascii_cert");
}
catch (SVNException e) {
}
return null;
}
private void storeServerCertificate(String realm, String data, int failures) throws SVNException {
myAuthDirectory.mkdirs();
File file = new File(myAuthDirectory, SVNFileUtil.computeChecksum(realm));
SVNWCProperties props = new SVNWCProperties(file, "");
props.delete();
try {
props.setPropertyValue("ascii_cert", data);
props.setPropertyValue("svn:realmstring", realm);
props.setPropertyValue("failures", Integer.toString(failures));
SVNFileUtil.setReadonly(props.getFile(), false);
}
catch (SVNException e) {
props.delete();
}
}
public static X509Certificate loadCertificate(File pemFile) {
InputStream is = null;
try {
is = SVNFileUtil.openFileForReading(pemFile, SVNLogType.WC);
}
catch (SVNException e) {
return null;
}
try {
CertificateFactory factory = CertificateFactory.getInstance("X509");
return (X509Certificate)factory.generateCertificate(is);
}
catch (CertificateException e) {
return null;
}
finally {
SVNFileUtil.closeFile(is);
}
}
}