Introduction to AzureKeyVault

Azure Key Vault enables Microsoft Azure applications and users to store and use several types of secret/key data:

  • Cryptographic keys: Supports multiple key types and algorithms, and enables the use of Hardware Security Modules (HSM) for high value keys.
  • Secrets: Provides secure storage of secrets, such as passwords and database connection strings.
  • Certificates: Supports certificates, which are built on top of keys and secrets and add an automated renewal feature.
  • Azure Storage: Can manage keys of an Azure Storage account for you. Internally, Key Vault can list (sync) keys with an Azure Storage Account, and regenerate (rotate) the keys periodically.

AzureKeyVault is an R package for working with the Key Vault service. It provides both a client interface, to access the contents of the vault, and a Resource Manager interface for administering the Key Vault itself.

Resource Manager interface

AzureKeyVault extends the AzureRMR package to handle key vaults. In addition to creating and deleting vaults, it provides methods to manage access policies for user and service principals.

# create a key vault
rg <- AzureRMR::get_azure_login()$
    get_subscription("sub_id")$
    get_resource_group("rgname")
kv <- rg$create_key_vault("mykeyvault")

# list current principals (by default includes logged-in user)
kv$list_principals()

# get details for a service principal
svc <- AzureGraph::get_graph_login()$
    get_service_principal("app_id")

# give the service principal read-only access to vault keys and secrets
kv$add_principal(svc,
    key_permissions=c("get", "list", "backup"),
    secret_permissions=c("get", "list", "backup"),
    certificate_permissions=NULL,
    storage_permissions=NULL)

Client interface

The client interface is R6-based. To instantiate a new client object, call the key_vault function. This object includes sub-objects for interacting with keys, secrets, certificates and managed storage accounts.

vault <- key_vault("https://mykeyvault.vault.azure.net")

# can also be done from the ARM resource object
vault <- kv$get_endpoint()

Keys

Key Vault supports RSA and elliptic curve (ECDSA) asymmetric encryption keys. The keys component of the client object provides methods for managing keys:

  • create: Create a new key, or a new version of an existing key.
  • import: Import a key from a PEM file.
  • get: Retrieve an existing key.
  • list: List all keys in the vault.
  • delete: Delete a key.
  • backup: Return a base64-encoded blob representing the key.
  • restore: Use a blob obtained by the backup method to restore a key.
  • do_operation: Carry out arbitrary REST operations on keys. Used by the above methods.

In turn, an individual key is represented by an object of class stored_key. This has the following methods:

  • list_versions: List the available versions for this key.
  • set_version: Set the version of the key to use. The default is to use the most recently created version.
  • encrypt: Encrypt a character string or raw vector, producing a base64-encoded ciphertext string.
  • decrypt: Decrypt a ciphertext string, producing either a character string or raw vector. The inverse operation of encrypt.
  • sign: Sign a hashed digest.
  • verify: Verify the signature of a hash. The inverse operation of sign.
  • wrap: Wrap a symmetric key. This is technically the same as encrypting it, but is provided as a distinct operation to allow more granular management of permissions.
  • unwrap: Unwrap a wrapped key. The inverse operation of wrap.

The key object contains the public key component in the key field, as a parsed JSON web key. Note that Azure Key Vault does not provide access to the private key component.

# create a new RSA key with 4096-bit key size
vault$keys$create("newkey", type="RSA", rsa_key_size=4096)

key <- vault$keys$get("newkey")

# encrypting and decrypting
plaintext <- "super secret"
ciphertext <- key$encrypt(plaintext)
decrypted_text <- key$decrypt(ciphertext, as_raw=FALSE)
plaintext == decrypted_text
#> [1] TRUE

# signing and verifying
dig <- openssl::sha256(charToRaw(plaintext))
sig <- key$sign(dig)
key$verify(sig, dig)
#> [1] TRUE

# exporting the public key component, using the jose and openssl packages
pubkey <- key$key
openssl::write_pem(jose::read_jwk(pubkey), "pubkey.pem")

# importing a key generated by openssl
sslkey <- openssl::rsa_keygen()
vault$keys$import("sslkey", sslkey)

# importing a key from a file
openssl::write_pem(sslkey, "sslkey.pem")
vault$keys$import("sslkeyfromfile", "sslkey.pem")

Secrets

Key Vault allows you to store confidential information such as passwords, database connection strings, tokens, API keys, and so on. The secrets component of the client object provides methods for managing generic secrets:

  • create: Create a new secret, or a new version of an existing secret.
  • get: Retrieve an existing secret.
  • list: List all secrets in the vault.
  • delete: Delete a secret.
  • backup: Return a base64-encoded blob representing the secret.
  • restore: Use a blob obtained by the backup method to restore a secret.
  • do_operation: Carry out arbitrary REST operations on secrets. Used by the above methods.

An individual secret is represented by an object of class stored_secret. Unlike a key, a secret is essentially just data, so the object does not provide any operations. It has the following methods for managing secret versions:

  • list_versions: List the available versions for this secret.
  • set_version: Set the version of the secret to use. The default is to use the most recently created version.

The secret itself is in the value field of the object, of class secret_value. This class has a print method that hides the value, to help guard against shoulder-surfing. Note that this will not stop a determined attacker; as a general rule, you should minimise assigning secrets or passing them around your R environment. If you want the raw string value itself, eg when passing it to jsonlite::toJSON or other functions which do not accept arbitrary object classes as inputs, use unclass to strip the class attribute first.

# create a new secret
vault$secrets$create("newsecret", "hidden text")
secret <- vault$secrets$get("newsecret")
secret$value
#> <hidden>

Certificates

The certificates component provides methods for working with SSL/TLS authentication certificates:

  • create: Create a new certificate, or a new version of an existing certificate. The default is to create a self-signed certificate.
  • import: Import a certificate from a PFX file.
  • get: Retrieve an existing certificate.
  • list: List all certificates in the vault.
  • delete: Delete a certificate.
  • backup: Return a base64-encoded blob representing the certificate.
  • restore: Use a blob obtained by the backup method to restore a certificate.
  • set_contacts: Set the email address(es) to contact when a certificate is due for renewal.
  • get_contacts: Get the email address(es) of the contacts.
  • add_issuer: Adds the details for an issuer (certificate authority).
  • get_issuer: Retrieve the details for an issuer.
  • remove_issuer: Removes the details for an issuer.
  • list_issuers: Lists the issuers available to this vault.
  • do_operation: Carry out arbitrary REST operations on certificates. Used by the above methods.

An individual certificate is represented by an object of class stored_certificate. This has the following methods:

  • list_versions: List the available versions for this certificate.
  • set_version: Set the version of the certificate to use. The default is to use the most recently created version.
  • export: Export the certificate as either a PEM or PFX file (the format is fixed at certificate creation).
  • export_cer: Export the certificate public key as a CER file.
  • sign: Sign a hashed digest.
  • verify: Verify a signature against a digest.
  • set_policy: Sets the policy for a certificate, ie the authentication details.
  • get_policy: Retrieve the policy for a certificate.
# create a new self-signed certificate (will also create an associated key and secret)
cert <- vault$certificates$create("newcert",
    subject="CN=example.com",
    x509=cert_x509_properties(dns_names="example.com"))

# import a certificate from a PFX file
vault$certificates$import("importedcert", "mycert.pfx")

# export the certificate as a PEM file
cert$export("newcert.pem")

# to export as a PFX file, set the 'format' argument at cert creation
newcert2 <- vault$certificates$create("newcert2",
    subject="CN=example.com",
    format="pfx")
newcert2$export("newcert2.pfx")

Note that exporting a certificate to a file should not be done unless absolutely necessary, as the point of using Key Vault is to avoid having to save sensitive data on a local machine.

The AzureAuth package is able to make use of certificates stored in Key Vault to authenticate with Azure Active Directory. Here is some example code to do so. The app (client) in question needs to be setup with the certificate public key; we create the app via the AzureGraph package and pass it the key.

## create a registered app in Azure Active Directory with cert credentials
gr <- AzureGraph::get_graph_login()
certapp <- gr$create_app("certapp", password=FALSE, certificate=cert$cer)

# authenticate using data in Key Vault
AzureAuth::get_azure_token("resource_url", "mytenant", certapp$properties$appId,
    certificate=cert)

Storage accounts

Key Vault can be configured to manage access to an Azure Storage Account, by automatically regenerating access keys and saving commonly-used access patterns as shared access signature (SAS) templates. The storage component of the client object provides methods for working with managed accounts:

  • add: Add a new storage account.
  • get: Retrieve an existing account.
  • list: List all storage accounts in the vault.
  • remove: Stop managing a storage account.
  • backup: Return a base64-encoded blob representing the storage account.
  • restore: Use a blob obtained by the backup method to restore an account.
  • do_operation: Carry out arbitrary REST operations on accounts. Used by the above methods.

An individual certificate is represented by an object of class stored_account, which has the following methods. Note that unlike the other types of objects, storage accounts are not versioned.

  • regenerate_key: Manually regenerate an access key.
  • create_sas_definition: Create a SAS definition, from which an actual SAS can be obtained.
  • get_sas_definition: Retrieve an existing SAS definition.
  • delete_sas_definition: Delete a SAS definition.
  • list_sas_definitions: List existing SAS definitions.
  • show_sas: Get a SAS from a definition.
# get the storage account details
library(AzureStor)
res <- AzureRMR::get_azure_login()$
    get_subscription("sub_id")$
    get_resource_group("rgname")$
    get_storage_account("mystorageacct")

# add a managed storage account
stor <- vault$storage$add("mystorage", res, "key1")

# Creating a new SAS definition
today <- Sys.time()
sasdef <- res$get_account_sas(expiry=today + 7*24*60*60, services="b", permissions="rw")
stor$create_sas_definition("newsas", sasdef, validity_period="P15D")

stor$show_sas("newsas")

See also

For more information, see the official Key Vault documentation.