Dne 26.11.2010 08:41, Martin Kuba napsal(a):
Spring Security jsem nepoužíval, jenom Spring LDAP, ale podle toho popisu
soudím,
že je třeba si vytvořit vlastní implementaci AbstractContextSource, viz
http://static.springsource.org/spring-ldap/docs/1.3.x/apidocs/index.html?org/springframework/ldap/core/support/AbstractContextSource.html
která se k připojí k LDAP serveru a použije přitom SSL certifikát.
V něm je třeba se správně autentizovat, popis je na
http://download.oracle.com/javase/jndi/tutorial/ldap/security/ssl.html
v části "Using Custom Sockets". Je třeba si vytvořit SslSocketFactory, která
dokáže předložit klientský SSL certifikát. To se dělá zhruba takto:
Tak jsem si to zkusil, a je to tak. V konfiguraci Springu se musí
org.springframework.ldap.core.support.LdapContextSource
nahradit za vlastní implementaci, která musí vypadat nějak takto:
package cz.makub;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.core.support.AbstractContextSource;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.InitialLdapContext;
import javax.net.SocketFactory;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.util.Hashtable;
/**
* Alternative to LdapContextSource allowing client SSL certificates.
*
* @author Martin Kuba [email protected]
* @version $Id:$
* @see org.springframework.ldap.core.support.LdapContextSource
*/
public class MyContextSource extends AbstractContextSource {
final static Logger log = LoggerFactory.getLogger(MyContextSource.class);
private String keyStoreFile;
private String keyStorePassword;
public void setKeyStoreFile(String keyStoreFile) {
this.keyStoreFile = keyStoreFile;
}
public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
@SuppressWarnings({"unchecked"})
@Override
protected DirContext getDirContextInstance(Hashtable env) throws
NamingException {
String url = (String) env.get(Context.PROVIDER_URL);
try {
if (new URI(url).getScheme().equalsIgnoreCase("ldaps")) {
env.put("java.naming.ldap.factory.socket",
"cz.makub.MyContextSource$MySSLFactory");
MySSLFactory.setManagers(getKeyManagers(), null);
}
} catch (URISyntaxException e) {
log.error("LDAP URL " + url + " is wrong", e);
}
return new InitialLdapContext(env, null);
}
private KeyManager[] getKeyManagers() {
return getKeyManagers(loadKeyStoreFromFile(keyStoreFile,
keyStorePassword), keyStorePassword);
}
private static KeyManager[] getKeyManagers(KeyStore keyStore, String
storePassword) {
try {
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, storePassword != null ?
storePassword.toCharArray() : null);
return keyManagerFactory.getKeyManagers();
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
private static KeyStore loadKeyStoreFromFile(String ksfile, String
password) {
String kstype;
if (ksfile.endsWith(".jks")) {
kstype = "JKS";
} else if (ksfile.endsWith(".p12")) {
kstype = "PKCS12";
} else {
throw new RuntimeException("keystore file name " + ksfile + " must end
with .ks (JKS) or .p12 (PKCS12)");
}
try {
KeyStore store = KeyStore.getInstance(kstype);
store.load(new FileInputStream(ksfile), password != null ?
password.toCharArray() : null);
return store;
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
/**
* SSL SocketFactory enabling client certificates and customs server checks.
*/
@SuppressWarnings({"UnusedDeclaration"})
public static class MySSLFactory extends SocketFactory {
final static MySSLFactory thisFactory = new MySSLFactory();
static SSLSocketFactory factory = (SSLSocketFactory)
SSLSocketFactory.getDefault();
public static void setManagers(KeyManager[] keyManagers, TrustManager[]
trustManagers) {
try {
SSLContext sctx = SSLContext.getInstance("TLS");
sctx.init(keyManagers, trustManagers, null);
factory = sctx.getSocketFactory();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static SocketFactory getDefault() {
return thisFactory;
}
@Override
public Socket createSocket() throws IOException {
return factory.createSocket();
}
public Socket createSocket(String s, int i) throws IOException {
return factory.createSocket(s, i);
}
public Socket createSocket(String s, int i, InetAddress inetAddress,
int i1) throws IOException {
return factory.createSocket(s, i, inetAddress, i1);
}
public Socket createSocket(InetAddress inetAddress, int i) throws
IOException {
return factory.createSocket(inetAddress, i);
}
public Socket createSocket(InetAddress inetAddress, int i, InetAddress
inetAddress1, int i1) throws IOException {
return factory.createSocket(inetAddress, i, inetAddress1, i1);
}
}
}
Klientský certifikát pak musí být ve formátu JKS nebo PKCS12 v souboru
odkazovaném property keyStoreFile a v property keyStorePassword
musí být heslo k soouboru i ke klíči, to u PKCS12 bývá totožné.
Tož tak.
Makub
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Supercomputing Center Brno Martin Kuba
Institute of Computer Science email: [email protected]
Masaryk University http://www.ics.muni.cz/~makub/
Botanicka 68a, 60200 Brno, CZ mobil: +420-603-533775
--------------------------------------------------------------