How to encrypt and decrypt using TPM EK

0

I am using TPM Java library provided by Microsoft. Connection is working for simulator. Stuck in how to encrypt string using TPM Endrosment public key and decrypt using TPM Endrosment Private key.

Below is the code which is incorrect functionality, Where TPM private EK is not visible outside world then how to decrypt using private EK.

public class Sample {

    boolean usesTbs;
    Tpm tpm;
    Cipher cipher;
    public static byte[] nullVec = new byte[36];
    private static final String SHA_256 = "SHA-256";
    private static final String EQUALS = "=";
    private final String registrationId;

    private TPMT_PUBLIC ekPublic = null;
    private TPMT_PUBLIC srkPublic = null;
    private TPM2B_PUBLIC idKeyPub = null;

    private static final TPM_HANDLE SRK_PERSISTENT_HANDLE = TPM_HANDLE.persistent(0x00000001);
    private static final TPM_HANDLE EK_PERSISTENT_HANDLE = TPM_HANDLE.persistent(0x00010001);
    private static final TPM_HANDLE ID_KEY_PERSISTENT_HANDLE = TPM_HANDLE.persistent(0x00000100);
    private static final TPMT_SYM_DEF_OBJECT AES_128_SYM_DEF = new TPMT_SYM_DEF_OBJECT(TPM_ALG_ID.AES, 128, TPM_ALG_ID.CFB);

    private static final TPMT_PUBLIC EK_TEMPLATE = new TPMT_PUBLIC(
            // TPMI_ALG_HASH    nameAlg
            TPM_ALG_ID.SHA256,
            // TPMA_OBJECT  objectAttributes
            new TPMA_OBJECT(TPMA_OBJECT.restricted, TPMA_OBJECT.decrypt, TPMA_OBJECT.fixedTPM, TPMA_OBJECT.fixedParent,
                    TPMA_OBJECT.adminWithPolicy, TPMA_OBJECT.sensitiveDataOrigin),
            // TPM2B_DIGEST authPolicy
            javax.xml.bind.DatatypeConverter.parseHexBinary("837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa"),
            // TPMU_PUBLIC_PARMS    parameters
            new TPMS_RSA_PARMS(AES_128_SYM_DEF, new TPMS_NULL_ASYM_SCHEME(), 2048, 0),
            // TPMU_PUBLIC_ID       unique
            new TPM2B_PUBLIC_KEY_RSA());

    private static final TPMT_PUBLIC SRK_TEMPLATE = new TPMT_PUBLIC(
            // TPMI_ALG_HASH    nameAlg
            TPM_ALG_ID.SHA256,
            // TPMA_OBJECT  objectAttributes
            new TPMA_OBJECT(TPMA_OBJECT.restricted, TPMA_OBJECT.decrypt, TPMA_OBJECT.fixedTPM, TPMA_OBJECT.fixedParent,
                    TPMA_OBJECT.noDA, TPMA_OBJECT.userWithAuth, TPMA_OBJECT.sensitiveDataOrigin),
            // TPM2B_DIGEST authPolicy
            new byte[0],
            // TPMU_PUBLIC_PARMS    parameters
            new TPMS_RSA_PARMS(AES_128_SYM_DEF, new TPMS_NULL_ASYM_SCHEME(), 2048, 0),
            // TPMU_PUBLIC_ID       unique
            new TPM2B_PUBLIC_KEY_RSA());

    public TpmSample() throws Exception {
        System.out.println("===> PATH = " + System.getenv("PATH"));
        System.out.println("===> path = " + System.getenv("path"));

        usesTbs = CmdLine.isOptionPresent("tbs", "t");
        System.out.println("Connecting to " + (usesTbs ? "OS TPM" : "TPM Simulator"));
        tpm = usesTbs ? TpmFactory.platformTpm() : TpmFactory.localTpmSimulator();

        clearPersistent(tpm, EK_PERSISTENT_HANDLE, "EK");
        clearPersistent(tpm, SRK_PERSISTENT_HANDLE, "SRK");
        ekPublic = createPersistentPrimary(tpm, EK_PERSISTENT_HANDLE, TPM_RH.OWNER, EK_TEMPLATE, "EK");
        srkPublic = createPersistentPrimary(tpm, SRK_PERSISTENT_HANDLE, TPM_RH.OWNER, SRK_TEMPLATE, "SRK");
        //SRS_SecurityProviderTPMEmulator_25_002: [ The constructor shall set the registration Id to null if none was provided. ]
        this.registrationId = null;
    }

    private void cleanSlots(TPM_HT slotType) {
        GetCapabilityResponse caps = tpm.GetCapability(TPM_CAP.HANDLES, slotType.toInt() << 24, 8);
        TPML_HANDLE handles = (TPML_HANDLE) caps.capabilityData;

        if (handles.handle.length == 0)
            System.out.println("No dangling " + slotType.name() + " handles");
        else
            for (TPM_HANDLE h : handles.handle) {
                System.out.printf("Dangling " + slotType.name() + " handle 0x%08X\n", h.handle);
                tpm.FlushContext(h);
            }
    }

    public byte[] getEndrosmentPrivateKey() {
        return (new TPM2B_PRIVATE()).toTpm();
    }

    public byte[] getEndorsementKey() {
        //SRS_SecurityProviderTPMEmulator_25_032: [ This method shall return the TPM2B_PUBLIC form of EK. ]
        return (new TPM2B_PUBLIC(ekPublic)).toTpm();
    }

    public byte[] getStorageRootKey() {
        //SRS_SecurityProviderTPMEmulator_25_033: [ This method shall return the TPM2B_PUBLIC form of SRK. ]
        return (new TPM2B_PUBLIC(srkPublic)).toTpm();
    }

    private TPMT_PUBLIC createPersistentPrimary(Tpm tpm, TPM_HANDLE hPersistent, TPM_RH hierarchy, TPMT_PUBLIC inPub, String primaryRole) throws Exception {
        ReadPublicResponse rpResp = tpm._allowErrors().ReadPublic(hPersistent);
        if (rpResp == null) {
            throw new Exception("ReadPublicResponse cannot be null");
        }
        TPM_RC rc = tpm._getLastResponseCode();

        if (rc == TPM_RC.SUCCESS) {
            // TODO: Check if the public area of the existing key matches the requested one
            return rpResp.outPublic;
        }
        if (rc != TPM_RC.HANDLE) {
            throw new Exception("Unexpected failure {" + rc.name() + "} of TPM2_ReadPublic for {" + primaryRole + "}");
        }

        TPMS_SENSITIVE_CREATE sens = new TPMS_SENSITIVE_CREATE(new byte[0], new byte[0]);
        CreatePrimaryResponse cpResp = tpm.CreatePrimary(TPM_HANDLE.from(hierarchy), sens, inPub,
                new byte[0], new TPMS_PCR_SELECTION[0]);
//        System.out.println("RSA Primary Key: \n" + cpResp.toString());
        if (cpResp == null) {
            throw new Exception("CreatePrimaryResponse cannot be null");
        }

        tpm.EvictControl(TPM_HANDLE.from(TPM_RH.OWNER), cpResp.handle, hPersistent);
        tpm.FlushContext(cpResp.handle);
        return cpResp.outPublic;
    }

    private void clearPersistent(Tpm tpm, TPM_HANDLE hPersistent, String keyRole) throws Exception {
        tpm._allowErrors().ReadPublic(hPersistent);
        TPM_RC rc = tpm._getLastResponseCode();
        if (rc == TPM_RC.SUCCESS) {
            tpm.EvictControl(TPM_HANDLE.from(TPM_RH.OWNER), hPersistent, hPersistent);
        } else if (rc != TPM_RC.HANDLE) {
            throw new Exception("Unexpected failure for {" + rc.name() + "} of TPM2_ReadPublic for " + keyRole + " 0x" + hPersistent.handle);
        }
    }

    public SecretKeySpec setKey(byte[] key) {

        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance(SHA_256);
            key = digest.digest(key);
            key = Arrays.copyOf(key, 16);
            return new SecretKeySpec(key, "AES");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    public byte[] encrypt(String data, byte[] publicEKey) throws Exception {
        SecretKeySpec secretKeySpec = setKey(publicEKey);
        cipher = Cipher.getInstance("AES/CFB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(new byte[16]));
        byte[] cipherText = cipher.doFinal(data.getBytes("UTF-8"));
        System.out.println("Encrypted Data from Method:->" + new String(cipherText, "UTF-8"));
        return cipherText;
    }

    public byte[] decrypt(byte[] cipherText, byte[] privateEKey) throws Exception {
        SecretKeySpec secretKeySpec = setKey(privateEKey);
        Cipher cipherDecrypt = Cipher.getInstance("AES/CFB/NoPadding");
        cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(new byte[16]));
        byte[] decipheredText = cipher.doFinal(cipherText);
        System.out.println("Decrypted Data from Method:-> " + new String(decipheredText));
        return decipheredText;
    }


    public static void main(String[] args) throws Exception {
        String data = "asdfqwert";
        Sample tpmSample = new Sample();
        tpmSample.cleanSlots(TPM_HT.TRANSIENT);
        tpmSample.cleanSlots(TPM_HT.LOADED_SESSION);
        tpmSample.getTpmInformation();
        System.out.println("Endrosment Key :->" + new String(org.bouncycastle.util.encoders.Base64.encode(tpmSample.getEndorsementKey())));
        System.out.println("Private Endrosment Key :->" + new String(org.bouncycastle.util.encoders.Base64.encode(tpmSample.getEndrosmentPrivateKey())));

        byte[] publicEKey = new String(Base64.encode(tpmSample.getEndorsementKey())).getBytes("UTF-8");
        byte[] privateEKey = new String(Base64.encode(tpmSample.getEndrosmentPrivateKey())).getBytes("UTF-8");

        System.out.println("Original Data ->" + data);
        byte[] enc = tpmSample.encrypt(data, publicEKey);
        tpmSample.decrypt(enc, privateEKey);
    }
}
java
encryption
tpm
asked on Stack Overflow Dec 17, 2020 by Sriram

1 Answer

1

The Endorsement Key(EK) is not inteded to be used directly for encryption. Instead, you are expected to create a StorageKey(SK) or Storage Key Hierarchy using a Primary Key. Then, create another key for encryption and use that key to encryption portion of your hard drive or file.

This can be achieved using TPM2 Seal/Unseal. If it is a matter of a single string you could cheat away using TPM2 Encrypt/Decrypt, but that's not recommended. Better to have a file with the secret and encrypt that file.

What I could recommend is to check this out for some foundation about TPM https://google.github.io/tpm-js/ and here is a nice discussion about the difference between using Seal/Unseal and EncryptDecrypt https://developers.tpm.dev/posts/8628948

These two resources should clear your confusion and then it is a matter of writing code. I am not sure only if Java and the Microsoft TPM2.0 stack are the quickest way to achieve what you want, but once you get the concept right, then it is really just implementation :)

answered on Stack Overflow Jan 10, 2021 by dimitom

User contributions licensed under CC BY-SA 3.0