Skip to content
generated from heaths/template-golang

Cryptography client for Azure Key Vault SDK for Go

License

Notifications You must be signed in to change notification settings

heaths/azcrypto

Repository files navigation

Cryptography Client for Azure Key Vault

reference ci codecov

This module provides a cryptography client for the Azure Key Vault Keys client module for Go. This project is not supported by the Azure SDK team, but does align with the cryptography clients in other supported languages like the CryptographyClient I wrote for the Azure SDK for .NET. See related projects for more information.

Getting started

Install packages

Install azcrypto and azidentity with go get:

go get github.com/heaths/azcrypto
go get github.com/Azure/azure-sdk-for-go/sdk/azidentity

azidentity is used for Azure Active Directory authentication as demonstrated below.

Prerequisites

Authentication

This document demonstrates using azidentity.NewDefaultAzureCredential to authenticate. This credential type works in both local development and production environments. We recommend using a managed identity in production.

NewClient accepts any azcore.TokenCredential including those from azidentity. See the azidentity documentation for more information about other credential types.

Create a client

Constructing the client requires your key's URL, called a key ID, which you can get from the Azure Portal or Azure CLI. You should store this key ID including the version used to encrypt, sign, or wrap with your data to make sure you can decrypt, verify, and unwrap.

import (
    "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    "github.com/heaths/azcrypto"
)

func main() {
    cred, err := azidentity.NewDefaultAzureCredential(nil)
    if err != nil {
        // TODO: handle error
    }

    client, err := azcrypto.NewClient(
        "https://{vault-name}.vault.azure.net/keys/{key-name}/{key-version}",
        cred,
        nil,
    )
    if err != nil {
        // TODO: handle error
    }
}

You can also create a Client from a JSON Web Key (JWK) using NewClientFromJSONWebKey. No attempt to connect to Azure Key Vault or Managed HSM will be made.

Key concepts

The Client will attempt to download the specified public key when first used. This will improve throughput while reducing the risk of getting throttled by Key Vault's rate limits.

Encrypt and decrypt

You can encrypt with a public key, but decryption requires a private key to which Azure Key Vault or Managed HSM does not provide access. The following encryption operation will be performed locally if the caller has the keys/get data action permission, and decrypted remotely.

import (
    "context"

    "github.com/heaths/azcrypto"
)

func encryptAndDecrypt(client *azcrypto.Client, plaintext string) (string, error) {
    encryptResult, err := client.Encrypt(
        context.TODO(),
        azcrypto.EncryptAlgorithmRSAOAEP256,
        []byte(plaintext),
        nil,
    )
    if err != nil {
        return "", err
    }

    decryptResult, err := client.Decrypt(
        context.TODO(),
        azcrypto.EncryptAlgorithmRSAOAEP256,
        encryptResult.Ciphertext,
        nil,
    )
    if err != nil {
        return "", err
    }

    return string(decryptResult.Plaintext), nil
}

Sign and verify

You can verify a signature with a public key, but signing requires a private key to which Azure Key Vault or Managed HSM does not provide access. The following signing operation will be performed on Key Vault or Managed HSM, but verification will be performed locally if the caller has the keys/get data action permission.

import (
    "context"
    "crypto/sha256"

    "github.com/heaths/azcrypto"
)

func signAndVerify(client *azcrypto.Client, plaintext string) (bool, error) {
    // Performed remotely by Azure Key Vault or Managed HSM.
    signResult, err := client.SignData(
        context.TODO(),
        azcrypto.SignAlgorithmES256,
        []byte(plaintext),
        nil,
    )
    if err != nil {
        return false, err
    }

    // Performed locally if the public key could be retrieved.
    verifyResult, err := client.VerifyData(
        context.TODO(),
        signResult.Algorithm,
        []byte(plaintext),
        signResult.Signature,
        nil,
    )
    if err != nil {
        return false, err
    }

    return verifyResult.Valid, nil
}

Key wrap and unwrap

You can wrap a key - typically a key used for symmetric algorithms such as AES - with a public key, but unwrapping the key requires a private key to which Azure Key Vault or Managed HSM does not provide access. The following wrapping operation will be performed locally if the caller has the keys/get data action permission, and unwrapped remotely on Key Vault or Managed HSM.

This is useful for keeping symmetric algorithm keys such as AES secure while taking advantage of symmetric keys' speed for use in block ciphers.

import (
    "context"

    "github.com/heaths/azcrypto"
)

func wrapAndUnwrapKey(client *azcrypto.Client, key []byte) ([]byte, error) {
    wrappedKey, err := client.WrapKey(
        context.TODO(),
        azcrypto.WrapKeyAlgorithmRSAOAEP256,
        key,
        nil,
    )
    if err != nil {
        return nil, err
    }

    unwrappedKey, err := client.Decrypt(
        context.TODO(),
        azcrypto.WrapKeyAlgorithmRSAOAEP256,
        wrappedKey.EncryptedKey,
        nil,
    )
    if err != nil {
        return nil, err
    }

    return unwrappedKey.Key, nil
}

Examples

Get started with our examples.

Related projects

Though not officially part of the Azure SDKs, this module was developed using the same guidelines. Convenience methods have been added to help call cryptographic key operations correctly, as well as support for caching public keys when permitted, and using a JWK in scenarios it may be stored as an Azure Key Vault secret.

For more information about the other Key Vault SDKs, see https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/security/keyvault or, for individual packages:

License

Licensed under the MIT license.