For countries that have adopted an Electronic Identification Card (eID), implementing web application authentication based on the eID card is an attractive solution, because the issuer is trustworthy (that’s the least we can say) and the proof of identity has a legal value.
This blog describes how to configure OpenSSO (or the Sun supported Access Manager) to provide strong authentication based on the eID certificate.
I made my tests with the Belgium eID card but I suspect other cards to behave similarly.
On the client side, you will need, well an eID, a card reader and the required drivers (aka middleware) usually provided by the card manufacturer or the card issuer.
Once this is installed, you will need to add the Card as a Security Device (Certificate Store) into the list of security devices recognized by your browser. In the case of the Belgium eID card, Fedict does provide a tutorial for the main browsers, mail readers and office suites.
On the server side, all the softwares I used are all available as open source project or, alternatively, if you’re looking for indemnification and 24/7 local support, as a commercial offering from Sun.
- Glassfish v2 (or Sun Java System Application Server 9.1)
- OpenSSO v1 build3 (or Sun Java System Access Manager 7.1)
- OpenDS v1 (or Sun Java System Directory Server)
Just install Glassfish, OpenSSO using embedded or external OpenDS (I choose Embedded for simplicity). Instructin to download and install glassfish are posted here. Instructions to download and install OpenSSO are posted here.
There are two main steps to configure the certificate authentication with OpenSSO : prepare the LDAP backend and configure the Authentication Module.
Prepare the LDAP
The first step requires you to create an entry for the user in the LDAP server. Amongst the usual firstname, lastname attributes, this entry will contain the user certificate (it’s public key, signed by the Government’s key, aka Certificate Authority) and the card serial number, that we will use to uniquely match the card’s certificate with a user’s LDAP entry.
1. Extract your certificate and the root certificates from your card
On Linux and Solaris, use can use the pkcs15-tool provided by OpenSC.
On Mac OSX, you start the KeyChain Access application (located under Applications -> Utilities), right click on the certificate name, then select "Export <Certificate Name>" as shown below.
2. Verify your certificate
You can verify the content of the certificate just extracte with the OpenSSL command (adjust file name) :
openssl x509 -inform DER -in ./Desktop/sst.cer -text
3. Prepare the LDAP entry
You have to create an LDAP entry for the user represented by your eID card. I used the following LDIF file and the ldapadd command to create the LDAP entry. Alternatively, you can use any LDAP GUI such as LDABrowser.
dn: uid=sst,ou=People,dc=sun,dc=com
objectClass: person
objectClass: inetorgperson
objectClass: top
objectClass: organizationalperson
sn: Stormacq
userPassword:: e1NTSEF9cnpjNHFEcm9HbTVJV0IvZHJuQW5iVnNkd0crWW9YS0NrWUsxRVE9PQ==
cn: Sebastien Stormacq
uid: sst
givenName: Sebastien
(my password is password)
[UPDATE]
To Simplify the process of creating new entries, you can also use the following ldapmodify command :
-bash-3.2$ ldapmodify -b -a -D "cn=Directory manager" -w password -p 1389
dn: uid=sst,ou=People,dc=sun,dc=com
objectClass: person
objectClass: inetorgperson
objectClass: top
objectClass: organizationalperson
sn: Stormacq
userPassword: password
cn: Sebastien Stormacq
uid: sst
givenName: Sebastien
serialNumber: 72020412345
userCertificate: /export/home/sst/sst.cer
adding new entry uid=sst,ou=People,dc=opensso,dc=java,dc=net
The -b option of ldapmodify ensures the required magic to read the certificate file and base64 encode it
[/UPDATE]
Test you LDAP entry by issuing the follwoing command (change hostname, port number, password and uid accordingly):
spirou:OpenDS sst$ ldapsearch -h localhost -p 1389 -D "cn=Directory Manager" -w adminadmin -x uid=sst dn
# extended LDIF
#
# LDAPv3
# base <> with scope subtree
# filter: uid=sst
# requesting: dn
#
# sst, People, sun.com
dn: uid=sst,ou=People,dc=sun,dc=com
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
4. Import the Certificate into the LDAP
The default LDAP schema contains an attribute to store the user’s certificate : userCertificate. This is a binary attribute. To add this attribute, you have to convert your certificate in BASE64 format and issue an ldapmodify command.
I was lazy and used the LDAPBrowser’s GUI instead. This GUI allows you to load a binary attribute from a file content : right click on the entry, select Add Attribute, type userCertificate as attribute name, select Binary as type, then load the content of your certificate file.
5. Import the SerialNumber in the LDAP
The eID Serial Number will be used as a unique attribute to match the card’s certificate and the LDAP entry. It will also be used to retrieve’s the user’s profile from the LDAP. We therefore need to add a serialNumber attribute into the LDAP. Unfortunately, such an attribute is not defined in the LDAP schema. We have two options here : the nice and clean way to add the attribute is to modify the LDAP schema to add support for an extra attribute. The quick and dirty way is to disable schema check on OpenDS.
Guess which option I choose 🙂 ?
To disable schema check on OpenDS, open the file <opends home>config/config.ldif and change ds-cfg-check-schema: true to ds-cfg-check-schema: false
And restart OpenDS (if you are using the embedded OpenDS, restart Glassfish). Now, we are ready to add our serialNumber attribute.
First, find your serial number in your certificate
spirou:~ sst$ openssl x509 -inform DER -in ./Desktop/sst.cer -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
10:00:00:00:00:00:ef:0c:a4:e2:c3:87:79:34:76:24
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=BE, CN=Citizen CA/serialNumber=200715
Validity
Not Before: Dec 13 00:46:02 2007 GMT
Not After : Dec 10 23:59:59 2012 GMT
Subject: C=BE, CN=S\xC3\xA9bastien Stormacq (Authentication), SN=Stormacq, GN=S\xC3\xA9bastien G\xC3\xA9rard/serialNumber=72020412345
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:84:81:47:4b:68:51:86:21:9a:04:5c:79:16:3a:
e5:d2:44:fc:2e:7c:37:59:8c:89:dd:36:21:7b:da:
44:10:f3:1c:3d:a6:ac:44:da:54:c9:0e:3a:a8:e2:
b4:e4:e3:1d:15:3c:3f:3b:e6:8b:32:61:49:48:32:
10:ee:68:d1:b7:13:f9:95:8b:9f:51:72:84:f0:ed:
b4:cb:cc:e5:3a:85:7e:4d:c0:1d:25:80:9d:df:25:
8d:ca:28:e1:a1:e7:79:d7:fd:b8:b2:87:04:01:dc:
9d:88:17:fe:7f:c0:5a:0c:28:6d:25:08:08:b0:57:
41:b2:e4:52:14:0b:89:e0:2f
Exponent: 65537 (0x10001)
Then, use ldapmodify or LDAPBrowser’s GUI to add an attribute named serialNumber with the appropriate value.
A last ldapsearch will allow us to verify that everything is OK (change hostname, port number, password and uid accordingly) :
spirou:OpenDS sst$ ldapsearch -h localhost -p 1389 -D "cn=Directory Manager" -w adminadmin -x uid=sst
# extended LDIF
#
# LDAPv3
# base <> with scope subtree
# filter: uid=sst
# requesting: ALL
#
# sst, People, sun.com
dn: uid=sst,ou=People,dc=sun,dc=com
objectClass: person
objectClass: inetorgperson
objectClass: top
objectClass: organizationalperson
sn: Stormacq
userCertificate:: MIID9jCCAt6gAwIBAgIQEAAAAAAA7wyk4sOHeTR2JDANBgkqhkiG9w0BAQUF
ADAzMQswCQYDVQQGEwJCRTETMBEGA1UEAxMKQ2l0aXplbiBDQTEPMA0GA1UEBRMGMjAwNzE1MB4XD
TA3MTIxMzAwNDYwMloXDTEyMTIxMDIzNTk1OVowgYIxCzAJBgNVBAYTAkJFMS0wKwYDVQQDDCRTw6
liYXN0aWVuIFN0b3JtYWNxIChBdXRoZW50aWNhdGlvbikxETAPBgNVBAQTCFN0b3JtYWNxMRswGQY
DVQQqDBJTw6liYXN0aWVuIEfDqXJhcmQxFDASBgNVBAUTCzcyMDIwNDQ1MTgyMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQCEgUdLaFGGIZoEXHkWOuXSRPwufDdZjIndNiF72kQQ8xw9pqxE2lTJD
jqo4rTk4x0VPD875osyYUlIMhDuaNG3E/mVi59RcoTw7bTLzOU6hX5NwB0lgJ3fJY3KKOGh53nX/b
iyhwQB3J2IF/5/wFoMKG0lCAiwV0Gy5FIUC4ngLwIDAQABo4IBODCCATQwHwYDVR0jBBgwFoAUys4
fltKUqk3aDuoZtMinyvJVRMUwbQYIKwYBBQUHAQEEYTBfMDUGCCsGAQUFBzAChilodHRwOi8vY2Vy
dHMuZWlkLmJlbGdpdW0uYmUv1234Z2l1bXJzLmNydDAmBggrBgEFBQcwAYYaaHR0cDovL29jc3AuZ
WlkLmJlbGdpdW0uYmUwRAYDVR0gBD0wOzA5BgdgOAEBAQICMC4wLAYIKwYBBQUHAgEWIGh0dHA6Ly
9yZXBvc2l0b3J5LmVpZC5iZWxnaXVtLmJlMDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwuZWl
kLmJlbGdpdW0uYmUvZWlkYzIwMDcxNS5jcmwwDgYDVR0PAQH/BAQDAgeAMBEGCWCGSAGG+EIBAQQE
AwIFoDANBgkqhkiG9w0BAQUFAAOCAQEAbsgtXPw03mr/GdB4ph7EkEALrcC1rMYZ+36Z4ZjnpQFVw
Mq6Urio5VfgWvFLyHG2mv15mEoA60DgT0xJpIIJIvmPRnugXla572lFUQXcySe9iiLNu45YWyYBFs
eaI/AlUCfxlg7nO0juDns+OdhzRv3JrOnBptLhcp9WEjvOQLH7vlm7ZV8Ez2xzdeA8Zp9Jo/wtPcq
nSBzNJ/LdA0CMKXKyqqbYdlHSfOh5Bp0uMlZ0KQeZMVYd1qwLqk/cfeyj61QrZbo4+VAwp4qT3FxL
p8IBKtyTEJm17Lsn9NkdVhSkFS8vRVgfsdgfcY3kDTC+HNbkL6jm2zd3b3RoYbyAEwAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
userPassword:: e1NTSEF9cnpjNHFEcm9HbTVJV0IvZHJuQW5iVnNkd0crWW9YS0NrWUsxRVE9PQ=
=
cn: Sebastien Stormacq
serialNumber: 72020412345
givenName: Sebastien
uid: sst
mail: sst@maildomain.net
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
We are now ready to configure the OpenSSO Certificate Authentication module.
Configure Certificate Authentication in OpenSSO and Glassfish
1. Import Root Certificate into Glassfish certificate store
Glassfish must be able to verify your certificate’s authenticity. You must import the two Root Certificate (Citizen and Belgium) into Glassfish’s cacerts.jks certificate database (change file path accordingly) :
- keytool -import -alias belgium -file /Users/sst/Desktop/belgium.cer -keystore ./cacerts.jks -storepass changeit
- keytool -import -alias citizen -file /Users/sst/Desktop/citizen.cer -keystore ./cacerts.jks -storepass changeit
The cacert file is located under <glassfish home>/domains/<your domain name>/config
2. Change Glassfish’s configuration to enable client-side certificate authentication
By default, client-side certificate is disabled on Glassfish, we need to re-enable it : edit the domain.xml file located in the Glassfish’s domain configuration directory (same as above), and change.
<http-listener acceptor-threads="1" address="0.0.0.0" blocking-enabled="false" default-virtual-server="server" enabled="true"
family="inet" id="http-listener-2" port="55181" security-enabled="true" server-name="" xpowered-by="true">
<ssl cert-nickname="s1as" client-auth-enabled="true" ssl2-enabled="false" ssl3-enabled="true"
tls-enabled="true" tls-rollback-enabled="true"/>
</http-listener>
And restart Glassfish
3. Create a certificate-based authenticaion module
- Logon on the OpenSSO console (http://your host name:your port number/opensso)
- Select the realm (default name is sun)
- Select Authentication tab
- Under "Module Instances", click new
- Type a name for the module, i.e. eID
- Select Certificate in the list
- Click OK
4. Configure the Authentication Module
Click on the eID module in the list of available modules and configure as show below :
This will tell the Authentication module to use the Certificate’s serialNumber attribute to search for a matching certificate in the LDAP data store.
Then specify which attribute will be used to access the user’s profile in LDAP (notice that the authentication operation and the profile retrieval operation might use different LDAP attributes)
Finally,
- in the "DAP Server Where Certificates are Stored" list, be sure that your LDAP server is referenced, i.e. localhost:50389 when using the embedded OpenDS,
- Ensure the user name and credentials to access the LDAP store are correct.
- Ensure the base DN is given in the "LDAP Search Start DN" list (mine is ou=people,dc=sun,dc=com)
Click Save, then Back to Authentication
5. Configure Authentication’s Advanced Properties
Open the "Advanced Properties"
Ensure the "User profile" is Required.
Add the serialNumber attribute in the "Alias Search Attribute Name" as shown below :
This tells OpenSSO to search the LDAP attribute serialNumber for matching serialNumber from the certificate.
6. Configure the Authentication Chain
Finally, we must add our Certificate Authentication module in our authentication chain. We are adding it in the ldapservice chain, in the first position and with the "Sufficient" attribute, as shown below
This setting will make the Certificate Authentication module the first one to be proposed by OpenSSO. Should the certificate based authentication fails, OpenSSO will fall back to the traditional LDAP authentication, allowing your user to continue to connect to OpenSSO.
7. Test Test Test
- Open your favorite browser and point it to OpenSSO, using SSL ! (https://<your host name>:<ssl port number>/opensso)
- The browser will probably complaint about an invalid or unverifiable server certificate, this is normal, unless you have installed a genuine server side certificate on Glassfish. You can safely ignore is this warning.
- The browser will prompt you for your eID PIN code
- And voila, you should be directed to your users’ profile page.
8. Tips
- When using Firefox, be sure to start Firefox after you plugin the card reader
- If you have several client side certificates installed under Firefox, it will ask you which certificate you want to use to autehnticate, be sure to use the BPELPIC certifcate (for Belgium eID cards)
- The OpenDS log file is located under <OpenSSH home>/opends/logs and contains valuable information to understand why you can’t login
To deploy this in real life, you just need to deploy an OpenSSO Web or J2EE agent in your favorite container and you will be ready to protect your application with eID authentication !
Should find errors in this blog entry, or some steps I forgot, do not hesitate to leave a comment.
#1 by Jim Klimov on 13/03/2008 - 02:53
Do you by chance know, how exactly does the Certificate Auth Module work?
Does it use HTTP headers added by an HTTPS-deciphering layer or some local variables and flags in the context of the application server running AMServer/Portal/etc?
We have a project which concerns integration of proprietary cryptography with Sun Portals. There is no stable Java integration yet (the core product is "certified" and unchangeable, and I can only guess it’s written in C). Thus we can’t use the benefits of Java CryptoAPI to directly decipher the HTTPS data in the App Server context.
Instead, we use a separate program which can decipher these algorithms and forward plaintext HTTP to the backend servers (i.e. through a Sun SRA Gateway to the real-backend App Servers) and encodes the responses for the clients. You can consider it an "SSL Accelerator" or "proxy" in some common terms.
This program can also add HTTP headers (i.e. Certificate numbers chain) to the forwarded stream.
Can the Certificate Auth Module be used in this scenario, or are we bound to creation of a new Auth Module as well?
#2 by Sebastien Stormacq on 14/03/2008 - 08:13
Our Certificate Authentication Module is implementing the SSL client authentication RFC (I think it RFC 2246 : http://www.ietf.org/rfc/rfc2246.txt).
Should your configuration respects the standards, then it will work. Otherwise, writing a custom Authentication module is probably the solution.
#3 by Jim Klimov on 19/03/2008 - 06:15
Thank you.
And is there any good document or blog detailing how the Sun Certificate Authentication Module works? Does it interact with a crypto API directly?
In particular, how does the auth module know which certificate is presented by the visitor – does the module itself participate in TLS handshake, does it process certain HTTP headers set by an SSL gateway (or the web container itself), or are there some Java flags and variables set by the web container?
#4 by Flawinne Julien on 29/05/2008 - 10:08
Bonjour 🙂
Article très intéressent et assez détailé, j’était un des élèves de la java one after glow 😀 .
En passant, un merci pour votre présence.
++
#5 by Jan Van den Bergh on 22/07/2009 - 23:03
We recently tried a similar approach in our test environment to authenticate use the Belgian eID. However, it is not legal to store the serial number (= national number of a person) in a database.
Do you have any suggestions on how to do this without storing this number in plain text (apparantly a salted hash is allowed).
#6 by Sebastien Stormacq on 23/07/2009 - 00:07
Thank Jan for pointing me to this important legal aspect.
Some possibilities :
– Do not store the certificate in the LDAP. The module will check the validity of the certificate and accept whatever valid certificate is presented. This also simplifies the provisioning process as you don’t have to copy the certificates to the Directory and maintain revoked, expired etc certificates.
– Use another identifying attribute, if there is such attribute (an email address ?)
– Sublclass (or modify the source code of the) X509 authentication module to create your own. This would allow you to store a hash key of the serial number in the LDAP and search for the hash instead.
Should anyone have other options, let’s share and discuss this !