spring - 基于 SSL 的 Spring LDAP
问题描述
我正在尝试使用多租户实现 spring ldap,其中 ldap 服务器的数量未知。客户端应该将他们的 LDAP 信息和证书插入到表单中,我应该创建一个安全的 TLS 连接。我已经设法设置了没有 SSL 的常规 LDAP,但我在配置 SSL 以使其正常工作时遇到问题。
这是我的测试服务器:
docker run \
--rm \
-p 389:389 \
-p 636:636 \
--hostname local.dev \
--name ldap-local \
-e LDAP_ORGANISATION="Local" \
-e LDAP_DOMAIN=local.dev \
-e LDAP_BASE_DN="dc=local,dc=dev" \
-e LDAP_LOG_LEVEL=-1 \
-e LDAP_READONLY_USER=true \
-e LDAP_READONLY_USER_USERNAME=milos \
-e LDAP_READONLY_USER_PASSWORD=milos1234 \
-v /home/devel/script/ldap/keys:/container/service/slapd/assets/certs \
-e LDAP_TLS_CRT_FILENAME=ldap.crt \
-e LDAP_TLS_KEY_FILENAME=ldap.key \
-e LDAP_TLS_CA_CRT_FILENAME=jatheon.crt \
-e LDAP_TLS_VERIFY_CLIENT=try \
osixia/openldap:1.2.4 --copy-service --loglevel trace
安装的密钥文件夹包含使用以下命令生成的自签名证书:
openssl req -newkey rsa:1024 -x509 -sha256 -days 3650 -nodes -out ldap.crt -keyout ldap.key
我使用 Spring LdapTemplate 来验证连接:
final LdapTemplate template = new LdapTemplate(contextFactory.buildLdapContext(connection));
template.getContextSource().getContext(connection.getUserDN(), connection.getAdminPassword());
buildLdapContext(连接):
public LdapContextSource buildLdapContext(final LdapConnection connection) {
final LdapContextSource context = new LdapContextSource();
context.setBase(connection.getBaseDN());
context.setUrl(connection.getConnectionUrl());
context.setUserDn(connection.getUserDN());
context.setPassword(connection.getAdminPassword());
if(connection.hasCertificate()) {
final DefaultTlsDirContextAuthenticationStrategy authenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy();
authenticationStrategy.setHostnameVerifier((hostname, session) -> hostname.equals(connection.getHost()));
authenticationStrategy.setSslSocketFactory(ldapSslSocketFactoryBuilder.buildSslSocketFactory(connection));
authenticationStrategy.setShutdownTlsGracefully(true);
context.setAuthenticationStrategy(authenticationStrategy);
}
context.afterPropertiesSet();
return context;
}
buildSslSocketFactory:
public SSLSocketFactory buildSslSocketFactory(final LdapConnection connection) {
try {
final KeyStore store = buildKeyStore(connection);
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(store, null);
final SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(kmf.getKeyManagers(), new TrustManager[] {new DummyTrustManager()}, null);
return ctx.getSocketFactory();
} catch(Exception e) {
throw new LdapException(e.getMessage(), e);
}
}
private KeyStore buildKeyStore(final LdapConnection ldapConnection) {
try {
// Load in-memory keystore
final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(null);
// Decode certificate
byte[] decoded = Base64.decodeBase64(ldapConnection.getSslCertificate()
.replaceAll(BEGIN_CERT, "")
.replaceAll(END_CERT, "")
.trim().getBytes(StandardCharsets.UTF_8));
// Load certificate
CertificateFactory certificateFactory = CertificateFactory.getInstance("x.509");
Certificate cert = certificateFactory.generateCertificate(new ByteArrayInputStream(decoded));
keystore.setCertificateEntry(ldapConnection.getConnectionUrl(), cert);
return keystore;
} catch(Exception e) {
log.error(e.getMessage(), e);
throw new LdapException(e.getMessage(), e);
}
}
我使用 DummyTrustManager 来启用自签名证书:
public class DummyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
这可行,但由于某种原因,它适用于任何随机证书,即。我可以放置任何有效的证书,它会起作用。这是 OpenLDAP 日志:
5ea9a99d connection_read(12): unable to get TLS client DN, error=49 id=1000
5ea9a99d conn=1000 fd=12 TLS established tls_ssf=256 ssf=256
如果我更改-e LDAP_TLS_VERIFY_CLIENT=demand请求失败:
TLS: can't accept: No certificate was found..
5ea9aaee connection_read(12): TLS accept failure error=-1 id=1000, closing
解决方案
推荐阅读
- r - 使用 coord_polar 更改 Y 比例
- php - 在 profile.php 中显示登录用户的数据
- python - rpy2 pass : 在函数参数中
- dolphindb - 如何在 DolphinDB 中使用 cutPoints?
- amazon-web-services - 在每次应用时销毁和创建 Ec2 实例
- c++ - 如何在不使用“if”的情况下编写返回值取决于标志的函数?
- r - 使用 lubridate - R 解析日期
- node.js - 如何检查文档是否存在于 MongoDB 中
- python - 如何查找 numpy 数组的索引并在列表中打印相应的行
- c# - 将 CsvReader 转换为 SqlDataReader