Twilio is launching a new Console. Some screenshots on this page may show the Legacy Console and therefore may no longer be accurate. We are working to update all screenshots to reflect the new Console experience. Learn more about the new Console.
Voice Recording Encryption is a feature that provides additional security on your Twilio Programmable Voice Recordings. It allows you to encrypt your recordings with a public key.
Once you activate the Voice Recording Encryption feature, only you will be able to decrypt the recordings. There is no one at Twilio, including Twilio support, that will be able to decrypt your recordings. Therefore testing of this feature should only be done on test accounts with non-production recordings.
Today, by default, all Programmable Voice Recordings are encrypted at rest while stored in Twilio's cloud storage. With Voice Recording Encryption enabled, your recordings are encrypted with your public key as soon as the call ends, while the recording is within the Twilio infrastructure, and before it is in cloud storage. The recording remains in this encrypted state until you retrieve it, ensuring that the recording can only be accessed by you, the holder of the corresponding private key.
The Voice Recording Encryption feature is implemented using hybrid encryption. The following are the summarized set of steps of encryption/decryption for each recording.
Detailed decryption steps and code samples can be found later in this guide.
First, generate an RSA public/private key pair. There are many different ways to do this, we recommend using openssl. Once you have openssl installed, you can generate a 2048 length private key with this command:
openssl genrsa -out private_key.pem 2048
The generated file, private_key.pem
, contains your private key, which will look something like this:
1-----BEGIN RSA PRIVATE KEY-----2MIIEpAIBAAKCAQEAtePBUk3IM45Jj8eFFrmwzjr/2seEtMknl5OD7VDBipazsq5v3MBnIYcE+EuzDiFC5XXww9rncFRZC0I3hLUejUTkJNZjMDQzVFkGXo9+A4MsXRZqK4OOYhCNAr2C1acpHKK6bEqGhRW2F2R0dSndbEKOCpPKD70ZF2aZyQdb//9104ROdh5bvsycQD7ZGQ8V5SoUo6kPBjQv1sbi99LN6uQm+trUDHkBhbpeKU836YPpIH1ZAqG6h2sSzRHN0eXdOPYNdu649ZuOSz0kIUN22e8R39suRhu6VbrC2kvVz2Su+tSPMWlp7gKjMboVKrsWUH9B1fQM9ajixc8fc892ZoGBqaQIDAQABAoIBAQCd5BlbEr4pUui08cOQs+ABs5XZYOj4OmVdPEvTAuwtm/K78+sL2JEt34EG8N978o+ZlKntukaRkgbB69Tc8ceUViKnq+Fed7pJoM+d9il4/Okz2eZCp8ffhLKDoHLEeJkNjIz7mC3xtQkegU10s+sZrOcW/P6r7KrsHrOFti0IqiTOWps1M6gIUKFWcIRIh/6SyN0gmdDxmfGD9o4W11CePswAS0fmwMZPCwQ9GazC8iVL+CvrF92UNfmNQSUiuR0GynOlsMnDu2GvSim3yO129lqWAo1yyEBVU8x6pS1wFTdsXQ7Ch2Ei9ZU+XE6SL5lq3jSc8WqIGmLvZ+zw5eAR138J73+fkBAoGBAO12zPHKgvN5nHRTrO3gNVcl92201umLHllf2elOjlE98/qtNsuX14R96LILDv4rgSjwH0+eVQW2g2B5o3D6KPvXdEvUmaRIXDValqr1UzED1DFWLs1MQK15HO30rJSpfWpTD3B56zvMb620avIBv3+Oe6kmjImn7Db/nyuEZrs49sE5AoGBAMQW16bAXgbG5GDUMVvJfrWwiXz3Ip7yv2j6xz5MtU58gytVV2ZnesLSCfpKrUpalPDWsX1704ZBuZ7bqZR4UpGQnGlYePtttKMdI4Vbo+tPK8gNN8ELu+8Fgmr0UNv3BWmcSRzo18AfiWWIHZS6iAkPoaYWQtCtf3WU0wnt/beiP/NWKxAoGAafCUYlLMtT7OE/+4qK9c19XLLtfh4tuyd7tLfUigen6orPLEjWp2GoiJpdTVLYPPLapi7axflhrk5ceeqSqR2j20k3AxWoLeiyaoMtsLueD8H7ir8+Rgz80LNwXvcKtk7mh7/NwHnDgKot5Yz/sDqi6w218Lfn/wnRkn/cTRfWlTRGsdECgYEAuXjP4lsdlMyT3MFhqnzGlYEqibyaaoYD7cWN22Qrpjplw4YsbkMwvbf4EhOyh6LYQFmCdoPxRJ47W4WCPbTa5wE8DIZmGlO6fjIk/E2341z2d3nxI5rav0IB0vKWzQiAyR03lqzouF5VBzUmuBIrjzWGqz9jg1WF1VpI3Er32447aQo3ECgYBQ7UZ3IP1+unprNsvVDT4CbjsoAypstmQhfgxYiNPY0wB7rvTOWT3q253vwOBwVBjfvkG8yYglYgHc0xGOrqL6DxhMUFTxBe0iDvBX0QM1tpp4apsKdHvuuQ26h1icaQZp8WKxBOzVilj3DLoHJEyIrsWWMnDHazV4fxbxijpj4uwJCw==27-----END RSA PRIVATE KEY-----
If your particular project/cryptography library requires PKCS8 syntax, you can convert your private key to PKCS #8 format by executing the following:
1openssl pkcs8 -in private_key.pem -topk8 -nocrypt -out private_key_pkcs8.pem2
You will need to do this for our Java Decryption Tool and our JavaScript Decryption Tool.
It is your responsibility to keep your private key safe. Losing your private key means that you will not be able to decrypt any of the files that were encrypted with the corresponding public key.
You can obtain the public key by executing the following command:
openssl rsa -in private_key.pem -pubout -out public_key.pem
The file public_key.pem
contains the public key. It should look something like this:
1-----BEGIN PUBLIC KEY-----2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtePBUk3IM45Jj8eFFrmw3zjr/2seEtMknl5OD7VDBipazsq5vMBnIYcE+EuzDiFC5XXww9rncFRZC0I3hLUej4UTkJNZjMDQzVFkGXo9+A4MsXRZqKOOYhCNAr2C1acpHKK6bEqGhRW2F2R0dSndbE5KOCpPKD70ZF2aZyQdb//9104ROdhbvsycQD7ZGQ8V5SoUo6kPBjQv1sbi99LN6uQ6m+trUDHkBhbpeKU836YPpIH1ZAqGh2sSzRHN0eXdOPYNdu649ZuOSz0kIUN22e8R739suRhu6VbrC2kvVz2Su+tSPMWlpgKjMboVKrsWUH9B1fQM9ajixc8fc892ZoGBq8aQIDAQAB9-----END PUBLIC KEY-----
In the next step, you will configure Twilio with this public key.
Once you have the RSA public key, you should create a Twilio Public Key resource that contains your public key. You can do this with the Public Key Resource REST API or, in the Console.
To configure the public key in the Console, navigate to Runtime > Credentials in the Console and click the Create new Credential button:
Provide a friendly name for your public key. Then, copy the full contents of the public_key.pem
file generated in step 1
— including the BEGIN PUBLIC KEY
and END PUBLIC KEY
lines — and paste it into the PUBLIC KEY field. Click Create to create the Twilio Public Key:
Once you submit the Twilio Public Key resource, the public key itself will no longer be retrievable from Twilio. All future references to this particular public key will be the via an associated unique SID identifier, with the form CRxx
.
You can enable Voice Recording Encryption at a project or subaccount level via the Console:
Obtain the public_key_sid
, encrypted_cek
, and iv
parameters within EncryptionDetails
JSON object, which you can retrieved by making an HTTP GET
request to the Recordings resource. The EncryptionDetails
data will also be posted to your RecordingStatusCallback.
EncryptionDetails
includes relevant encryption details if the recording was encrypted with the Voice Recording Encryption feature. It will be null
if the recording was not encrypted. The parameters within EncryptionDetails include :
Parameters | Description |
---|---|
type | The type of encryption. Currently, the only value supported is rsa-aes. |
public_key_sid | A 34-character string that uniquely identifies the public key resource used as by the recording encryption. |
encrypted_cek | Base64-encoded Content Encryption Key (CEK) used as part of recording encryption. |
iv | Base64-encoded randomly generated Initial Vector (IV) used as part of recording encryption. |
Any recording encrypted with Voice Recording Encryption will contain additional encryption properties on the recording resource. See the request/response example below to query the recording resource metadata.
The request:
curl -X GET 'https://api.twilio.com/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Recordings/REXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.json'
The response:
1{2"account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",3"api_version": "2010-04-01",4"call_sid": "CAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",5"conference_sid": "CFXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",6"channels": 1,7"date_created": "Fri, 14 Oct 2016 21:56:34 +0000",8"date_updated": "Fri, 14 Oct 2016 21:56:38 +0000",9"start_time": "Fri, 14 Oct 2016 21:56:34 +0000",10"price": "-0.0025",11"price_unit": "USD",12"duration": "4",13"sid": "REXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",14"source": "StartConferenceRecordingAPI",15"status": "completed",16"error_code": null,17"uri": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Recordings/REXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.json",18"subresource_uris": {19"add_on_results": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Recordings/REXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/AddOnResults.json",20"transcriptions": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Recordings/REXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Transcriptions.json"21},22"encryption_details": {23"type": "rsa-aes",24"encryption_public_key_sid": "CRXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",25"encryption_cek": "OV4h6zrsxMIW7h0Zfqwfn6TI2GCNl54KALlg8wn8YB8KYZhXt6HlgvBWAmQTlfYVeLWydMiCewY0YkDDT1xmNe5huEo9vjuKBS5OmYK4CZkSx1NVv3XOGrZHpd2Pl/5WJHVhUK//AUO87uh5qnUP2E0KoLh1nyCLeGcEkXU0RfpPn/6nxjof/n6m6OzZOyeIRK4Oed5+rEtjqFDfqT0EVKjs6JAxv+f0DCc1xYRHl2yV8bahUPVKs+bHYdy4PVszFKa76M/Uae4jFA9Lv233JqWcxj+K2UoghuGhAFbV/JQIIswY2CBYI8JlVSifSqNEl9vvsTJ8bkVMm3MKbG2P7Q==",26"iv": "8I2hhNIYNTrwxfHk"27}28}
RecordingStatusCallback is the reliable way to receive webhooks for completed or failed recordings. Please see this RecordingStatusCallback support article for more details.
Subscribe to 'failed' RecordingStatusCallbackEvent to receive callbacks in recording encryption failure scenarios; these are described later in this article.
Here is an example of a RecordingStatusCallback webhook with encryption parameters:
1"RecordingSource": "OutboundAPI"2"EncryptionDetails": "{"type":"rsa-aes","public_key_sid":"CR201607f4ca45a533cdca8d9a828c2a87","encrypted_cek":"ZriXxBEXSywEohXQZV53KGvyzAO1HpKRxCuMo/pcKiT7C+bWKfelZuX0eW1jb7iGcESrOqwvLo4v4GVRPDdJKsaO6R/AVTDcA+he5syPDBgg20ECilAhC/9/CNxfbIuQD+rRKmx0O7SOJJyazbc4zlv+4ClWwDm6g/8z0ekpYs/tNrlQenbxU/Un9uLeeBaJtFKeK5YSUea5n3Kce22iaPZMy3WUGBg+JfOHrccvCjDjX5QQ21I3rcdpgb5nwpzf3MQwmExhW8SJtmQ1cL4jDeKojM255HhhcgOYDwcyrTfY7svUkqNrEKei1q5ZFdBl+SjjKfSdE0BgEvTceZZYrQ==","iv":"7MiadYE7QDgVSRm9"}"3"RecordingSid": "REb719a56ceca43b2d06967983570e658a"4"RecordingUrl": "https://api.twilio.com/2010-04-01/Accounts/AC18d5c6f2003e8710de63b2f9c412b145/Recordings/REb719a56ceca43b2d06967983570e658a"5"RecordingStatus": "completed"6"RecordingChannels": "1"7"ErrorCode": "0"8"CallSid": "CA5987df4d600665d67f53e1bd4cec76d6"9"RecordingStartTime": "Tue, 28 May 2019 02:18:02 +0000"10"AccountSid": "AC18d5c6f2003e8710de63b2f9c412b145"11"RecordingDuration": "5"
public_key_sid
and use it to decrypt the base64-encoded encrypted_cek
from your retrieved EncryptionDetails
object.iv
from your retrieved EncryptionDetails
object to initialize an AES256-GCM SecretKey object.If you are using a decryption tool that requires the authentication tag, this tag is returned in the last 16 bytes of the ciphertext.
To help you with your own code, here are some decryption code samples in key languages:
Voice Recording Encryption is enabled but Twilio is unable to determine or retrieve the public key configured for recording encryption
public_key_sid
within EncryptionDetails
will represent the SID
of the public key used for encryption, which is not necessarily the one added to the account.Twilio is unable to encrypt the recording because there aren't any public keys on the account or an intermittent system issue occurred.
Status
of failed
, an ErrorCode
of 16104
, and an EncryptionDetails
that is null
.Status
of failed
, and an ErrorCode
of 16104
. EncryptionDetails
is not included in the callback.