我的客户端代码已使用LDAP协议针对嵌入式ApacheDS 1.5.5成功测试。在使用Apache DS 2.0.0-M23的端到端测试中,我成功地使用OpenLDAP建立了LDAPS连接。但是,我在使用基于Java的spring-ldap建立LDAPS连接时遇到了问题。我的代码基于spring-ldap。任何深入挖掘TLS或LDAPS的技巧都会受到赞赏。
版本
Oracle JDK:带有JCE Unlimited Strength Jurisdiction Policy Files的Java版“1.8.0_92”。Spring-Ldap版本:2.3.1.RELEASE
Spring Version:4.3.2.RELEASE
Apache DS版本:2.0.0-M23部署在Docker容器中。
创建公共证书和私钥
导出LDAP服务器上使用的证书:
[jlee@iel-jlee-lt1 security]$ keytool -genkey -keyalg "RSA" -dname "cn=tangotelecom.com, ou=dev, o=tangotelecom, c=IE" -alias tangotelecom -keystore tangotelecom.ks -storepass secret -validity 730
Enter key password for <tangotelecom>
(RETURN if same as keystore password):
[jlee@iel-jlee-lt1 security]$ keytool -list -keystore tangotelecom.ks
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
tangotelecom, 06-Jul-2017, PrivateKeyEntry,
Certificate fingerprint (SHA1): 56:0C:5C:7A:DD:97:F1:4D:A2:B6:D1:2B:EC:D0:11:4A:B1:79:F3:CF
[jlee@iel-jlee-lt1 security]$ keytool -export -keystore tangotelecom.ks -alias tangotelecom -file tangotelecom.cer
Enter keystore password:
Certificate stored in file <tangotelecom.cer>
将公共证书导入cacerts文件以供客户使用:
[tango@iel-dev-tfsr-vm2 16:30:57 john]$ sudo keytool -import -trustcacerts -keystore /opt/java8/jre/lib/security/cacerts -storepass changeit -noprompt -alias tangotelecom -file /home/tango/john/tangotelecom.cer
[sudo] password for tango:
Certificate was added to keystore
TLS DEBUG
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1500415942 bytes = { 188, 130, 166, 249, 170, 224, 174, 199, 55, 88, 195, 144, 45, 147, 143, 225, 45, 223, 30, 91, 195, 43, 83, 241, 197, 150, 41, 54 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Extension server_name, server_name: [type=host_name (0), value=tangotelecom.com]
***
Thread-8, WRITE: TLSv1.2 Handshake, length = 260
Thread-8, READ: TLSv1.2 Handshake, length = 1257
*** ServerHello, TLSv1.2
RandomCookie: GMT: 1500416088 bytes = { 2, 142, 226, 16, 107, 183, 43, 206, 220, 107, 239, 113, 204, 172, 241, 154, 87, 185, 49, 94, 163, 9, 187, 229, 53, 41, 78, 254 }
Session ID: {89, 111, 136, 88, 183, 123, 76, 181, 211, 21, 149, 160, 218, 222, 226, 244, 139, 145, 143, 72, 3, 7, 184, 96, 34, 155, 76, 54, 60, 117, 215, 179}
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
** TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN=tangotelecom.com, OU=dev, O=tangotelecom, C=IE
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
Key: Sun RSA public key, 2048 bits
modulus: 22980180191390060691645191877521292031505114546367033225089259442617005098274762521554359946742065878415849288262185146512594592923140681158742168163906077539516435863146152603064701157961527256158756559349035940810739650464273192076580461817700601005238106947505955687153311906740088488389957201959449147054853786865353874624664733186248186940444317144962411956375681259742751499094173934719877768378414836567412356975874572170519318320962628133692056724599530776533547533308568698574604059550372476889963449850184432248569481738632894450321061794712987632959422197533269207285625710288277728878653909213375498707927
public exponent: 65537
Validity: [From: Thu Jul 06 15:48:29 IST 2017,
To: Sat Jul 06 15:48:29 IST 2019]
Issuer: CN=tangotelecom.com, OU=dev, O=tangotelecom, C=IE
SerialNumber: [ 6dce9c09]
Certificate Extensions: 1
[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: E1 BA 73 DD F2 41 0F CF BD 4B 9B 99 D7 01 BD 2F ..s..A...K...../
0010: 7D 9A 31 B7 ..1.
]
]
]
Algorithm: [SHA256withRSA]
Signature:
0000: 2C DB 4A FA BD A9 1D 29 C0 0A 8F 69 7F 39 11 DA ,.J....)...i.9..
0010: 4A 21 0D 66 53 A5 03 CC 33 AB 4D E6 C1 55 75 E3 J!.fS...3.M..Uu.
0020: 8C 22 C6 F9 05 26 27 EF BC C0 CB D8 12 3C 7C 95 ."...&'......<..
0030: 9B 56 88 06 71 CA 3A 17 99 6A 23 53 E9 CC 9B 05 .V..q.:..j#S....
0040: E6 85 89 25 DC CF 95 3A 41 62 5D 0C CE E9 0F D4 ...%...:Ab].....
0050: 7E 0E B6 4D CE 5A 49 F4 BB DD 9A 85 55 C1 39 0E ...M.ZI.....U.9.
0060: 0F B4 34 44 05 64 E5 F3 F0 4A 5C 40 91 2C 1E B3 ..4D.d...J\@.,..
0070: 39 D1 56 71 C2 83 48 CA 31 E5 44 60 B4 65 8D BA 9.Vq..H.1.D`.e..
0080: 70 65 7C 93 B8 84 E4 60 38 EA C7 21 7D AF 30 BD pe.....`8..!..0.
0090: 6F D5 CC 04 DA 33 E8 14 DA E8 AC BA 6A 03 5E C9 o....3......j.^.
00A0: 56 DC 78 07 FB 44 DB A8 B2 4D CE 30 CB 56 7E 6B V.x..D...M.0.V.k
00B0: F6 CE BF 3D B4 77 21 33 53 BA D8 E2 B1 B9 F4 95 ...=.w!3S.......
00C0: 05 7E FB B4 3A CF 09 29 10 B1 4E CF 38 AF 81 D6 ....:..)..N.8...
00D0: 6B DD 60 51 1C 04 27 41 71 98 07 DE B9 82 4A 1A k.`Q..'Aq.....J.
00E0: 0D F7 04 6D 09 F4 E7 1F FE 2C 07 41 EE 84 F3 F6 ...m.....,.A....
00F0: 1A F4 62 87 3D 94 A6 5D FD 3D 8B EE 1B A1 5B 52 ..b.=..].=....[R
]
***
Found trusted certificate:
[
[
Version: V3
Subject: CN=tangotelecom.com, OU=dev, O=tangotelecom, C=IE
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
Key: Sun RSA public key, 2048 bits
modulus: 22980180191390060691645191877521292031505114546367033225089259442617005098274762521554359946742065878415849288262185146512594592923140681158742168163906077539516435863146152603064701157961527256158756559349035940810739650464273192076580461817700601005238106947505955687153311906740088488389957201959449147054853786865353874624664733186248186940444317144962411956375681259742751499094173934719877768378414836567412356975874572170519318320962628133692056724599530776533547533308568698574604059550372476889963449850184432248569481738632894450321061794712987632959422197533269207285625710288277728878653909213375498707927
public exponent: 65537
Validity: [From: Thu Jul 06 15:48:29 IST 2017,
To: Sat Jul 06 15:48:29 IST 2019]
Issuer: CN=tangotelecom.com, OU=dev, O=tangotelecom, C=IE
SerialNumber: [ 6dce9c09]
Certificate Extensions: 1
[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: E1 BA 73 DD F2 41 0F CF BD 4B 9B 99 D7 01 BD 2F ..s..A...K...../
0010: 7D 9A 31 B7 ..1.
]
]
]
Algorithm: [SHA256withRSA]
Signature:
0000: 2C DB 4A FA BD A9 1D 29 C0 0A 8F 69 7F 39 11 DA ,.J....)...i.9..
0010: 4A 21 0D 66 53 A5 03 CC 33 AB 4D E6 C1 55 75 E3 J!.fS...3.M..Uu.
0020: 8C 22 C6 F9 05 26 27 EF BC C0 CB D8 12 3C 7C 95 ."...&'......<..
0030: 9B 56 88 06 71 CA 3A 17 99 6A 23 53 E9 CC 9B 05 .V..q.:..j#S....
0040: E6 85 89 25 DC CF 95 3A 41 62 5D 0C CE E9 0F D4 ...%...:Ab].....
0050: 7E 0E B6 4D CE 5A 49 F4 BB DD 9A 85 55 C1 39 0E ...M.ZI.....U.9.
0060: 0F B4 34 44 05 64 E5 F3 F0 4A 5C 40 91 2C 1E B3 ..4D.d...J\@.,..
0070: 39 D1 56 71 C2 83 48 CA 31 E5 44 60 B4 65 8D BA 9.Vq..H.1.D`.e..
0080: 70 65 7C 93 B8 84 E4 60 38 EA C7 21 7D AF 30 BD pe.....`8..!..0.
0090: 6F D5 CC 04 DA 33 E8 14 DA E8 AC BA 6A 03 5E C9 o....3......j.^.
00A0: 56 DC 78 07 FB 44 DB A8 B2 4D CE 30 CB 56 7E 6B V.x..D...M.0.V.k
00B0: F6 CE BF 3D B4 77 21 33 53 BA D8 E2 B1 B9 F4 95 ...=.w!3S.......
00C0: 05 7E FB B4 3A CF 09 29 10 B1 4E CF 38 AF 81 D6 ....:..)..N.8...
00D0: 6B DD 60 51 1C 04 27 41 71 98 07 DE B9 82 4A 1A k.`Q..'Aq.....J.
00E0: 0D F7 04 6D 09 F4 E7 1F FE 2C 07 41 EE 84 F3 F6 ...m.....,.A....
00F0: 1A F4 62 87 3D 94 A6 5D FD 3D 8B EE 1B A1 5B 52 ..b.=..].=....[R
]
*** ECDH ServerKeyExchange
Signature Algorithm SHA512withRSA
Server key: Sun EC public key, 256 bits
public x coord: 65335040139868512120179408985992060136049248034231183953393366519969264579494
public y coord: 99476183036375747385598841109418769282434814800382623908755157948061455956677
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
*** ServerHelloDone
*** ECDHClientKeyExchange
ECDH Public value: { 4, 6, 89, 131, 180, 42, 189, 111, 119, 56, 149, 20, 50, 182, 23, 83, 192, 173, 201, 60, 5, 132, 156, 221, 3, 44, 131, 63, 1, 166, 164, 34, 3, 56, 203, 122, 234, 174, 144, 123, 135, 210, 236, 219, 230, 153, 161, 126, 189, 78, 72, 8, 98, 201, 57, 28, 243, 18, 56, 113, 156, 118, 238, 160, 238 }
Thread-8, WRITE: TLSv1.2 Handshake, length = 70
SESSION KEYGEN:
PreMaster Secret:
0000: BD 2B CF E4 3E 6A 0D 32 97 16 67 3E 5A 46 CD CD .+..>j.2..g>ZF..
0010: CA 52 5B 89 28 43 45 35 09 89 74 1F B6 D9 02 7B .R[.(CE5..t.....
CONNECTION KEYGEN:
Client Nonce:
0000: 59 6F 88 C6 BC 82 A6 F9 AA E0 AE C7 37 58 C3 90 Yo..........7X..
0010: 2D 93 8F E1 2D DF 1E 5B C3 2B 53 F1 C5 96 29 36 -...-..[.+S...)6
Server Nonce:
0000: 59 6F 88 58 02 8E E2 10 6B B7 2B CE DC 6B EF 71 Yo.X....k.+..k.q
0010: CC AC F1 9A 57 B9 31 5E A3 09 BB E5 35 29 4E FE ....W.1^....5)N.
Master Secret:
0000: 44 0B B5 4E 2C 69 E1 68 85 F3 21 08 1A 75 F8 01 D..N,i.h..!..u..
0010: 33 1A 6B 3A 55 7B D3 65 F8 D6 3C BD 10 40 B2 E4 3.k:U..e..<..@..
0020: A2 BC 8A 31 1F 41 9E C6 FC A4 40 F7 2F B2 59 3F ...1.A....@./.Y?
Client MAC write Secret:
0000: C0 97 F8 99 19 72 9D 18 63 FD CD 8C 64 23 BC B1 .....r..c...d#..
0010: 7B 58 D4 52 9C 71 E6 42 0A 20 ED 53 31 5F 27 A7 .X.R.q.B. .S1_'.
0020: 47 3E EA 1D AA 12 CA AA 14 1B 63 98 E0 1B AD F8 G>........c.....
Server MAC write Secret:
0000: 98 BE 74 96 5D 1B 69 7D 96 0D 83 25 C3 D6 24 B0 ..t.].i....%..$.
0010: 57 15 08 62 A6 69 E3 DA 77 3D 73 A3 E6 D9 A8 7A W..b.i..w=s....z
0020: 46 E0 D2 49 ED 53 86 45 18 97 92 C3 C9 61 45 4E F..I.S.E.....aEN
Client write key:
0000: 27 67 0E 91 81 3A 6A 5B 87 78 1D E0 EB 6B C7 AE 'g...:j[.x...k..
0010: B8 78 13 9F 6F C2 8B 4E AC 3F 01 07 BC 5C 9B 0E .x..o..N.?...\..
Server write key:
0000: 3E D2 EE 99 D4 53 84 15 C6 D6 66 5D A8 A5 40 7C >....S....f]..@.
0010: 65 BB 8E A8 DF F7 B8 22 6C EF 6C 82 26 63 7D 99 e......"l.l.&c..
... no IV derived for this protocol
Thread-8, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data: { 101, 250, 185, 66, 42, 146, 49, 129, 108, 63, 34, 156 }
***
Thread-8, WRITE: TLSv1.2 Handshake, length = 96
Thread-8, READ: TLSv1.2 Change Cipher Spec, length = 1
Thread-8, READ: TLSv1.2 Handshake, length = 96
*** Finished
verify_data: { 221, 251, 110, 51, 35, 95, 63, 112, 169, 189, 61, 120 }
***
%% Cached client session: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
ForkJoinPool.commonPool-worker-2, WRITE: TLSv1.2 Application Data, length = 96
Thread-8, handling exception: javax.net.ssl.SSLException: Unsupported record version Unknown-38.2
%% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
Thread-8, SEND TLSv1.2 ALERT: fatal, description = unexpected_message
Thread-8, WRITE: TLSv1.2 Alert, length = 80
Thread-8, called closeSocket()
Thread-8, called close()
Thread-8, called closeInternal(true)
Spring LDAP客户端配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
<context:property-placeholder location="classpath:application.properties"/>
<bean id="ldapAuthenticationStrategy" class="org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy">
<property name="shutdownTlsGracefully" value="true"/>
</bean>
<ldap:context-source url="${ldap.primary.url}, ${ldap.secondary.url}"
username="${ldap.userDn}"
password="${ldap.password}"
base="${ldap.base}"
referral="follow"
native-pooling="false"
authentication-strategy-ref="ldapAuthenticationStrategy">
</ldap:context-source>
<ldap:ldap-template id="ldapTemplate" context-source-ref="contextSource" time-limit="5000"/>
</beans>
代码
public class TangoLdapClient {
static final String UID = "uid";
private final LdapTemplate ldapTemplate;
/**
* Constructor.
* @param ldapTemplate {@link LdapTemplate}.
*/
public TangoLdapClient(@NonNull final LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
/**
* Authenticate with username and password.
* @param username username.
* @param password password.
* @throws EmptyResultDataAccessException in the case that the username is not found, and therefore cannot be mapped to dn.
* @throws AuthenticationException in the case where the password doesn't match.
*/
public void authenticate(@NonNull final String username, @NonNull final String password)
throws EmptyResultDataAccessException, AuthenticationException {
Preconditions.checkArgument(StringUtils.isNotBlank(username));
ldapTemplate.authenticate(buildFindUserFromUserName(username), password,
(ctx, ldapEntryIdentification) -> wrappedMapper(ctx, ldapEntryIdentification));
}
private LdapQuery buildFindUserFromUserName(final String username) {
return query().where(UID).is(username);
}
private static DirContextOperations wrappedMapper(final DirContext ctx,
final LdapEntryIdentification ldapEntryIdentification) {
try {
return (DirContextOperations) ctx.lookup(ldapEntryIdentification.getRelativeName());
} catch (NamingException e) {
throw new RuntimeException("Failed to lookup " + ldapEntryIdentification.getRelativeName(), e);
}
}
}