Cumulocity IoT Tenant Options Encryption

What product/components do you use and which version/fix level?

Cumulocity IoT

What are trying to achieve? Please describe in detail.

I want to save a secret password in the Cumulocity tenant options out of a microservice. How do I encrypt/decrypt it? Is it done with a custom script?

It looks like there is an example of a custom solution here: cumulocity-clients-java/EncryptionService.java at develop · SoftwareAG/cumulocity-clients-java · GitHub

/*
 * Copyright (c) 2012-2020 Cumulocity GmbH
 * Copyright (c) 2020-2022 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors.
 *
 * Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG.
 */

package com.cumulocity.lpwan.tenant.option;

import java.nio.charset.Charset;

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.stereotype.Component;

import com.google.common.base.Charsets;

@Component
public class EncryptionService {
    public static final String CIPHER = "{cipher}";

    private static final Charset CHARSET = Charsets.UTF_8;
    private static final StringKeyGenerator KEY_GENERATOR = KeyGenerators.string();

    public String decryptString(String encrypted, String password) throws DecryptFailedException {
        if (encrypted == null) {
            throw new DecryptFailedException("Cannot decrypt empty string");
        }
        if (!isEncrypted(encrypted)) {
            throw new DecryptFailedException("Cannot decrypt unencrypted string");
        }
        String withoutPrefix = withoutPrefix(encrypted);
        String salt = extractSalt(withoutPrefix);
        String key = extractKey(withoutPrefix);
        try {
            return Encryptors.text(password, salt).decrypt(key);
        } catch (IllegalStateException e) {
            throw new DecryptFailedException("Decrypting of string failed", e);
        }
    }

    public String encryptString(String plain, String password) {
        String salt = KEY_GENERATOR.generateKey();
        String hash = Encryptors.text(password, salt).encrypt(plain);
        return CIPHER + mergeSalt(hash, salt);
    }

    public boolean isEncrypted(String encrypted) {
        if (encrypted.startsWith(CIPHER)) {
            return true;
        }
        return false;
    }

    private String mergeSalt(String encrypted, String salt) {
        String merged = encrypted + salt;
        return new String(Base64.encode(merged.getBytes(CHARSET)), CHARSET);
    }

    private String extractSalt(String encrypted) {
        String decoded = new String(Base64.decode(encrypted.getBytes(CHARSET)));
        return StringUtils.right(decoded, 16);
    }

    private String extractKey(String encrypted) {
        String decoded = new String(Base64.decode(encrypted.getBytes(CHARSET)));
        return decoded.substring(0, decoded.length() - 16);
    }

    private String withoutPrefix(String prefixedString) {
        return prefixedString.substring(CIPHER.length());
    }
}

Is there a way to leave the encoding and decoding up to Cumulocity somehow? A scenario to make it available only if the credentials, that are trying to get it, have a specific role.

1 Like

Yes the tenant options API provides built in encyption and decryption functionalities as documented here: Cumulocity IoT - OpenAPI Specification

If your option key is prefixed with credentials. the platform will automatically encrypt the value upon receiving it. This option will not be accessible in clear text form by regular users. Only microservices and an other system users may access the decrypted versions of these options.

3 Likes

As per API, all you need to do is prefix the name of the option with “credentials” then it will be automatically encrypted for the microservice that has created it

Cumulocity IoT - OpenAPI Specification

Encrypted credentials

Adding a “credentials.” prefix to the key will make the value of the option encrypted. When the option is sent to a microservice, the “credentials.” prefix is removed and the value is decrypted. For example:

{
  "category": "secrets",
  "key": "credentials.mykey",
  "value": "myvalue"
}

In that particular example, the request will contain an additional header "Mykey": "myvalue" .

2 Likes