HashiCorp Vault for Algorand key signing

Hello all, I just deployed a HashiCorp Dedicated Vault to use for Secure Enclave transaction signing. If anyone has already done this and can provide high-level tips that would be awesome. My goal is to create a solution for programmatically generating wallets and signing transactions. The idea for this came from an excellent article about Enterprise KMS on Algorand that you can find here - Overcoming the biggest challenge in enterprise adoption of Web3 technology. I’ll be updating this post with my progress. Update: I’ve made it all the way to the ed25519 key import step, but no matter how I encode the cyphertext, it returns this error: “Code: 500
Errors:
1 error occurred:
* provided type does not support public_key import”. Which is odd because I am not importing the publicKey. The cyphertext is just the wrapping_key and encoded_privatekey pem. No matter how I encode the pem it thinks I am trying to import the publicKey. Any ideas? This is the current encoding script I am using:

const crypto = require("crypto");

const privateKeyBase64 = "<ALGORAND_PRIVATE_KEY>";
const wrappingKeyPEM = `-----BEGIN PUBLIC KEY-----
<YOUR_WRAPPING_KEY>
-----END PUBLIC KEY-----`;

function generateAESKey() {
    return crypto.randomBytes(32); // 256-bit AES key
}

function encryptWithAES(aesKey, privateKey) {
    const cipher = crypto.createCipheriv("aes-256-cbc", aesKey, Buffer.alloc(16, 0));
    let encrypted = cipher.update(privateKey);
    encrypted = Buffer.concat([encrypted, cipher.final()]);
    return encrypted;
}

function encryptAESKeyWithWrappingKey(wrappingKeyPEM, aesKey) {
    return crypto.publicEncrypt(
        {
            key: wrappingKeyPEM,
            padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
            oaepHash: "sha256",
        },
        aesKey
    );
}

function createCiphertext(encryptedAESKey, encryptedPrivateKey) {
    return Buffer.concat([encryptedAESKey, encryptedPrivateKey]);
}

function main() {
    try {
        const privateKeyBuffer = Buffer.from(privateKeyBase64, "base64");

        // Generate AES Key
        const aesKey = generateAESKey();

        // Encrypt private key with AES
        const encryptedPrivateKey = encryptWithAES(aesKey, privateKeyBuffer);

        // Encrypt AES key with Vault's wrapping key
        const encryptedAESKey = encryptAESKeyWithWrappingKey(wrappingKeyPEM, aesKey);

        // Combine the AES-encrypted private key and RSA-encrypted AES key
        const ciphertext = createCiphertext(encryptedAESKey, encryptedPrivateKey);

        console.log(
            `vault write transit/keys/algorand-signing-key/import \\ ciphertext="${ciphertext.toString("base64")}" \\ hash_function="SHA256"`
        );
    } catch (error) {
        console.error("Error:", error.message);
    }
}

main();

Setting up the vault to sign transactions was relatively easy. But I’m running into issues attaching the returned signatures to the unsigned transactions.

1 Like

Signing inside the Enclave is crucial. Simply managing the keys and not signing will not provide complete security.

1 Like

One cool feature about Dedicated HashiCorp Vault is that it’s an all-in-one solution. No need for individual AWS or anything else.

Ok this is a lot more complicated than I thought it would be. But I’ve almost got it. Will probably make a guide when done.

2 Likes

I don’t personally have experience on this, but have you checked out the Hashi repo from Algorand Foundation? It may be able to do what you’re attempting.

1 Like

Nullen you rock! I think this is exactly what I was looking for. Will update here when I give it a shot.

Is there a reason people aren’t talking about this more? From what I have seen so far it’s super cool and WAY easier than trying to set up AWS enclaves.

OK I made it past the ed25519 import step. I wrote a script to generate the HashiCorp prompt for importing keys with the correct syntax. Will include it in the library when this is done. I’m on the final step now and totally stuck again. For some reason, I can’t get the Vault Signatures to line up with what the transaction is expecting when I go to submit it. The main challenge is manually constructing the message pack for the transaction from the Vault signature and the unsigned transaction. I had initially hoped to use Algo Models for this but Algo Models has some good examples for creating transactions, but there are not any full examples that include actually submitting the transaction. So if I make the transaction with Algo Models and try to submit with Algod it won’t let me. Maybe there is another step here I am missing still.