Securing & Hardening OpenIDM

OpenIDM provides a security management service, that manages keystore and truststore files. The security service is accessible over the REST interface, enabling you to read and import SSL certificates, and to generate certificate signing requests.

This chapter describes the security management service and its REST interface.

In addition, the chapter outlines the specific security procedures that you should follow before deploying OpenIDM in a production environment.

In a production environment, avoid the use of communication over insecure HTTP, self-signed certificates, and certificates associated with insecure ciphers.

Accessing the Security Management Service

OpenIDM stores keystore and truststore files in a folder named /path/to/openidm/security. These files can be managed by using the keytool command, or over the REST interface, at the URL https://localhost:8443/openidm/security. For information about using the keytool command, see http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html.

The following sections describe how to manage certificates and keys over REST.

Displaying the Contents of the Keystore

OpenIDM generates a symmetric key and a private key the first time the server is started. After startup, display the contents of the keystore over REST, as follows:

$ curl \
 --cacert self-signed.crt \
 --header "X-OpenIDM-Username: openidm-admin" \
 --header "X-OpenIDM-Password: openidm-admin" \
 --request GET \
 "https://localhost:8443/openidm/security/keystore"
    {
  "type" : "JCEKS",
  "provider" : {
     "Cipher.Blowfish SupportedKeyFormats" : "RAW",
     "AlgorithmParameters.DESede" : "com.sun.crypto.provider.DESedeParameters",
     "AlgorithmParameters.DES" : "com.sun.crypto.provider.DESParameters",
  ...
  },
  "aliases" : [ "openidm-sym-default", "openidm-localhost" ]
}

By default, OpenIDM includes the following aliases:

  • openidm-sym-default - the default symmetric key that is used, for example, to encrypt the configuration.

  • openidm-localhost - the default alias that is used by the Jetty web server to service SSL requests. This alias references a private key and a self-signed certificate. You can use the self-signed certificate for testing purposes. When you deploy OpenIDM in a production environment, you should replace the self-signed certificate with a certificate that has been signed by a certificate authority.

Importing a Signed Certificate into the Keystore

If you have an existing CA-signed certificate, you can import it into OpenIDM’s keystore by running a RESTful PUT command on the keystore alias. Include the signed certificate, private key, CA root certificate, and any intermediate certificates in the JSON payload.

The following command imports a CA-signed certificate, with the alias example-com into the keystore. Replace that alias with the alias of your certificate.

$ curl \
 --cacert self-signed.crt \
 --header "X-OpenIDM-Username: openidm-admin" \
 --header "X-OpenIDM-Password: openidm-admin" \
 --header "Content-Type: application/json" \
 --request PUT \
 --data '{
    "alias": "example-com",
    "cert": [
        "-----BEGIN CERTIFICATE-----\n
MIIGcDCCBVigAwIBAgIDC23tMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\n
TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\n
YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\n
MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTMwODA3MTMyODAz\n
WhcNMTQwODA4MDY0NTM5WjB2MRkwFwYDVQQNExBwZ3BDaGU4cEJPZnptVE9KMQsw\n
CQYDVQQGEwJHQjEjMCEGA1UEAxMadGVzdC1jb25uZWN0LmZvcmdlcm9jay5jb20x\n
JzAlBgkqhkiG9w0BCQEWGHBvc3RtYXN0ZXJAZm9yZ2Vyb2NrLmNvbTCCASIwDQYJ\n
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJRWGbnMGs+uGKU6ZrlTaaFdPczLqZnv\n
D37T0FOc/X3XXHxSVH94FDk7N4ansP2o6BsDWttIkM2AXkX3efMRaNpgxg7l4+DL\n
opV6H1RkrRba2Lom6Hp2pgkqvOBfd1ZMOmLbjUHt0jhypnIzu7TVwtTH7Ywsrx9F\n
uR9d4veYdW70IeQ64EhUG3RJBGG++AYJZCOjgEfbCwAYe/NoX/YVu+aMreHMR/+0\n
CV0YXKvHZgytcwZIc5WkQYaSWQA9lDWZzt5XjCErCATfiGEQ0k02QgpEfNTXxwQs\n
kfxh//O/qbfOWmloGwVU/2NY+5z3ZW8/eCksmiL1gGAYQAd+9+WI7BsCAwEAAaOC\n
Au4wggLqMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUF\n
BwMBMB0GA1UdDgQWBBR2zHzb71ZOHSwDZk28L9It3PvOtzAfBgNVHSMEGDAWgBTr\n
QjTQmLCrn/Qbawj3zGQu7w4sRTA0BgNVHREELTArghp0ZXN0LWNvbm5lY3QuZm9y\n
Z2Vyb2NrLmNvbYINZm9yZ2Vyb2NrLmNvbTCCAVYGA1UdIASCAU0wggFJMAgGBmeB\n
DAECATCCATsGCysGAQQBgbU3AQIDMIIBKjAuBggrBgEFBQcCARYiaHR0cDovL3d3\n
dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjCB9wYIKwYBBQUHAgIwgeowJxYgU3Rh\n
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwAwIBARqBvlRoaXMgY2VydGlm\n
aWNhdGUgd2FzIGlzc3VlZCBhY2NvcmRpbmcgdG8gdGhlIENsYXNzIDEgVmFsaWRh\n
dGlvbiByZXF1aXJlbWVudHMgb2YgdGhlIFN0YXJ0Q29tIENBIHBvbGljeSwgcmVs\n
aWFuY2Ugb25seSBmb3IgdGhlIGludGVuZGVkIHB1cnBvc2UgaW4gY29tcGxpYW5j\n
ZSBvZiB0aGUgcmVseWluZyBwYXJ0eSBvYmxpZ2F0aW9ucy4wNQYDVR0fBC4wLDAq\n
oCigJoYkaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0MS1jcmwuY3JsMIGOBggr\n
BgEFBQcBAQSBgTB/MDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5zdGFydHNzbC5j\n
b20vc3ViL2NsYXNzMS9zZXJ2ZXIvY2EwQgYIKwYBBQUHMAKGNmh0dHA6Ly9haWEu\n
c3RhcnRzc2wuY29tL2NlcnRzL3N1Yi5jbGFzczEuc2VydmVyLmNhLmNydDAjBgNV\n
HRIEHDAahhhodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS8wDQYJKoZIhvcNAQEFBQAD\n
ggEBAKVOAHtXTrgISj7XvE4/lLxAfIP56nlhpoLu8CqVlLK6eK4zCQRyTiFYx3xq\n
VQMSNVgQIdimjEsMz8o5/fDrCrozsT6sqxIPFsdgdskPyz9YyC9Y/AVBuECxabQr\n
B//0STicfdPg8PuDYtI64/INA47d/gtb57RaTFYxKs6bU8vtObinDJCwT33x4tvt\n
ob18DwB3/PeTbWyVUIxB0nvfm89dys0SF2alaA/bLuy0B7rdlppd4dOMpmiD0tnI\n
DORtr5HOD1xGiixZWzA1V2pTmF/hJZbhmEgBUSIyPK5Z9pZPephMf+/KrovbQqKr\n
6SEjgs7dGwpo6fA2mfCH5cCrid0=\n
-----END CERTIFICATE-----",
        "-----BEGIN CERTIFICATE-----\n
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\n
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\n
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\n
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\n
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\n
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\n
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\n
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\n
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\n
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\n
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\n
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\n
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\n
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\n
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\n
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\n
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\n
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\n
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n
-----END CERTIFICATE-----"
    ],
    "privateKey": "-----BEGIN RSA PRIVATE KEY-----\n
zDot5q3vP9YjCihMZMkSa0zT2Zt+8S+mC0EVuYuTVhVpqrVNtkP1mlt+CYqmDffY\n
sGuD6SMrT6+SeAzX2uYFgY4+s8yaRWBcr0C5Z7yihilM6BK+IJ4is9kaW5VFr1Ph\n
wRKvSeFHBGh2wLNpjVSNPzLMDZBtkVi9Ny/xD5C3M1Gah0PGmnrPGCP8tr1Lshv4\n
PxYJwzHzouTdQDkLYlCjMN++NmIYfx7zrbEYV4VzXMxgNq7d3+d5dlVfE8xpAjSR\n
Lqlamib+doe1oWOQ2WiS6baBAH+Gw5rgqfwhJbCY/UlbCpuJ6kl7TLvTrFp8YpvB\n
Iv1GD0yuwSued3a+AxMFuIzTBYd2rC6rHq+eF4eHd/Q/Sbm9+9VuW/h8dW3LGvbE\n
5SUUhNw6uSkOZmZ0z/+FLbwoLPCASukY9biSd+12KJf4N42WZxID+9mJTp1j/Bv7\n
n29oGfZ3vav8PqG+F987hSyWEIdGTMfIxwaUrdYe1fmbUCxv0suMcYTRbAs9g3cm\n
eCNxbZBYC/fL+Nlj5NjZ+gxA/tEXV7wWynPZW3mZny6fQpDTDMslqsoFZR+rAUzH\n
ViePuLbCdxIC5heUyqvDBbeOzgQWOu6SZjX+mAQpo0DPKt1KDP4DKv9EW92sIwW3\n
AnFg98sje0DZ+zfsnevGioQMJrG0JSnqTYADxHaauu7NWndkfMZisfNIKA0u+ajU\n
AbP8xFXIP5JU8O4tWmlbxAbMOYfrZHabFNZx4DH1OVOJqdJIVx0KER0GSZd50D6W\n
QBzCfEbwMlJ17OB0AgWzNrbaak3MCmW1mh7OecjQwge1ajy7ho+JtQ==\n
-----END RSA PRIVATE KEY-----"
 }' \
 "https://localhost:8443/openidm/security/keystore/cert/example-com"

    {
  "_id": "example-com",
  "alias": "example-com",
  "cert": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
  "privateKey": "-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----"
}

If the import is successful, the command returns the certificate alias that has been added to the keystore, along with the certificates and keys.

By default, OpenIDM uses the certificate with the alias openidm-localhost to service SSL requests. If you use a different certificate alias, you must change the value of the openidm.https.keystore.cert.alias property in your project’s conf/boot/boot.properties file to match the new alias, so that OpenIDM can use the new signed certificate. This change requires a server restart.

Generating a Certificate Signing Request Over REST

If you do not have an existing signed certificate, you can generate a certificate signing request (CSR) over REST, as described in this section. The details of the CSR are specified in JSON format, for example:

{
    "CN" : "www.example.com",
    "OU" : "HR",
    "L"  : "Cupertino",
    "C"  : "US"
}

For information about the complete contents of a CSR, see http://www.sslshopper.com/what-is-a-csr-certificate-signing-request.html.

To generate a CSR over the REST interface, include the private key alias in the URL. The following example uses the alias example-com). Set "returnPrivateKey" : true to return the private key along with the request.

$ curl \
 --cacert self-signed.crt \
 --header "X-OpenIDM-Username: openidm-admin" \
 --header "X-OpenIDM-Password: openidm-admin" \
 --header "Content-Type: application/json" \
 --request POST \
 --data '{"CN" : "www.example.com",
 "OU" : "HR",
 "L"  : "Cupertino",
 "C"  : "US",
 "returnPrivateKey" : true,
 "alias" : "example-com"}' \
 "https://localhost:8443/openidm/security/keystore?_action=generateCSR"
{
  "_id": "example-com",
  "csr": "-----BEGIN CERTIFICATE REQUEST-----\n
MIICmzCCAYMCAQAwWDEZMBcGA1UEAwwQd3d3MS5
leGFtcGxlLmNvbTELMAkGA1UE\nCwwCSFIxDTALBgNVBAoMBE5vbmUxEjAQBgNVBAcMCUN1cGVyd
GlubzELMAkGA1UE\nBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAjCjTt1b
o0WKH\nP/4PR/Td3A1ElTo4/J/7o7eWflOqs8vW5d76SMcJFKOQ6FhoOcOHRNewch+a0DBK\njKF
aRCE1c0PuXiIlrO7wsF4dFTtTZKAhrpFdM+0hU4LeyCDxQQ5UDga3rmyVIvC8\nL1PvW+sZEcZ9r
T67XOV03cwUpjvG4W58FCUKd6UAI0szfIrFdvJp4q4LkkBNkk9J\nUf+MXsSVuHzZrqvqhX900Is
a19mXD6/P9Cql8KmwEzzbglGFf6uYAK33F71Kx409\nTeS85sjmBbyJwUVwhgQ0R35H3HC6jex4P
jx1rSfPmsi61JBx9kyGu6rnSv5FOQGy\nBQpgQFnJAgMBAAEwDQYJKoZIhvcNAQENBQADggEBAKc
yInfo2d7/12jUrOjL4Bqt\nStuQS/HkO2KAsc/zUnlpJyd3RPI7Gs1C6FxIRVCzi4Via5QzE06n2
F8HHkinqc6m\nBWhIcf5Omk6fSqG0aw7fqn20XWDkRm+I4vtm8P8CuWftUj5qv5kmyUtrcQ3+YPD
O\nL+cK4cfuCkjLQ3h4GIgBJP+gfWX8fTmCHyaHEFjLTMj1hZYEx+3f8awOVFoNmr3/\nB8LIJNH
UiFHO6EED7LDOwa/z32mTRET0nK5DVO60H80JSWxzdWYZQV/IzHzm8ST4\n6j6vuheBZiG5gZR2V
F0x5XoudQrSg7lpVslXBHNeiM85+H08RMQh8Am2bp+Xstw=\n",
     -----END CERTIFICATE REQUEST-----\n",
  "publicKey": {
     "format": "X.509",
     "encoded": "-----BEGIN PUBLIC KEY-----\n
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr
ALtYU662bNbQZG7JZ3M\noOUmVP9cPP3+DhQ5H0V0qB+9YjE4XUtuwUGqaUmuT+mrXHwGpLAqvUm
NsVyXJj9s\nJhX6PCyXzO3RdKBVC8pphMfKXodjBC57ef0OkWjO5ZRAqCRwS3BXkoCfu6/ZXRpk\
ncc/A1RmLZdPmcuKmN5vQl4E3Z6F4YyG7M0g7TE54dhqPvGNS9cO4r0Vom9373MDh\n+8QSfmLCC
94Ro+VUAF9Q6nk2j0PgTi+QZ0i93jbKAWWX57w6S5i7CpEptKyeP9iG\ncFnJddSICPHkbQJ73gu
lyZYkbcBblNUxIhODZV5bJ0oxn9qgYvzlxJupldYsYkBo\ncwIDAQAB\n
     -----END PUBLIC KEY-----\n",
     "algorithm": "RSA"
  },
  "privateKey": {
     "format": "PKCS#8",
     "encoded": "-----BEGIN RSA PRIVATE KEY-----\n
MIIEpAIBAAKCAQEArALtYU662bNbQZG7JZ3MoOU
VP9cPP3+DhQ5H0V0qB+9YjE4\nXUtuwUGqaUmuT+mrXHwGpLAqvUmNsVyXJj9sJhX6PCyXzO3RdK
BVC8pphMfKXodj\nBC57ef0OkWjO5ZRAqCRwS3BXkoCfu6/ZXRpkcc/A1RmLZdPmcuKmN5vQl4E3
Z0i93jbKAWWX57w6S5i7CpEptKyeP9iGcFnJddSICPHkbQJ73gulyZYkbcBb\nlNUxIhODZV5bJ0
Z6F4\nYyG7M0g7TE54dhqPvGNS9cO4r0Vom9373MDh+8QSfmLCC94Ro+VUAF9Q6nk2j0Pg\nTi+Q
oxn9qgYvzlxJupldYsYkBocwIDAQABAoIBAGmfpopRIPWbaBb8\nWNIBcuz9qSsaX1ZolP+qNWVZ
bgfq7Y0FMlo/frQXEYBzqSETGJHC6wVn0+bF6scV\nVw86dLtyVWVr8I77HdoitfZ2hZLuZ/rh4d
BohpPi63YoyJs7DPTy4y2/v1aLuwoy\nMiQ0l6c3bm6sr+eIVgMH4A9Xk5/jzAHVTCBrvfTYZnh6
qD4Qmiuj8pQn79HQV8NK\nLt/5kmV1+uGj78jg7NR06NjNsa4L3mNZSiqsn2haPXZAnBjKfWApxe
GugURgNBCO\ncmYqCDZLvpMy4S/qoRBu+6qdYGprb+tHshBYNywuDkrgszhwgr5yRm8VQ60T9tM/
\nceKM+TECgYEA2Az2DkpC9TjJHPJG7x4boRRVqV5YRgPf5MrU+7PxDMb+EauXXUXg\nsch9Eeon
30yINqSv6FwATLVlkzQpZLkkJ6GJqAxUmPjRslAuosiSJqKaWamDUDbz\nSu/7iANJWvRGayqZsa
GQqFwM0Xpfp/EiBGe757k0D02u8sAv94A75bsCgYEAy9FQ\nMwDU3CaDzgv0qgR1ojXkSW0dCbv0
QPEkKZ2Ik7JbXzwVGzfdv2VUVrzRKBGReYzn\nGg/s4HbZkYy4O+SJo44n/5iO2pgKG5MEDFHSpw
X54Rm+qabT2fQ2lFJ/myWKsPgJ\n4gZ9bUvcemCcLLzsiAphueulQp49eOLnkzPlQKkCgYEAy7A0
jrZuuDjoStUUET5G\neC/urvZWrPPcMx0TfZZhTVWSlWA8HWDS/WnymGA1ZS4HQdU0TxHl6mwerp
C/8ckn\nEAIZAQlW/L2hHcbAoRIN0ET+1kedmJOl/mGQt+O5Vfn1JfYM3s5ezouyPhBsfK43\nDw
Ypvsb6EO+BYDXXQzVvwx8CgYB9o67LcfTFLNzNFCOi9pLJBm2OMbvXt0wPCFch\nbCG34hdfMntU
RvDjvgPqYASSrZm+kvQW5cBAciMWDOe4y91ovAW+En3lFBoO+2Zg\nbcPr/8wUTblxfQxU660Fa4
GL0u2Wv5/f+94vlLb5nTpIfcFU7wllAXTjBwaf0Uet\nPy1P2QKBgQDPoyJqPi2TdN7ZQYcoXAM4
Gl5Yv9oO16RC917XH6SLvj0ePmdLgBXo\nrR6aAmOjLzFp9jiytWZqVR9DbAWd2YNpvQav4Gude3
lteew02UT+GNv/gC71bXCw\ncFTxnmKjP8YYIBBqZXzuk9wEaHN7OdGybUW0dsBCGxTXwDKe8XiA
6w==\n-----END RSA PRIVATE KEY-----\n",
     "algorithm": "RSA"
}

This sample request returns the CSR, the private key associated with the request, and the public key. The security management service stores the private key in the repository.

When the signed certificate is returned by the certificate authority and you import the certificate into the keystore, you do not need to supply the private key. The security management service locates the private key in the repository, adds the certificate chain, and loads it into the keystore.

If you will be importing the signed certificate into the keystore of an OpenIDM instance that is not connected to the repository in which this private key was stored, you must include the private key when you import the signed certificate. Setting "returnPrivateKey" : true in the CSR enables you to maintain a copy of the private key for this purpose.

Send the output from

"csr": "-----BEGIN CERTIFICATE REQUEST-----
     ...
     -----END CERTIFICATE REQUEST-----

to your certificate authority for signature.

When the signed certificate is returned, import it into the keystore, as described in "Importing a Signed Certificate into the Keystore".

Generating a Self-Signed Certificate Over REST

To generate a self-signed X.509 certificate, use the generateCert action on the keystore endpoint. This action must be performed as an authenticated administrative user. The generated certificate is returned in the response to the request, and stored in the OpenIDM keystore.

Specify the details of the certificate in the JSON payload. For example:

$ curl \
 --cacert self-signed.crt \
 --header "X-OpenIDM-Username: openidm-admin" \
 --header "X-OpenIDM-Password: openidm-admin" \
 --header "Content-Type: application/json" \
 --request POST \
 --data '{
   "algorithm" : "RSA",
   "signatureAlgorithm" : "SHA512WithRSAEncryption",
   "keySize"  : 2048,
   "domainName"  : "www.example.com",
   "validFrom" : "2015-08-13T07:59:44.497+02:00",
   "validTo" : "2016-08-13T07:59:44.497+02:00",
   "returnPrivateKey" : true,
   "alias" : "new-alias"
 }' \
 "https://localhost:8443/openidm/security/keystore?_action=generateCert"
{
  "publicKey": {
    "algorithm": "RSA",
    "encoded": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
    ...
    \n-----END PUBLIC KEY-----\n",
    "format": "X.509"
  },
  "cert": "-----BEGIN CERTIFICATE-----\nMIIDSDCCAjCgAwIBAgIGAUfOo3GvMA0GCSqGSIb3
    ...
    \n-----END CERTIFICATE-----\n",
  "type": "X.509",
  "_id": "new-alias"
}

The following certificate details can be specified:

  • "algorithm" (optional) - the public key algorithm, for example, RSA. If no algorithm is specified, a default of RSA is used.

  • "signatureAlgorithm" (optional) - the signature type, for example, SHA512WithRSAEncryption. If no algorithm is specified, a default of SHA512WithRSAEncryption is used.

  • "keySize" (optional) - the size of the key (in bits) used in the cryptographic algorithm, for example 2048. If no key size is specified, a default of 2048 is used.

  • "domainName" - the fully qualified domain name (FQDN) of your server, for example www.example.com.

  • "validFrom" and "validTo" (optional) - the validity period of the certificate, in UTC time format, for example 2014-08-13T07:59:44.497+02:00. If no values are specified, the certificate is valid for one year, from the current date.

  • "returnPrivateKey" (optional) - set this to true to return the private key along with the request.

  • "alias" - the keystore alias or string that identifies the certificate, for example openidm-localhost.

Security Precautions for a Production Environment

Out of the box, OpenIDM is set up for ease of development and deployment. When you deploy OpenIDM in production, there are specific precautions you should take to minimize security breaches. After following the guidance in this section, make sure that you test your installation to verify that it behaves as expected before putting it into production.

Use SSL and HTTPS

Disable plain HTTP access, as described in "Secure Jetty".

Use TLS/SSL to access OpenIDM, ideally with mutual authentication so that only trusted systems can invoke each other. TLS/SSL protects data on the network. Mutual authentication with strong certificates, imported into the trust and keystores of each application, provides a level of confidence for trusting application access.

Augment this protection with message level security where appropriate.

Restrict REST Access to the HTTPS Port

When possible, use a certificate to secure REST access, over HTTPS. For production, that certificate should be signed by a certificate authority.

OpenIDM generates a self-signed certificate when it first starts up. You can use this certificate to test secure REST access.

While not recommended for production, you can test secure REST access using the default self-signed certificate. To do so, you can create a self-signed certificate file, self-signed.crt, using the following procedure:

  1. Extract the certificate that is generated when OpenIDM starts up.

    $ openssl s_client -showcerts -connect localhost:8443 </dev/null

    This command outputs the entire certificate to the terminal.

  2. Using any text editor, create a file named self-signed.crt. Copy the portion of the certificate from -----BEGIN CERTIFICATE----- to ----END CERTIFICATE----- and paste it into the self-signed.crt file, which should appear similar to the following:

    $ more self-signed.crt
    -----BEGIN CERTIFICATE-----
    MIIB8zCCAVygAwIBAgIETkvDjjANBgkqhkiG9w0BAQUFADA+MSgwJgYDVQQKEx9P
    cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
    c3QwHhcNMTEwODE3MTMzNTEwWhcNMjEwODE3MTMzNTEwWjA+MSgwJgYDVQQKEx9P
    cGVuSURNIFNlbGYtU2lnbmVkIENlcnRpZmljYXRlMRIwEAYDVQQDEwlsb2NhbGhv
    c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKwMkyvHS5yHAnI7+tXUIbfI
    nQfhcTChpWNPTHc/cli/+Ta1InTpN8vRScPoBG0BjCaIKnVVl2zZ5ya74UKgwAVe
    oJQ0xDZvIyeC9PlvGoqsdtH/Ihi+T+zzZ14oVxn74qWoxZcvkG6rWEOd42QzpVhg
    wMBzX98slxkOZhG9IdRxAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEASo4qMI0axEKZ
    m0jU4yJejLBHydWoZVZ8fKcHVlD/rTirtVgWsVgvdr3yUr0Idk1rH1nEF47Tzn+V
    UCq7qJZ75HnIIeVrZqmfTx8169paAKAaNF/KRhTE6ZII8+awst02L86shSSWqWz3
    s5xPB2YTaZHWWdzrPVv90gL8JL/N7/Q=
    -----END CERTIFICATE-----
  3. Test REST access on the HTTPS port, referencing the self-signed certificate in the command. For example:

    $ curl \
     --header "X-OpenIDM-Username:openidm-admin" \
     --header "X-OpenIDM-Password:openidm-admin" \
     --cacert self-signed.crt \
     --request GET \
     "https://localhost:8443/openidm/managed/user/?_queryId=query-all-ids"
         {
        "result": [],
        "resultCount": 0,
        "pagedResultsCooke": null,
        "remainingPagedResuts": -1
    }

Restrict the HTTP Payload Size

Restricting the size of HTTP payloads can protect the server against large payload HTTP DDoS attacks. OpenIDM includes a servlet filter that limits the size of an incoming HTTP request payload, and returns a 413 Request Entity Too Large response when the maximum payload size is exceeded.

By default, the maximum payload size is 5MB. You can configure the maximum size in your project’s conf/servletfilter-payload.json file. That file has the following structure by default:

{
     "classPathURLs" : [ ],
     "systemProperties" : { },
     "requestAttributes" : { },
     "scriptExtensions" : { },
     "initParams" : {
         "maxRequestSizeInMegabytes" : "5"
     },
     "urlPatterns" : [
         "/*"
     ],
     "filterClass" : "org.forgerock.openidm.jetty.LargePayloadServletFilter"
 }

Change the value of the maxRequestSizeInMegabytes property to set a different maximum HTTP payload size. The remaining properties in this file are described in "Registering Additional Servlet Filters".

Encrypt Data Internally and Externally

Beyond relying on end-to-end availability of TLS/SSL to protect data, OpenIDM also supports explicit encryption of data that goes on the network. This can be important if the TLS/SSL termination happens prior to the final endpoint.

OpenIDM also supports encryption of data stored in the repository, using a symmetric key. This protects against some attacks on the data store. Explicit table mapping is supported for encrypted string values.

OpenIDM automatically encrypts sensitive data in configuration files, such as passwords. OpenIDM replaces clear text values when the system first reads the configuration file. Take care with configuration files having clear text values that OpenIDM has not yet read and updated.

Use Message Level Security

OpenIDM supports message level security, forcing authentication before granting access. Authentication works by means of a filter-based mechanism that lets you use either an HTTP Basic like mechanism or OpenIDM-specific headers, setting a cookie in the response that you can use for subsequent authentication. If you attempt to access OpenIDM URLs without the appropriate headers or session cookie, OpenIDM returns HTTP 401 Unauthorized, or HTTP 403 Forbidden, depending on the situation. If you use a session cookie, you must include an additional header that indicates the origin of the request.

Message Level Security with Logins

The following examples show successful authentications.

$ curl \
 --cacert self-signed.crt \
 --dump-header /dev/stdout \
 --user openidm-admin:openidm-admin \
 "https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Set-Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ...;Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)

{"result":[],"resultCount":"0","pagedResultsCookie":null,"remainingPagedResults":-1}


$ curl \
 --cacert self-signed.crt \
 --dump-header /dev/stdout \
 --header "X-OpenIDM-Username: openidm-admin" \
 --header "X-OpenIDM-Password: openidm-admin" \
 "https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Set-Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ...;Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)

{"result":[],"resultCount":"0","pagedResultsCookie":null,"remainingPagedResults":-1}


$ curl \
 --dump-header /dev/stdout \
 --cacert self-signed.crt \
 --header "Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ..." \
 --header "X-Requested-With: OpenIDM Plugin" \
 "https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"

Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)

Notice that the last example uses the cookie OpenIDM set in the response to the previous request, and includes the X-Requested-With header to indicate the origin of the request. The value of the header can be any string, but should be informative for logging purposes. If you do not include the X-Requested-With header, OpenIDM returns HTTP 403 Forbidden.

The careful readers among you may notice that the expiration date of the JWT cookie, January 1, 1970, corresponds to the start of UNIX time. Since that time is in the past, browsers will not store that cookie after the browser is closed.

You can also request one-time authentication without a session.

$ curl \
 --dump-header /dev/stdout \
 --cacert self-signed.crt \
 --header "X-OpenIDM-NoSession: true" \
 --header "X-OpenIDM-Username: openidm-admin" \
 --header "X-OpenIDM-Password: openidm-admin" \
 "https://localhost:8443/openidm/managed/user?_queryId=query-all-ids"

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)

{"result":[],"resultCount":"0","pagedResultsCookie":null,"remainingPagedResults":-1}

Sessions and the JWT Cookie

OpenIDM maintains sessions with a JWT session cookie, stored in a client browser. By default, it deletes the cookie when you log out. Alternatively, if you delete the cookie, that ends your session.

You can modify what happens to the session after a browser restart. Open the authentication.json file, and change the value of the sessionOnly property. For more information on sessionOnly, see "Session Module".

The JWT session cookie is based on the JWT_SESSION module, described in "Supported Authentication and Session Modules".

Replace Default Security Settings

The default security settings are adequate for evaluation purposes. In production environments, change at least the following settings:

  • The password of the default administrative user (openidm-admin)

  • The default keystore password

Change the Default Administrator Password
  1. To change the password of the default administrative user, first retrieve the complete user object to make sure you have the currently assigned roles:

    $ curl \
     --cacert self-signed.crt \
     --header "X-OpenIDM-Username: openidm-admin" \
     --header "X-OpenIDM-Password: openidm-admin" \
     --request GET \
     "https://localhost:8443/openidm/repo/internal/user/openidm-admin"
    {
      "_id": "openidm-admin",
      "_rev": "1",
      "password": "openidm-admin",
      "roles": [
        {
          "_ref": "repo/internal/role/openidm-admin"
        },
        {
          "_ref": "repo/internal/role/openidm-authorized"
        }
      ],
      "userName": "openidm-admin"
    }
  2. Update the password with a PUT request, including the roles property that you retrieved in the previous step:

    The following example changes the password of the openidm-admin user to Passw0rd:

    $ curl \
     --cacert self-signed.crt \
     --header "Content-Type: application/json" \
     --header "X-OpenIDM-Username: openidm-admin" \
     --header "X-OpenIDM-Password: openidm-admin" \
     --request PUT \
     --data '{
         "password": "Passw0rd",
         "userName": "openidm-admin",
         "roles": [
           {
             "_ref": "repo/internal/role/openidm-admin"
           },
           {
             "_ref": "repo/internal/role/openidm-authorized"
           }
         ],
         "_id": "openidm-admin"
         }' \
     "https://localhost:8443/openidm/repo/internal/user/openidm-admin"
    {
      "_id": "openidm-admin",
      "_rev": "2",
      "password": {
        "$crypto": {
          "value": {
            "algorithm": "SHA-256",
            "data": "gjTSqGjVyfTLiWRlEemKKArELUipXyaW416y14U9KbWOkvT6ReGu7PffiExIb26K"
          },
          "type": "salted-hash"
        }
      },
      "userName": "openidm-admin",
      "roles": [
        {
          "_ref": "repo/internal/role/openidm-admin"
        },
        {
          "_ref": "repo/internal/role/openidm-authorized"
        }
      ]
    }
  3. Test that the update has been successful by querying OpenIDM with the new credentials:

    $ curl \
     --cacert self-signed.crt \
     --header "X-OpenIDM-Username: openidm-admin" \
     --header "X-OpenIDM-Password: Passw0rd" \
     --request GET \
     "https://localhost:8443/openidm/repo/internal/user/openidm-admin"
    {
      "_id": "openidm-admin",
      "_rev": "2",
      ...
    }

The administrative user can also reset their own password in the Self-Service UI as follows:

  1. Log into the Self-Service UI (https://localhost:8443/) with the default username and password (openidm-admin and openidm-admin).

  2. In the top right corner, select View Profile.

  3. On the Password tab, enter and confirm the new password, then click Update.

Change the Default Keystore Password

OpenIDM uses the information in conf/boot/boot.properties, including the keystore password, to start up. The keystore password is changeit by default, and is stored in clear text in the boot.properties file.

You must set a strong keystore password in any production deployment, but especially in cluster deployments. In a cluster deployment, the keystore is distributed through the repository. The strength of the keystore password is therefore the only thing that protects your deployment against exposure of the keystore and of any values encrypted by that keystore (such as user passwords).

To set an obfuscated version of the keystore password in the boot.properties file, follow these steps.

  1. Generate an obfuscated version of the password, by using the crypto bundle provided with OpenIDM:

    $ $ java -jar /path/to/openidm/bundle/openidm-crypto-4.5.1-20.jar
    This utility helps obfuscate passwords to prevent casual observation.
    It is not securely encrypted and needs further measures to prevent disclosure.
    Please enter the password:
    OBF:1vn21ugu1saj1v9i1v941sar1ugw1vo0
    CRYPT:a8b5a01ba48a306f300b62a1541734c7
  2. Paste either the obfuscated password (OBF:xxxxxxx) or the encrypted password (CRYPT:xxxxxxx) into the conf/boot/boot.properties file.

    Comment out the regular keystore password and remove the comment tag, either from the line that contains the obfuscated password or from the line that contains the encrypted password:

    $ more conf/boot/boot.properties
    ...
    # Keystore password, adjust to match your keystore and protect this file
    # openidm.keystore.password=changeit
    openidm.truststore.password=changeit
    
    # Optionally use the crypto bundle to obfuscate the password and set one of these:
    openidm.keystore.password=OBF:1vn21ugu1saj1v9i1v941sar1ugw1vo0
    # openidm.keystore.password=CRYPT:a8b5a01ba48a306f300b62a1541734c7
    ...
  3. Restart OpenIDM.

    $ ./startup.sh

The keystore password and the password of the keys themselves must be the same. If the password of your existing certificate is not the same as the keystore password, change the certificate password to match that of the keystore, as follows:

$ $ keytool \
 -keypasswd \
 -alias openidm-localhost \
 -keystore keystore.jceks \
 -storetype JCEKS
Enter keystore password: keystore-pwd
Enter key password for <openidm-localhost>old-password
New key password for <openidm-localhost>: keystore-pwd
Re-enter new key password for <openidm-localhost>: keystore-pwd

Secure Jetty

If you do not want to use regular HTTP on a production OpenIDM system, you need to make two changes.

First, edit the openidm/conf/jetty.xml configuration file. Comment out or delete the <Call name="addConnector"> code block that includes the openidm.port.http property. Keep the <Call name="addConnector"> code blocks that contain the openidm.port.https and openidm.port.mutualauth properties. You can set the value for these properties in the conf/boot/boot.properties file.

Second, edit the openidm/conf/config.properties configuration file. Set the org.osgi.service.http.enabled property to false, as shown in the following excerpt:

# Enable pax web http/https services to enable jetty
org.osgi.service.http.enabled=false
org.osgi.service.http.secure.enabled=true

Protect Sensitive REST Interface URLs

Anything attached to the router is accessible with the default policy, including the repository. If you do not need such access, deny it in the authorization policy to reduce the attack surface.

In addition, you can deny direct HTTP access to system objects in production, particularly access to action. As a rule of thumb, do not expose anything that is not used in production.

For an example that shows how to protect sensitive URLs, see "Understanding the Access Configuration Script (access.js)".

OpenIDM supports native query expressions on the repository, and you can enable these over HTTP. For example, the following query returns all managed users in an OrientDB repository:

$ curl \
 --cacert self-signed.crt \
 --header "X-OpenIDM-Username: openidm-admin" \
 --header "X-OpenIDM-Password: openidm-admin" \
 "https://localhost:8443/openidm/managed/user?_queryExpression=select+*+from+managed_user"

By default, direct HTTP access to native queries is disallowed, and should remain so in production systems.

For testing or development purposes, it can be helpful to enable native queries on the repository over HTTP. To do so, edit the access control configuration file (access.js). In that file, remove any instances of "disallowQueryExpression()" such as the following:

// openidm-admin can request nearly anything (except query expressions on repo endpoints)
{
    "pattern"   : "*",
    "roles"     : "openidm-admin",
    "methods"   : "*", // default to all methods allowed
    "actions"   : "*", // default to all actions allowed
 // "customAuthz" : "disallowQueryExpression()",
    "excludePatterns": "repo,repo/*"
},
// additional rules for openidm-admin that selectively enable certain parts of system/
{
    "pattern"   : "system/*",
    "roles"     : "openidm-admin",
    "methods"   : "create,read,update,delete,patch,query", // restrictions on 'action'
    "actions"   : ""
 // "customAuthz" : "disallowQueryExpression()"
},

Protect Sensitive Files & Directories

Protect OpenIDM files from access by unauthorized users.

In particular, prevent other users from reading files in at least the openidm/conf/boot/ and openidm/security/ directories.

The objective is to limit access to the user that is running the service. Depending on the operating system and configuration, that user might be root, Administrator, openidm, or something similar.

Protecting key files in Unix
  1. For the target directory, and the files therein, make sure user and group ownership is limited to the user that is running the OpenIDM service.

  2. Disable access of any sort for other users. One simple command for that purpose, from the /path/to/openidm directory, is:

    # chmod -R o-rwx .
Protecting key files in Windows
  1. The OpenIDM process in Windows is normally run by the Administrator user.

  2. If you are concerned about the security of the administrative account, you can Deny permissions on the noted directories to existing users, or alternatively the Users group.

Remove or Protect Development & Debug Tools

Before you deploy OpenIDM in production, remove or protect development and debug tools, including the OSGi console that is exposed under /system/console. Authentication for this console is not integrated with authentication for OpenIDM.

To remove the OSGi console, remove the web console bundle, and all of the plugin bundles related to the web console, as follows:

$ cd /path/to/openidm/bundle
$ rm org.apache.felix.webconsole*.jar

If you cannot remove the OSGi console, protect the console by overriding the default admin:admin credentials. Create a file named openidm/conf/org.apache.felix.webconsole.internal.servlet.OsgiManager.cfg that contains the user name and password to access the console in Java properties file format, for example:

username=user-name
password=password

Protect the OpenIDM Repository

OpenIDM 4.5 only supports the use of a JDBC repository in production.

Use a strong password for the JDBC connection and change at least the password of the database user (openidm by default). When you change the database username and/or password, you must update the database connection configuration file (datasource.jdbc-default.json) for your repository type.

For example, the following excerpt of a MySQL connection configuration file indicates the required change when the database user password has been changed to myPassw0rd.

{
    "driverClass" : "com.mysql.jdbc.Driver",
    "jdbcUrl" : "jdbc:mysql://localhost:3306/openidm?allowMultiQueries=true&characterEncoding=utf8",
    "databaseName" : "openidm",
    "username" : "openidm",
    "password" : "myPassw0rd",
    "connectionTimeout" : 30000,
    "connectionPool" : {
        "type" : "bonecp"
    }
}

Use a case sensitive database, particularly if you work with systems with different identifiers that match except for case. Otherwise correlation queries or correlation scripts can pick up identifiers that should not be considered the same.

Remove OrientDB Studio

OpenIDM ships with the OrientDB Studio web application. ForgeRock strongly recommends that you remove the web application before deploying in a production environment. To remove OrientDB studio, delete the following directory:

/path/to/openidm/db/util/orientdb

Verify that the application has been removed by trying to access http://localhost:2480/.

Note that an error will be logged on startup when you have removed OrientDB Studio. You can safely ignore this error.

Adjust Log Levels

Leave log levels at INFO in production to ensure that you capture enough information to help diagnose issues. For more information, see "Configuring Server Logs".

At start up and shut down, INFO can produce many messages. Yet, during stable operation, INFO generally results in log messages only when coarse-grain operations such as scheduled reconciliation start or stop.

Set Up Restart At System Boot

You can run OpenIDM in the background as a service (daemon), and add startup and shutdown scripts to manage the service at system boot and shutdown. For more information, see "Starting and Stopping OpenIDM".

See your operating system documentation for details on adding a service such as OpenIDM to be started at boot and shut down at system shutdown.