aws-samples / aws-cloudhsm-jce-examples

Sample applications demonstrating how to use the CloudHSM JCE
MIT No Attribution
37 stars 57 forks source link

KeyStore should return only one key for the provided alias aesAlias #76

Closed maxchev closed 9 months ago

maxchev commented 1 year ago

Hi, I am using the examples in here to try to see if cloud HSM maes sense for me. Stating with a simple use case, I simply want to be able to store an AES key in a key store and retreive it.

I obviously wantrto cipher / decipher, but I am facing an issue before that.

After initial keystore and key creation, I reload a brand new keystore and want to use that one to "prove" I can retreive an previsously stored key.

I am able to see the alias I just created, but when I try to "getKey()", I am getting this error:

Exception in thread "main" java.security.UnrecoverableKeyException: KeyStore should return only one key for the provided alias aesAlias

Any Idea what I am doing wrong ?

Note: in order for the code tu run I had to add the VM arg as suggested here.

package com.redacted.commons.tests;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.util.Enumeration;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import com.amazonaws.cloudhsm.jce.jni.exception.AddAttributeException;
import com.amazonaws.cloudhsm.jce.provider.CloudHsmProvider;
import com.amazonaws.cloudhsm.jce.provider.attributes.KeyAttribute;
import com.amazonaws.cloudhsm.jce.provider.attributes.KeyAttributesMap;

public class AwsCloudHSMTest2 {

    static String keyStorePath = "D:\\Temp\\AwsCloudHsm\\awskeystore2.awks";
    static String keyStorePassword = "password";
    static String awsUser = "user";
    static String awsPassword = "password";

    static String aesAlias = "aesAlias";
    static String rsaAlias = "rsaAlias";

    public static void main(String[] args) throws Exception {

        System.out.println("Start");

        System.out.println("Listing Keys / login");

        System.setProperty("HSM_USER", awsUser);
        System.setProperty("HSM_PASSWORD", awsPassword);
        Security.addProvider(new CloudHsmProvider());

        System.out.println("Creating keystore if it doesnt exist");

        KeyStore ks = null;

        try {

            ks = KeyStore.getInstance(CloudHsmProvider.PROVIDER_NAME);
            ks.load(null, keyStorePassword.toCharArray());

        } catch (Exception e) {
            e.printStackTrace();
            System.err.println(e);

        }

        System.out.println("Creating AES Key");

        SecretKey sk = (SecretKey) generateAESKey(256, aesAlias);

        System.out.println("sk.getAlgorithm(): " + sk.getAlgorithm());

        // here I will be doing ciphering

        ks.setKeyEntry(aesAlias, sk, keyStorePassword.toCharArray(), null);

        System.out.println("AES Key set in KS - now saving");

        // saving the keystore

        FileOutputStream out = null;

        try {

            out = new FileOutputStream(keyStorePath);
            ks.store(out, keyStorePassword.toCharArray());
            out.close();

        } catch (Exception e) {
            e.printStackTrace();
            System.err.println(e);

        }

        // useless, but I like to see that anyways

        ks = null;
        sk = null;

        // reading key from keystore

        KeyStore ks2 = loadKeyStore(keyStorePath, keyStorePassword);

        listKeys(ks2);

        Key retrievedKey = ks2.getKey(aesAlias, null);

        System.out.println("Retrieved AES Key");
        System.out.println("sk2.getAlgorithm(): " + retrievedKey.getAlgorithm());

        // here I will be doing deciphering

    }

    public static Key generateAESKey(int keySizeInBits, String keyLabel) throws InvalidAlgorithmParameterException,
            NoSuchAlgorithmException, NoSuchProviderException, AddAttributeException {
        return generateAESKey(keySizeInBits, keyLabel, new KeyAttributesMap());
    }

    public static Key generateAESKey(int keySizeInBits, String keyLabel, KeyAttributesMap aesSpecKeyAttributes)
            throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException,
            AddAttributeException {

// Create an Aes keygen Algorithm parameter spec using KeyAttributesMap
        final KeyAttributesMap aesSpec = new KeyAttributesMap();

        aesSpec.putAll(aesSpecKeyAttributes);
        aesSpec.put(KeyAttribute.LABEL, keyLabel);
        aesSpec.put(KeyAttribute.SIZE, keySizeInBits);

        KeyGenerator keyGen = KeyGenerator.getInstance("AES", CloudHsmProvider.PROVIDER_NAME);
        keyGen.init(aesSpec);
        SecretKey aesKey = keyGen.generateKey();

        return aesKey;
    }

    public static KeyStore loadKeyStore(String path, String password) throws KeyStoreException {

        final KeyStore keyStore = KeyStore.getInstance(CloudHsmProvider.CLOUDHSM_KEYSTORE_TYPE);
        try {
            final FileInputStream instream = new FileInputStream(new File(path));
            // This call to keyStore.load() will open the CloudHSM keystore file with the
            // supplied
            // password.
            keyStore.load(instream, password.toCharArray());
        } catch (final IOException | NoSuchAlgorithmException | CertificateException ex) {
            System.err.println("Keystore not found, loading an empty store");
        }

        return keyStore;
    }

    public static void saveKeyStore(String path, String password, KeyStore keyStore) {

        FileOutputStream out = null;

        try {

            out = new FileOutputStream(path);
            keyStore.store(out, password.toCharArray());
            out.close();

        } catch (Exception e) {

            System.err.println(e);

        }

    }

    public static void listKeys(KeyStore keyStore) throws Exception {

        final Enumeration<String> aliases = keyStore.aliases();

        System.out.println("Listing KS ");
        while (aliases.hasMoreElements()) {

            String alias = (String) aliases.nextElement();

            System.out.println("Alias: " + alias);

        }

    }

}
narasimhaks commented 1 year ago

I believe it is because the keys that are generated are not being persistent. You will have to set the KeyAttribute.Token to true in the KeyAttributesMap.

rday commented 9 months ago

Hi @maxchev !

This error occurs when the HSM contains multiple keys (session or token) that have the same label property. The JCE KeyStore only allows a single key to be returned. If we detect multiple keys, we throw the exception.

You can use our SDK5 CLI to find these keys: https://docs.aws.amazon.com/cloudhsm/latest/userguide/cloudhsm_cli-key-list.html.

To fix this error, we recommend assigning each key a unique label using the set-attribute command: https://docs.aws.amazon.com/cloudhsm/latest/userguide/cloudhsm_cli-key-set-attribute.html

zeeshan-abid commented 3 months ago

hi @rday ,

I can only see the key-reference using the CLI command but I want to get the key using the key-reference/handle so Is there any way to get the key-reference or handle after key generation using the JCE?

Thanks

Regards, Zeeshan

Thanks

kladd commented 3 months ago

As of SDK version 5.12 this is possible by using KeyStoreWithAttributes with a KeyReferenceSpec. See the sample here: https://github.com/aws-samples/aws-cloudhsm-jce-examples/blob/sdk5/src/main/java/com/amazonaws/cloudhsm/examples/KeyStoreExampleRunner.java#L297-L315.

zeeshan-abid commented 3 months ago

@kladd , thanks for the reply but I already have seen this example to retrieve the key using the key-reference but I need that reference at the time of key generation then later on, I can get the get using that key-reference/handle.

kladd commented 3 months ago

@zeeshan-abid, My mistake. In that case the CLI is the only way to retrieve the key reference right now. The JCE SDK does not have a method to retrieve it.