AEM Encryption 101: How to Encrypt a Shared Secret

by Curtis Mortensen

Oct 02 , 2018

I recently had a request from a client who wanted to use a third party service that didn’t provide an authentication key. Instead I need to use a simple username and password combo that the client didn’t want exposed in our code repository where we generally keep our configuration values.

Having heard about encrypted configuration values, I set out to learn how to use them. I found a variety of resources out there, but none of them really walked me through from Point A to Point B. I had to kind of make some logical leaps to figure out what to do. It turns out that this is really straightforward once you know what to do.

First things first, take the value you want to encrypt and open up your system console to the Crypto Support tab (/system/console/crypto). It should look like this:

Just put the value you want to keep safe in the “Plain Text” field and click the Protect button. You’ll get back a value that looks something like this {89cb4befeb375b865332fed9bbde85b8842a2318be4901851c3b5a0495c49f13dad8b4cfd38a6fa26f07ea361ea4994e}

There is one “gotcha” to be aware of. When you encrypt the password using the Crypto Support tool, the encryption is specific to the instance you are running. If you want to re-use that same encrypted value, you will need to copy the crypto keys as helpfully outlined here by Nate Yolles:  http://www.nateyolles.com/blog/2017/05/sharing-crypto-keys-in-aem-63

You’ll want to copy that encrypted value and place it in your configuration file. It might look something like this:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="sling:OsgiConfig"
          username="mySampleUsername"
          encryptedpassword="\{89cb4befeb375b865332fed9bbde85b8842a2318be4901851c3b5a0495c49f13dad8b4cfd38a6fa26f07ea361ea4994e}"/>

Next you’ll want a service that pulls these configuration values in and makes them available for interaction with your third party application. Nate Yolles wrote an excellent and very helpful blog on how to use OSGi annotations to do this (http://www.nateyolles.com/blog/2017/05/osgi-declarative-services-annotations-in-aem) so for our purposes, we’ll just note that your service will look something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.example.servlets;
import com.adobe.granite.crypto.CryptoException;
import com.adobe.granite.crypto.CryptoSupport;
@Component(immediate=true,
        service=Servlet.class,
        property={
                Constants.SERVICE_DESCRIPTION + "=Example Servlet",
                "sling.servlet.methods=" + HttpConstants.METHOD_POST
        }
)
@Designate(ocd=ExampleServlet.Configuration.class)
public class ExampleServlet extends SlingAllMethodsServlet {
    private String apiPassword;
    private String apiUsername;
    protected CryptoSupport cryptoSupport;
    @Reference
    public void bindCryptoSupport(CryptoSupport cryptoSupport) {
        this.cryptoSupport = cryptoSupport;
    }
    public void unbindCryptoSupport(CryptoSupport cryptoSupport) {
        this.cryptoSupport = cryptoSupport;
    }
    @Override
    protected void doPost(final SlingHttpServletRequest req, final SlingHttpServletResponse resp) throws ServletException, IOException  {
        // some code here
        String username = apiUsername;
        String password = getPassword();
        someAPIFunction(username, password);
        // more code here
    }
    private String getPassword() {
        String passwd = "";
        try {
            if (this.cryptoSupport.isProtected(apiPassword)) {
                passwd = this.cryptoSupport.unprotect(apiPassword);
            } else if (apiPassword.length() > 0) {
                passwd = apiPassword;
            }
        } catch (CryptoException ex) {
            logger.error("A helpful log message");
        } catch (NullPointerException ex) {
            logger.error("A helpful null pointer exception message");
        }
        return passwd;
    }
     @Activate
    @Modified
    protected void Activate(Configuration config) {
        apiPassword = config.encryptedpassword();
        apiUsername = config.username();
    }
    @ObjectClassDefinition(name = "Example Servlet - OSGi")
    public @interface Configuration {
        @AttributeDefinition(
            name = "API Password",
            description = "Pull the API password from the configuration settings",
                type = AttributeType.STRING
        )
        String encryptedpassword() default "";
        @AttributeDefinition(
                name = "API Username",
                description = "Pull the API username from the configuration settings",
                type = AttributeType.STRING
        )
        String username() default "";
    }

Note that although I’m checking in the getPassword() function to make sure I’m decrypting properly, in my experience, this just works. My service doesn’t actually require me to make the “unprotect” call, but I left it here just in case it is helpful to anyone else.

It really is simple once you know what you’re doing and in retrospect, I was surprised that it took me as long as it did to figure out.

Author Bio

Curtis Mortensen is a Senior AEM Architect at R2integrated. He has been working in the web application space for over fifteen years. He’s earned a Bachelors in Computer Engineering from the University of Utah and has worked at a variety of software development positions from startups to huge global agencies. He started working with AEM over six years ago and has enjoyed building implementations for a diverse set of clients. Along the way, Curtis earned the AEM Developer and AEM Architect certifications from Adobe.

He and his wife have adopted four kids, three dogs and a changing number of rabbits. When that doesn’t keep him busy, he enjoys reading (mostly fantasy novels) and playing video games.

R2i accelerates customer connections. In a complex world that’s always demanding more, R2i is a digital agency that helps marketers create more awareness, build deeper relationships, and drive measurable impact.

Tags: AEM | Adobe Experience Manager | Experience Manager | Adobe CQ | Encryption