This post comes as the result of a request for TFIM help from a colleague. I thought the work product was useful and generic enough to be of interest to others. The use case was a user identity mediation flow, with a TFIM STS chain to validate an X509 certificate (sent as a BinarySecurityToken) and extract custom extension attributes. The value from one of the custom extension attribute is then used by another identity mapping module to lookup user attributes in an LDAP repository.
TFIM 6.2.0 has a built-in X509STSModule which performs token validation (validation of the certificate chain), and extracts the Subject DN as the principal name of the resulting STSUniversalUser. None of the other standard X509 attributes, or any custom extension data are read from the certificate. To assist with this use case I wrote an STS Mapping Module (following the pattern described in this developerworks tutorial) which extracts attribute data from an X509Certificate and inserts corresponding attributes in the STSUniversalUser. It is used in an STS chain, as shown in the following diagram:
The ExtensionsMapModule uses the java.security.cert.X509Certificate class to unpack certificate data, and map it to attributes in the STSUniversalUser. The following “getter” methods of the X509Certificate are used to map attributes, as shown below. Pay particular attention to the last entries notes on custom extensions and ASN.1 encoding bytes:
| X509Certificate method |
STSUniversalUser Attribute Name |
Encoding Notes |
| getSubjectDN |
SubjectDN |
String returned from API and included directly as attribute value |
| getIssuerDN |
IssuerDN |
String returned from API and included directly as attribute value |
| getNotBefore |
NotBefore |
Date returned from API, and SimpleDateFormat used to encode attribute using the date format string “yyyy-MM-dd’T’HH’:’mm’:’ss’Z'” |
| getNotAfter |
NotAfter |
Date returned from API, and SimpleDateFormat used to encode attribute using the date format string “yyyy-MM-dd’T’HH’:’mm’:’ss’Z'” |
| getSerialNumber |
SerialNumber |
BigInteger returned from API, decimal value included as string attribute value |
| getType |
Type |
String returned from API and included directly as attribute value |
| getVersion |
Version |
Integer returned from API and decimal value included as string attribute value |
| getBasicConstraints |
BasicConstraints |
Integer returned from API and decimal value included as string attribute value |
| getExtensionValue |
OID of extension |
The getExtensionValue method returns a byte[] which is an ASN.1 encoded octect string. This byte array includes the ASN.1 encoding data, and the complete resulting byte array is added as a string attribute value, with each byte printed as a 2-character hex string separated by spaces. |
Detailed Example
Let’s follow a detailed example, starting with a PEM-encoded certificate (this one is an Equifax public CA certificate), dump it’s contents with the OpenSSL command line utility, then see how attributes from that certificate are mapped by the ExtensionMapModule.
PEM-encoded Equifax CA certificate
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
-----END CERTIFICATE-----
Text output from openssl x509
# openssl x509 -in equifax.pem -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 903804111 (0x35def4cf)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
Validity
Not Before: Aug 22 16:41:51 1998 GMT
Not After : Aug 22 16:41:51 2018 GMT
Subject: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:c1:5d:b1:58:67:08:62:ee:a0:9a:2d:1f:08:6d:
91:14:68:98:0a:1e:fe:da:04:6f:13:84:62:21:c3:
d1:7c:ce:9f:05:e0:b8:01:f0:4e:34:ec:e2:8a:95:
04:64:ac:f1:6b:53:5f:05:b3:cb:67:80:bf:42:02:
8e:fe:dd:01:09:ec:e1:00:14:4f:fc:fb:f0:0c:dd:
43:ba:5b:2b:e1:1f:80:70:99:15:57:93:16:f1:0f:
97:6a:b7:c2:68:23:1c:cc:4d:59:30:ac:51:1e:3b:
af:2b:d6:ee:63:45:7b:c5:d9:5f:50:d2:e3:50:0f:
3a:88:e7:bf:14:fd:e0:c7:b9
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 CRL Distribution Points:
DirName:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority/CN=CRL1
X509v3 Private Key Usage Period:
Not After: Aug 22 16:41:51 2018 GMT
X509v3 Key Usage:
Certificate Sign, CRL Sign
X509v3 Authority Key Identifier:
keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
X509v3 Subject Key Identifier:
48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
X509v3 Basic Constraints:
CA:TRUE
1.2.840.113533.7.65.0:
0...V3.0c....
Signature Algorithm: sha1WithRSAEncryption
58:ce:29:ea:fc:f7:de:b5:ce:02:b9:17:b5:85:d1:b9:e3:e0:
95:cc:25:31:0d:00:a6:92:6e:7f:b6:92:63:9e:50:95:d1:9a:
6f:e4:11:de:63:85:6e:98:ee:a8:ff:5a:c8:d3:55:b2:66:71:
57:de:c0:21:eb:3d:2a:a7:23:49:01:04:86:42:7b:fc:ee:7f:
a2:16:52:b5:67:67:d3:40:db:3b:26:58:b2:28:77:3d:ae:14:
77:61:d6:fa:2a:66:27:a0:0d:fa:a7:73:5c:ea:70:f1:94:21:
65:44:5f:fa:fc:ef:29:68:a9:a2:87:79:ef:79:ef:4f:ac:07:
77:38
STSUniversalUser AttributeList after mapping all attributes, and the custom extensions 2.5.29.14 and 1.2.840.113533.7.65.0
<stsuuser:AttributeList>
<stsuuser:Attribute name="SubjectDN" type="urn:extensionsmap"gt;
<stsuuser:Value>OU=Equifax Secure Certificate Authority, O=Equifax, C=US<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="IssuerDN" type="urn:extensionsmap"gt;
<stsuuser:Value>OU=Equifax Secure Certificate Authority, O=Equifax, C=US<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="NotBefore" type="urn:extensionsmap"gt;
<stsuuser:Value>1998-08-23T02:41:51Z<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="NotAfter" type="urn:extensionsmap"gt;
<stsuuser:Value>2018-08-23T02:41:51Z<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="SerialNumber" type="urn:extensionsmap"gt;
<stsuuser:Value>903804111<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="Type" type="urn:extensionsmap"gt;
<stsuuser:Value>X.509<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="Version" type="urn:extensionsmap"gt;
<stsuuser:Value>3<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="BasicConstraints" type="urn:extensionsmap"gt;
<stsuuser:Value>2147483647<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="2.5.29.14" type="urn:extensionsmap"gt;
<stsuuser:Value>04 16 04 14 48 e6 68 f9 2b d2 b2 95 d7 47 d8 23 20 10 4f 33 98 90 9f d4<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:Attribute name="1.2.840.113533.7.65.0" type="urn:extensionsmap"gt;
<stsuuser:Value>04 0d 30 0b 1b 05 56 33 2e 30 63 03 02 06 c0<stsuuser:Value>
<stsuuser:Attribute>
<stsuuser:AttributeList>
In particular, let’s look at the custom attributes represented by the last two entries in the STSUniversalUser in more detail. The first entry, represented by OID=2.5.29.14 is actually the SubjectKeyIdentifier, and if you look at the certificate data output by OpenSSL, you’ll notice that the first 4 bytes of the sequence are not part of value of the Subject Key Identifier, but actually part of the ASN.1 encoding (04 = OCTET STRING, length = 0x16, contains another octet string of lenth 0x14). Similarly, for the other custom extension (1.2.840.113533.7.65.0), the first two bytes are ASN.1 encoding data (octet string of length 0x0d), and the rest of the bytes represent the value. The message here is that the mapping module dumps the raw bytes as returned by the method X509Certificate.getExtensionValue(), and it will be up to you to intepret them however you need to.
Deployment and Configuration
The module is available with complete source as a downloadable jar file here.
- Deploy it by copying to your FIM_install_root/plugins directory, then use the Runtime Node Management panel of the administration console to Publish Plugins and Reload Configurations.
- Create an instance of the module using the console under Security Token Service -> Module Instances.
- Finally use the instance in a chain. In my test environment I created a chain as shown:
The configuration of the ExtensionMapModule allows you to select which standard attributes should be included in the STSUniversalUser, and enter the OID’s of custom extension attributes that you would like to have included. Here’s the configuration from my example:
Testing the Module
This test harness example zip contains an example curl command and SOAP message to send to the TFIM security token service to test the trust chain.
Conclusion
This example demonstrates a basic mapping module that leverages Java API’s to extract additional information from X509 certificates sent to the STS as BinarySecurityToken. Hopefully it serves as another working example of how mapping modules can be used to extend basic functionality provided by the started TFIM STS token modules.