May 15, 2020
Estimated Post Reading Time ~

AEM Encryption 101: How to Encrypt a Shared Secret

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 to be 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:

<?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:

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.


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.