SQL Server EncryptByKey cryptographic message description
Published Mar 23 2019 11:15 AM 2,011 Views
First published on MSDN on Mar 30, 2009

Since the introduction of SQL Server 2008 extensible key management  (EKM), new opportunities may arise to handle data encryption on the client while still making the plaintext data accessible to authorized users in SQL Server. One issue between SQL Server and third party clients has been already discussed in the SQL Server Security forum in the past: describing the cryptographic message elements used by the resulting ciphertext generated by EncryptByKey builtin.

While we are in the process of finalizing our documentation regarding this subject matter, I would like to take this opportunity to bring you an early draft view of the information and to get your feedback.. Below I will explain each one of the parts of the message along with an example. Notice that in all cases we are using little-endian byte ordering.

CipherTextMessage := KeyGUID + EncryptionHeader + EncryptedMessage

KeyGUID := {16 bytes} Key_guid. This GUID serves as an identifier for the key and it is stored in metadata (SELECT key_guid FROM sys.symmetric_keys). It is used during decryption for finding the corresponding key in the keyring.

EncryptionHeader := Headerversion + ReservedBytes

Headerversion := {1 byte} SQL Server encryption message version. Current version = 1

ReservedBytes := {3 byte} Reserved. Must be 0.

EncryptedMessage := InitializationVector + _EncryptFunction (SymKey, InitializationVector, InnerMessage)

InitializationVector := {1 block} the length of this field depends on the algorithm being used. All AES family keys will be 16 bytes per block, while the DES family keys are 8 bytes per block. Initialization vectors are used to initialize the block algorithm. It is not intended to be a secret, but must be unique for every call to the encryption function in order to avoid revealing patterns. For simplicity we will refer to the Initialization Vector as IV.

_EncryptFunction(IV, key, plaintext) := {variable length} function used to encrypt the plaintext data using the key (the algorithm is specified by the key itself) and the IV. In the case of SQL Server, this function is the CryptEncrypt , but since the supported encryption algorithms are standard, this function should be considered generic.

Interoperability note: We use the block cipher in Cipher Block Chaining (CBC) mode. (default mode for most algorithms for Microsoft cryptographic providers, see AES provider Algorithms in BOL for further details).

InnerMessage := InnerMessageHeader + IntegrityBytes + Plaintext

InnerMessageHeader := MagicNumber + IntegrityBytesLength + PlaintextLength

MagicNumber := {4 bytes} As the name suggests, it is only an arbitrary value used to identify the message format. The current expected value is 3131961357 (decimal, unsigned). The main goal of this value is to detect if the message format is the expected one (if the value is incorrect, the message will be discarded). Extra points for anyone who identified the hexadecimal representation for this value which is (DWORD) 0xBAADF00D. For anyone curious about magic numbers, here is an interesting article about hexspeak .

IntegrityBytesLength := {2 bytes} The length of the IntegrityBytes field. May be either 0 or 20 (decimal).

IntegrityBytes := {IntegrityBytesLength bytes} This field is used when the @authenticator parameter is used when calling EncryptByKey. When this parameter is used, the Integrity field will be the cryptographic hash (SHA1) of the @ClearText (Plaintext) concatenated with the @authenticator parameter.

PlaintextLength := {2 bytes} The length of the Plaintext field.

Plaintext := {PlaintextLength bytes} The content of the @ClearText parameter (binary representation) when calling EncryptByKey.

Let’s use concrete examples and analyze each part of the message. For example, a call to SELECT ENCRYPTBYKEY( key_guid('key1'), 'Hello World!') resulted in the following ciphertext:


Detailed analysis of each field:


Value (hex)





This value should match the GUID for our key.



Version 1

Reserved bytes = 0



Randomly generated

Since the rest of the message is encrypted, it would be of little value to try to analyze it in the current form, but the following analysis will describe the inner message before being encrypted:



Value (hex)







0 bytes since we didn’t use the @authenticator parameter



@ClearText length = 12 bytes



Version history
Last update:
‎Mar 23 2019 11:15 AM
Updated by: