This started off as part of a small task, when I had half an hour gap before lunch. The whole end-to-end of getting TLS and LDAP, with certificate authentication took me several weeks to set up. Now I know the traps, it takes about 10 minutes.
I describe setting up TLS and LDAP (without certificate authentication) here. Get that working before trying certificate authentication.
Setting up the simplest case of an RSA certificate on the client and an RSA certificate on the server, was pretty easy to set up. Using an Elliptic Curve certificate to and RSA certificate on the server seems impossible, it eventually worked!
I created “What cipher specs should I use?” because most of my problems, were due to using the wrong cipher specs, or the right cipher specs, but it the wrong order!
Logging on
You can logon to LDAP and specify a userid (DN) and password, for example
ldapsearch -h 127.0.0.1 -D “cn=Admin, o=Your Company” -w secret -b “o=Your Company” “(objectclass=*)” aclEntry
If you use -w ? it will prompt for your password, so it is not visible.
You can also use a certificate to logon, so you do not need the password, you just need the private key (or in my case the USB dongle with my encrypted Hardware Security Module(HSM) keystore on it).
Note: If you have your TLS private key in a file, and people can copy that file, they can impersonate you! You need to protect the file, bearing in mind your corporate IT department may be able to view any backups etc that you have. Someone would need to steal my USB dongle to use my private key and logon.
Understanding the TLS 1.2 handshake and authentication.
Skip to first steps if you are keen to implement without understanding the background,
There are several stages to establishing a TLS connection and authentication.
- Agree the protocols for setting up the session, for example which sort of encryption, and the key size. This provides the privacy on the connection.
- The server sends down its certificate, and the client authenticates it
- The client sends up its certificate and the server authenticates it
- The Distinguished Name(DN)from the client certificate is looked up in the z/OS security manager, and the associated userid is looked up.
- The DN is used to look in the access control lists (ACLs) do determine the access the requester has to the data.
Agree the protocols
The client the sends the protocols it supports to the server. This is a list of numbers, and each number has a meaning.
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 (0xc087)
…
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f)
…
The part before the WITH covers
There must be at least one key type matching the certificate type.
- If you have an Elliptic Curve server certificate, then you need to have records with TLS…ECDSA…
- If you have an RSA server certificate you need to have records with TLS…RSA…
At the server, the list sent from the client is merged with the list from the server, for example if the server had
GSK_V3_CIPHER_SPECS_EXPANDED=006BC006C007C008c024c023c0250039
and the client sent C024, C025, C02C, then common elements are C024, C025. These two cipher specs are TLS_ECDHE_ECDSA_WITH… so the server’s certificate needs to be ECDSA – or an Elliptic Certificate.
If no entries match you get a message like “no cipher found”.
Authenticate the server
The server sends information down to the client.
- The cipher suite to be used.
- Information for setting up the encryption of the traffic for the session.
- The server’s certificate, and any CA certificates in the chain.
- The client then validates the certificate
- It use the signature algorithm in the certificate. This is the information after the “WITH” in the cipher spec. In my client’s certificate it has signing algorithm sha256WithRSAEncryption. There must be a cipher spec in the list ending in TLS…RSA_WITH… ending in SHA256
- It checks the signature of the CA, by checking its signing algorithm in the GSK_TLS_SIG_ALG_PAIRS parameter. I found it easiest to specify most of the available options 0601060305010503040104030402″. (0601, 0603…) See here.
Send the client certificate to the server
If the client certificate is wanted, the servers sends the “certificate request”.
- It sends down the certificate type it accepts, for example RSA, ECDSA, DSS. The client then sends its certificate of that type. If the there is no match, no certificate is sent. Note:For a long time I could not get a client certificate with Elliptic Curve, to work when the server had an RSA certificate. This was due to bad parameters on the z/OS end.
- There is a list of signature Hash Algorithms (eg rsa_pkcs1_sha256 (0x0401) ecdsa_secp256r1_sha256 ( 0x0403).
- It lists the Distinguished Names of the Certificate Authorities or Self Signed certificate.
- With the certificate type,the signature Hash and the Certificate Authorities, the client looks in its keystore for a certificate matching these parameters; then sends the first certificate that matches – or sends “no certificate”.
- It sends the Certificate Verify Signature Algorithm to the server. For my Elliptic Curve this was ecdsa_secp256r1_sha256 (0x0403). This has to be in the GSK_TLS_SIG_ALG_PAIRS in the z/OS LDAP environment file. (It took me days to find this problem!)
The server authenticates the client’s certificate
The server looks inside the certificate to see how the certificate was signed, for example using
openssl x509 -in ecec.pem -text -noout
This gave Signature Algorithm: ecdsa-with-SHA256. The server needs the matching cipher spec in the GSK_V3_CIPHER_SPECS_EXPANDED environment variable.
The client’s certificate will have been signed – either by a CA, or self signed. The Signature algorithm of the CA must be in the GSK_TLS_SIG_ALG_PAIRS environment variable. The values are defined here.
For example my CA had Signature Algorithm: ecdsa-with-SHA384. This requires 0503 (SHA-384 with ECDSA) to be in the GSK_TLS_SIG_ALG_PAIRS list. If this was missing I got
ldap_sasl_interactive_bind_s: Can’t contact LDAP server (-1)
additional info: A TLS fatal alert has been received.
from ldapsearch on Ubuntu. It took me another day or so to find this problem.
As you can see there are a lot of things you need to configure and to get right before it works!
First steps – using a client certificate
Firstly set up the TLS session so you can use certificates to connect to LDAP. This took me about a week, because of configuration problems, but finally it worked. I was able to connect from Ubuntu to z/OS.
Set up TLS between the client and the z/OS server, as described here.
Setup Ubuntu
I created an RSA certificate on Ubuntu using a shell script.
name=”rsaca256″
ca=”carsa1024″openssl genpkey -out $name.key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048
openssl req -config eccert.config -passin password -sha384 -new -key $name.key.pem -out $name.csr -outform PEM -subj “/C=GB/O=cpwebuser/CN=”$name -passin file:password.file -passout file:password.file
openssl ca -config openssl-ca-user.cnf -policy signing_policy $ext -md sha256 -cert $ca.pem -keyfile $ca.key.pem -out $name.pem -in $name.csr $enddate -extensions clientServer
I set up an Elliptic Curve
enddate=”-enddate 20220409174001Z”
password=” -passin file:password.file -passout file:password.file”
name=”ecec”
rm $name.key.pem
rm $name.csr
rm $name.pem
ca=”ca256″ # sign with thisopenssl ecparam -name secp256r1 -genkey -noout -out $name.key.pem
openssl req -config eccert.config -passin password -sha384 -new -key $name.key.pem -out $name.csr -outform PEM -subj “/C=GB/O=cpwebuser/CN=”$name $password
openssl ca -config openssl-ca-user.cnf -policy signing_policy $ext -md sha256 -cert $ca.pem -keyfile $ca.key.pem -out $name.pem -in $name.csr $enddate -extensions clientServeropenssl x509 -in $name.pem -text -noout|less
# Some tools need a .p12 file of the certificate
#openssl pkcs12 -export -inkey $name.key.pem -in $name.pem -out $name.p12 -CAfile $ca.pem -chain -name $name -passout file:password.file -passin file:password.file
Openldap uses a configuration file, for example ldaprc.
I set up my ldaprc configuration file on Ubuntu.
TLS_CACERT /home/colinpaice/ssl/ssl2/colinca.pem
URI ldaps://10.1.1.2:1389
TLS_REQCERT demand
TLS_KEY /home/colinpaice/ssl/ssl2/secp521r.key.pem
TLS_CERT /home/colinpaice/ssl/ssl2/secp521r.pem
Where
- TLS_CACERT /home/colinpaice/ssl/ssl2/colinca.pem was the z/OS CA certificate exported from z/OS and downloaded. It is needed to verify a certificate sent from z/OS.
- TLS_KEY /home/colinpaice/ssl/ssl2/secp521r.key.pem this file contains the private key
- TLS_CERT /home/colinpaice/ssl/ssl2/secp521r.pem this file contains the public key
- You can specify TLS_CIPHER_SUITE high to use the listed cipher specs. See the openssl ciphers command, for example
- openssl ciphers -v -V -s -tls1_2
- openssl ciphers -v -V -s -tls1_3
- I omitted TLS_CIPHER_SUITE for my testing.
You need the distinguished name (DN) of the certificate. You can display the subject distinguished name DN using the command openssl x509 -in secp521r.pem -text -noout. This gave me output which included:
Subject: C = GB, O = cpwebuser, CN = secp521r
I sent the Linux CA file, ca256.pem, to z/OS.
Set up z/OS
Configure LDAP.
In my LDAP configuration file I had
sslMapCertificate check fail
sslAuth serverClientAuth
listen ldap://:389
listen ldaps://:1389
sslKeyRingFile START1/MQRING
sslCertificate ZZZZ
commThreads 10
allowAnonymousBinds off
sslCipherSpecs GSK_V3_CIPHER_SPECS_EXPANDED
The sslCipherSpecs GSK_V3_CIPHER_SPECS_EXPANDED says look in the environment file, and take the value from there. As this is the default, you can omit it from the file.
The default GSK_V3_CIPHER_SPECS_EXPANDED value in the environment file is 003500380039002F00320033 which maps to
- 0035 TLS_RSA_WITH_AES_256_CBC_SHA
- 0038 TLS_DHE_DSS_WITH_AES_256_CBC_SHA
- 0039 TLS_DHE_RSA_WITH_AES_256_CBC_SHA
- 002f TLS_RSA_WITH_AES_128_CBC_SHA
- 0032 TLS_DHE_DSS_WITH_AES_128_CBC_SHA
- 0033 TLS_DHE_RSA_WITH_AES_128_CBC_SHA
Which are all very old (part of deprecated SSL, TLS 1.0, TLS 1.1) so you need to set this variable in your environment file. See the next section for what I used.
Update your environment file
I use the following value
GSK_V3_CIPHER_SPECS_EXPANDED=C02CC02BC030C02FC024C023130313011302
This supports
- C02C TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- C02B TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- C030 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- C02F TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- C024 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
- C023 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
- 1303 TLS_CHACHA20_POLY1305_SHA256 TLS 1.3
- 1301 TLS_AES_128_GCM_SHA256 TLS 1.3
- 1302 TLS_AES_256_GCM_SHA384 TLS 1.3
Put the strongest certificates first. For example, the following did not work.
GSK_V3_CIPHER_SPECS_EXPANDED=0035….C02F
It caused 0035 to be used. The Certificate-Types list had only RSA and DSS, and did not include ECDSA, so the client was not able to send its Elliptic Curve certificate.
When I used
GSK_V3_CIPHER_SPECS_EXPANDED=C02F….0035
it caused C02F to be used. This caused the certificate-types list to include the ECDSA, and so the client could send its Elliptic Curve certificate. This problem took me a week to find!
I specified
GSK_TLS_SIG_ALG_PAIRS=0601050104010301080608050804
For the certificate checking algorithms. These are for SHA = 224, 256, 384, 512 and certificate types = RSA, ECDSA, and DSA.
Add the CA from Linux and add it to the keyring
The Linux CA was uploaded to z/OS as COLIN.CARSA102.PEM.
Add it to the RACF keystore as a CERTAUTH certificate, and connect it to the keyring.
RACDCERT CHECKCERT('COLIN.CARSA102.PEM') RACDCERT DELETE (LABEL('LINUXCA')) CERTAUTH RACDCERT ADD('COLIN.CARSA102.PEM') - CERTAUTH WITHLABEL('LINUXCA') TRUST RACDCERT ID(START1) CONNECT(RING(MQRING)- CERTAUTH LABEL('LINUXCA')) SETROPTS RACLIST(DIGTCERT) REFRESH
Map the Distinguished Name to a z/OS userid.
You have to map the DN to a userid. In the example below, it maps the DN in the SDNFILTER to the userid ADCDA. This userid must be valid, for example if the userid is revoked, the authentication will fail (with an unhelpful message).
* remove any previous instance RACDCERT DELMAP(LABEL('LINUXSecp2' )) ID(ADCDA) SETROPTS RACLIST(DIGTNMAP, DIGTCRIT) REFRESH RACDCERT MAP ID(ADCDA ) - SDNFILTER('CN=secp521r.O=cpwebuser.C=GB') - WITHLABEL('LINUXSecp2') RACDCERT LISTMAP ID(ADCDA) SETROPTS RACLIST(DIGTNMAP, DIGTCRIT) REFRESH
Note the ‘.’ between the attributes, and the attributes are squashed up, in the right order.
I did not need to refresh LDAP as the change was automatically picked up.
I could now logon to LDAP using the certificate – but could do nothing.
Give the DN access to LDAP
I gave it access, using ldapmodify passing the file with
dn: o=Your Company changetype: modify add: aclEntry aclEntry : access-id:CN=SECP521R,O=CPWEBUSER,C=GB: object:ad:normal:grant:rscw:sensitive:rsc
Note: The DN can be in mixed case, or upper case.
I could now logon and issue queries using a certificate for authentication and authorisation.
Tracing problems
You can get trace output to the job log using the command
f GLDSRV,debug error+CONNS+LDAPBE
This traces
- any errors
- CONNS shows the cipher spec being used
- LDAPBE shows the DN, and SAF user etc being used.
You can turn trace off with
f GLDSRV,debug 0
You can get the low level trace by using the gsktrace=0xff in your environment file.
One thought on “Setting up certificate authentication in LDAP.”