Migrating encryption keys

Last modified 22 Apr 2021 17:31 +02:00
Since 3.9
This functionality is available since version 3.9.

There can be situations in which you need to re-encrypt all protected values with a current encryption key. A typical example is when the old key was compromised and you need to stop its use immediately. Here we describe how to do that.

Listing encryption keys before the action (optional)

Before attempting this operation you might want to check the current state of affairs. The following task (samples/tasks/bulk-actions/list-encryption-keys.xml) lists all objects that contain protected material along with the keys used. It can also report objects with sensitive data that is not encrypted (i.e. in the cleartext or hashed).

list-encryption-keys.xml
<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
      xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3"
      xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      oid="b427848b-ea98-4da1-acba-c16cbb0e9c22">
    <name>List encryption keys</name>
    <extension xmlns:mext="http://midpoint.evolveum.com/xml/ns/public/model/extension-3"
               xmlns:scext="http://midpoint.evolveum.com/xml/ns/public/model/scripting/extension-3">
        <scext:executeScript>
            <s:action>
                <s:type>execute-script</s:type>
                <s:parameter>
                    <s:name>script</s:name>
                    <value xsi:type="c:ScriptExpressionEvaluatorType">
                        <c:code>
                            import com.evolveum.midpoint.common.crypto.CryptoUtil

                            midpoint.applyDefinition(input)
                            keys = CryptoUtil.getEncryptionKeyNames(input.asPrismObject())
                            clear = CryptoUtil.containsCleartext(input.asPrismObject())
                            hashed = CryptoUtil.containsHashedData(input.asPrismObject())
                            if (!keys.isEmpty() || clear || hashed) {
                                append = ""
                                if (clear) {
                                    append += " [CLEARTEXT]"
                                }
                                if (hashed) {
                                    append += " [HASHED]"
                                }
                                log.info(sprintf('Object: %-100s uses keys: %s%s', input, keys, append))
                            }
                        </c:code>
                    </value>
                </s:parameter>
                <s:parameter>
                    <s:name>quiet</s:name>
                    <value>true</value>
                </s:parameter>
            </s:action>
        </scext:executeScript>
        <mext:useRepositoryDirectly>true</mext:useRepositoryDirectly>
    </extension>
    <ownerRef oid="00000000-0000-0000-0000-000000000002"/>
    <executionStatus>runnable</executionStatus>
    <category>BulkActions</category>
    <handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/iterative-scripting/handler-3</handlerUri>
    <recurrence>single</recurrence>
</task>

The result looks like this:

2018-10-16 18:06:02,665 [MODEL] [midPointScheduler_Worker-4] INFO (com.evolveum.midpoint.expression): Object: systemConfiguration:00000000-0000-0000-0000-000000000001(SystemConfiguration)                        uses keys: [A5yfWFgnD4euq3CgpdecmZPA/DE=]
2018-10-16 18:06:02,688 [MODEL] [midPointScheduler_Worker-4] INFO (com.evolveum.midpoint.expression): Object: user:00000000-0000-0000-0000-000000000002(administrator)                                             uses keys: [A5yfWFgnD4euq3CgpdecmZPA/DE=]
2018-10-16 18:06:02,923 [MODEL] [midPointScheduler_Worker-4] INFO (com.evolveum.midpoint.expression): Object: user:5e7bcc2a-84b6-465e-b656-e4cef28cf575(frank)                                                     uses keys: [] [CLEARTEXT]
2018-10-16 18:06:02,972 [MODEL] [midPointScheduler_Worker-4] INFO (com.evolveum.midpoint.expression): Object: resource:62a63be7-a5fb-4168-a389-ef69f8982a85(Basic Localhost OpenDJ)                                uses keys: [A5yfWFgnD4euq3CgpdecmZPA/DE=]
2018-10-16 18:06:02,981 [MODEL] [midPointScheduler_Worker-4] INFO (com.evolveum.midpoint.expression): Object: user:bce98bd5-f8cd-4a6a-8488-4ae7ad369c0b(joe)                                                       uses keys: [A5yfWFgnD4euq3CgpdecmZPA/DE=]
2018-10-16 18:06:02,984 [MODEL] [midPointScheduler_Worker-4] INFO (com.evolveum.midpoint.expression): Object: user:db052966-ce05-486e-97cf-60deea8e4af6(newuser1)                                                  uses keys: [ZxwccL30e4UxM5hSOxYkaYJsUUM=]
2018-10-16 18:06:03,007 [] [midPointScheduler_Worker-4] INFO (com.evolveum.midpoint.repo.common.task.AbstractSearchIterativeTaskHandler): Finished Execute script (Task(id:1539705731263-0-1, name:List encryption keys, oid:b427848b-ea98-4da1-acba-c16cbb0e9c22)). Processed 45 objects in 0 seconds, got 0 errors. Average time for one object: 18.622223 milliseconds (wall clock time average: 20.933332 ms).

We can see that the user frank has an item with a cleartext value (presumably it is the password value). And all objects except for newuser1 use the key A5yfWFgnD4euq3CgpdecmZPA/DE= whereas newuser1 has the key of ZxwccL30e4UxM5hSOxYkaYJsUUM=. Because (in this example scenario) we have switched from the old encryption key to the new one, and newuser1 was created after this switch, we can reasonably assume that ZxwccL30e4UxM5hSOxYkaYJsUUM= is the new key, whereas A5yfWFgnD4euq3CgpdecmZPA/DE= is the old one.

Migrating the keys

The following task (adapted from samples/tasks/bulk-actions/task-reencrypt.xml) will reencrypt all objects in "dry mode", meaning that it will not execute anything; it will just write out the objects that would get reencrypted. You could limit the set of objects processed by using mext:objectType and mext:objectQuery items.

task-reencrypt.xml (dry run)
<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
      xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
      xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3"
      oid="172b3d9f-66f8-4731-b023-0e2d8ca5cd6f">
    <name>Reencrypt selected objects</name>
    <extension xmlns:mext="http://midpoint.evolveum.com/xml/ns/public/model/extension-3"
               xmlns:scext="http://midpoint.evolveum.com/xml/ns/public/model/scripting/extension-3">
        <scext:executeScript>
            <s:pipeline>
                <s:action>
                    <s:type>apply-definition</s:type>   <!-- needed for ResourceType and ShadowType objects -->
                </s:action>
                <s:action>
                    <s:type>reencrypt</s:type>
                    <!-- remove the following or set to false to execute the reencryption -->
                    <s:parameter>
                        <s:name>dryRun</s:name>
                        <value>true</value>
                    </s:parameter>
                </s:action>
            </s:pipeline>
        </scext:executeScript>
        <mext:useRepositoryDirectly>true</mext:useRepositoryDirectly>
<!--    <mext:objectType>UserType</mext:objectType>
        <mext:objectQuery>
            <q:filter>
                <q:equal>
                    <q:path>name</q:path>
                    <q:value>administrator</q:value>
                </q:equal>
            </q:filter>
        </mext:objectQuery>  -->
    </extension>
    <ownerRef oid="00000000-0000-0000-0000-000000000002"/>
    <executionStatus>runnable</executionStatus>
    <category>BulkActions</category>
    <handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/iterative-scripting/handler-3</handlerUri>
    <recurrence>single</recurrence>
</task>

The task (specifically, the reencrypt action) looks for objects that contain sensitive data stored in cleartext or encrypted by a key that is not the current one, and tries to re-encrypt them with the current encryption key.

The output is like this:

2018-10-16 18:21:12,390 [MODEL] [midPointScheduler_Worker-8] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Would reencrypt (this is dry run) systemConfiguration:00000000-0000-0000-0000-000000000001(SystemConfiguration): 1 modification(s)
2018-10-16 18:21:12,397 [MODEL] [midPointScheduler_Worker-8] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Would reencrypt (this is dry run) user:00000000-0000-0000-0000-000000000002(administrator): 1 modification(s)
2018-10-16 18:21:12,417 [MODEL] [midPointScheduler_Worker-8] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Would reencrypt (this is dry run) user:5e7bcc2a-84b6-465e-b656-e4cef28cf575(frank): 1 modification(s)
2018-10-16 18:21:12,419 [MODEL] [midPointScheduler_Worker-8] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Would reencrypt (this is dry run) resource:62a63be7-a5fb-4168-a389-ef69f8982a85(Basic Localhost OpenDJ): 1 modification(s)
2018-10-16 18:21:12,424 [MODEL] [midPointScheduler_Worker-8] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Would reencrypt (this is dry run) user:bce98bd5-f8cd-4a6a-8488-4ae7ad369c0b(joe): 1 modification(s)
2018-10-16 18:21:12,429 [] [midPointScheduler_Worker-8] INFO (com.evolveum.midpoint.repo.common.task.AbstractSearchIterativeTaskHandler): Finished Execute script (Task(id:1539706872170-0-1, name:Reencrypt selected objects, oid:172b3d9f-66f8-4731-b023-0e2d8ca5cd6f)). Processed 46 objects in 0 seconds, got 0 errors. Average time for one object: 0.67391306 milliseconds (wall clock time average: 1.8043479 ms).

Note that newuser1 is not among objects to be updated, as it already has the current encryption key applied.

Now let’s run the same task but with dryRun parameter removed, i.e.

task-reencrypt.xml (no dry run)
<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
      xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
      xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3"
      oid="172b3d9f-66f8-4731-b023-0e2d8ca5cd6f">
    <name>Reencrypt selected objects</name>
    <extension xmlns:mext="http://midpoint.evolveum.com/xml/ns/public/model/extension-3"
               xmlns:scext="http://midpoint.evolveum.com/xml/ns/public/model/scripting/extension-3">
        <scext:executeScript>
            <s:pipeline>
                <s:action>
                    <s:type>apply-definition</s:type>   <!-- needed for ResourceType and ShadowType objects -->
                </s:action>
                <s:action>
                    <s:type>reencrypt</s:type>
                </s:action>
            </s:pipeline>
        </scext:executeScript>
        <mext:useRepositoryDirectly>true</mext:useRepositoryDirectly>
    </extension>
    <ownerRef oid="00000000-0000-0000-0000-000000000002"/>
    <executionStatus>runnable</executionStatus>
    <category>BulkActions</category>
    <handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/iterative-scripting/handler-3</handlerUri>
    <recurrence>single</recurrence>
</task>

The result is like this:

2018-10-16 18:23:26,101 [MODEL] [midPointScheduler_Worker-9] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Reencrypted systemConfiguration:00000000-0000-0000-0000-000000000001(SystemConfiguration): 1 modification(s)
2018-10-16 18:23:26,115 [MODEL] [midPointScheduler_Worker-9] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Reencrypted user:00000000-0000-0000-0000-000000000002(administrator): 1 modification(s)
2018-10-16 18:23:26,134 [MODEL] [midPointScheduler_Worker-9] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Reencrypted user:5e7bcc2a-84b6-465e-b656-e4cef28cf575(frank): 1 modification(s)
2018-10-16 18:23:26,141 [MODEL] [midPointScheduler_Worker-9] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Reencrypted resource:62a63be7-a5fb-4168-a389-ef69f8982a85(Basic Localhost OpenDJ): 1 modification(s)
2018-10-16 18:23:26,157 [MODEL] [midPointScheduler_Worker-9] INFO (com.evolveum.midpoint.model.impl.scripting.ExecutionContext): Script console message: Reencrypted user:bce98bd5-f8cd-4a6a-8488-4ae7ad369c0b(joe): 1 modification(s)
2018-10-16 18:23:26,162 [] [midPointScheduler_Worker-9] INFO (com.evolveum.midpoint.repo.common.task.AbstractSearchIterativeTaskHandler): Finished Execute script (Task(id:1539707005824-0-1, name:Reencrypt selected objects, oid:172b3d9f-66f8-4731-b023-0e2d8ca5cd6f)). Processed 46 objects in 0 seconds, got 0 errors. Average time for one object: 2.2173913 milliseconds (wall clock time average: 3.1956522 ms).

Listing encryption keys after the action (optional)

Now let’s repeat the first step to see the currently used encryption keys. Just execute List encryption keys task again. The result will be like:

2018-10-16 18:24:36,916 [MODEL] [midPointScheduler_Worker-11] INFO (com.evolveum.midpoint.expression): Object: systemConfiguration:00000000-0000-0000-0000-000000000001(SystemConfiguration)                        uses keys: [ZxwccL30e4UxM5hSOxYkaYJsUUM=]
2018-10-16 18:24:36,936 [MODEL] [midPointScheduler_Worker-11] INFO (com.evolveum.midpoint.expression): Object: user:00000000-0000-0000-0000-000000000002(administrator)                                             uses keys: [ZxwccL30e4UxM5hSOxYkaYJsUUM=]
2018-10-16 18:24:37,082 [MODEL] [midPointScheduler_Worker-11] INFO (com.evolveum.midpoint.expression): Object: user:5e7bcc2a-84b6-465e-b656-e4cef28cf575(frank)                                                     uses keys: [ZxwccL30e4UxM5hSOxYkaYJsUUM=]
2018-10-16 18:24:37,090 [MODEL] [midPointScheduler_Worker-11] INFO (com.evolveum.midpoint.expression): Object: resource:62a63be7-a5fb-4168-a389-ef69f8982a85(Basic Localhost OpenDJ)                                uses keys: [ZxwccL30e4UxM5hSOxYkaYJsUUM=]
2018-10-16 18:24:37,096 [MODEL] [midPointScheduler_Worker-11] INFO (com.evolveum.midpoint.expression): Object: user:bce98bd5-f8cd-4a6a-8488-4ae7ad369c0b(joe)                                                       uses keys: [ZxwccL30e4UxM5hSOxYkaYJsUUM=]
2018-10-16 18:24:37,097 [MODEL] [midPointScheduler_Worker-11] INFO (com.evolveum.midpoint.expression): Object: user:db052966-ce05-486e-97cf-60deea8e4af6(newuser1)                                                  uses keys: [ZxwccL30e4UxM5hSOxYkaYJsUUM=]
2018-10-16 18:24:37,108 [] [midPointScheduler_Worker-11] INFO (com.evolveum.midpoint.repo.common.task.AbstractSearchIterativeTaskHandler): Finished Execute script (Task(id:1539705731263-0-1, name:List encryption keys, oid:b427848b-ea98-4da1-acba-c16cbb0e9c22)). Processed 46 objects in 0 seconds, got 0 errors. Average time for one object: 15.282609 milliseconds (wall clock time average: 16.652174 ms).

We can see that all objects use the new encryption key of ZxwccL30e4UxM5hSOxYkaYJsUUM=.

Notes

Instead of

<mext:useRepositoryDirectly>true</mext:useRepositoryDirectly>

it is possible to use:

<mext:searchOptions>
    <option>
        <options>
            <noFetch>true</noFetch>
        </options>
    </option>
</mext:searchOptions>

Both approaches should work.

Known limitations

The tasks mentioned here were not tested on ShadowType objects. These usually do not contain encrypted data (unless using e.g. attribute caching); however, maybe the pending operations could contain passwords. This is to be tested yet.

Schema-less items (e.g. improperly configured resources that contain encrypted data in the configuration section but there are no definitions available for those data) will not be processed. They will not be listed among encryption keys used nor reencrypted.