diff --git a/doc/src/sgml/key-management.sgml b/doc/src/sgml/key-management.sgml new file mode 100644 index 0000000000..9195316a5e --- /dev/null +++ b/doc/src/sgml/key-management.sgml @@ -0,0 +1,280 @@ + + + + Encryption Key Management + + + key management + + + + PostgreSQL supports internal + Encryption Key Management System, which is designed to manage + the life cycles of cryptographic keys within the PostgreSQL system. This includes dealing + with their generation, storage, usage and rotation. + + + + Encryption Key Management System is enabled when PostgreSQL is + built with --with-openssl and + is specified during initdb. The cluster passphrase provided by + option during initdb + and the one generated by in the + postgresql.conf must match, otherwise, the database cluster + will not start up. Please note that the cluster passphrase command passed to initdb + must return a passphrase equal or longer than 64 bytes and less than 1024 bytes. For example. + + initdb -D dbname --cluster-passphrase-command="echo 1234567812345678123456781234567812345678123456781234567812345678" + + + + + Key Generations and Derivations + + + When cluster_passphrase_command option is specified to the initdb, the process will + derive the cluster passphrase into a Key Encryption Key (KEK) and a HMAC Key using key + derivation protocol before the actual generation of application level cryptographic + level keys. + + + + + Key Encryption Key (KEK) + + + KEK is primarily used to encapsulate or restore a given application level cryptographic + key + + + + + HMAC Key + + + HMAC key is used to compute the HASH of a given application level cryptographic key for + integrity check purposes. SHA256 is the algorithm used along with the + HMAC key to compute a cryptographic hash for integrity check purposes. + + + + + + + These 2 keys are not stored physically within the PostgreSQL cluster as they are designed + to be derived from the correctly configured cluster passphrase. + + + + Encryption Key Management System currently manages 3 application level cryptographic keys + that have different purposes and usages within the PostgreSQL system and these are generated + using pg_strong_random() after KEK and HMAC key derivation during initdb process. The 3 keys + are: + + + + + SQL Level Key + + + SQL Level Key is used to wrap and unwrap a user secret / passphrase via pg_wrap() and pg_unwrap() + SQL functions. These 2 functions are designed to be used in conjunction with the cryptographic + functions provided by pgcrypto extension to perform column level encryption/decryption without + having to supply a clear text user secret or passphrase that is required by many pgcrypto + functions as input. Please refer to [Wrap and Unwrap User Secret section] for usage examples. + + + + + Block Level Key + + + Block Level Key is primarily used to encrypt / decrypt buffers as part of the Transparent Data + Encryption (TDE) feature + + + + + WAL Level Key + + + WAL Level Key is primarily used to encrypt / decrypt WAL files as part of the Transparent Data + Encryption (TDE) feature + + + + + + + + Key Initialization + + + When a PostgreSQL cluster with encryption key management enabled is started, the + cluster_passphrase_command parameter in postgresql.conf will be evaluated and the cluster + passphrase will be derived into KEK and HMAC Key in similar ways as initdb. + + + + After that, the 3 encapsulated application level cryptographic keys will be retrieved from + pg_cryptokeys directory to be restored and integrity-checked by the key management system + using the derived KEK and HMAC key. If this process fails, it is likely that the cluster + passphrase supplied to the cluster is not the same as that supplied to the initdb process. + The cluster will refuse to start in this case and user has to manually correct the cluster + passphrase. + + + + + Wrap and Unwrap User Secret + + + Encryption key management system provides pg_wrap() and pg_unwrap SQL functions described in + to perform wrap and unwrap operations on user + secret with the SQL level encryption key. The SQL level encryption key is one of the 3 application + level keys generated during initdb process when cluster_passphrase is supplied. + + + + When pg_wrap() and pg_unwrap() functions are invoked, SQL + level encryption key will internally be used to perform the encryption and decryption operation with + HMAC-based integrity check. From user's point of view, he or she is not aware of the actual SQL level + encryption key used internally by both wrap functions. + + + + One possible use case is to combine pg_wrap() and pg_unwrap() + with . User wraps the user encryption secret with pg_wrap  + function and passes the wrapped encryption secret to pg_unwrap function for the  + pgcrypto encryption functions. The wrapped secret can be stored in the application server or somewhere + secured and should be obtained promptly for cryptographic operation with pgcrypto. + + + + Here is an example that shows how to encrypt and decrypt data together with + wrap and unwrap functions: + + + +=# SELECT pg_wrap('my secret passward'); + pg_wrap +-------------------------------------------------------------------------------------------------------------------------------------------------------------------- + \xb2c89f76f04f95d029f179e0fc3df4ed7254127b5562a9e27d42d1cd037c942dea65ce7c0750c520fa4f4e90481c9eb7e1e42a068248c262c1a6f25c6eab64303b1154ccc9a14361223641aab4a7aabe +(1 row) + + + + Once wrapping the user key, user can encrypt and decrypt user data using the + wrapped user key together with the key unwrap functions: + + + + =# INSERT INTO tbl + VALUES (pgp_sym_encrypt('secret data', + pg_unwrap('\xb2c89f76f04f95d029f179e0fc3df4ed7254127b5562a9e27d42d1cd037c942dea65ce7c0750c520fa4f4e90481c9eb7e1e42a068248c262c1a6f25c6eab64303b1154ccc9a14361223641aab4a7aabe'))); + INSERT 1 + + =# SELECT * FROM tbl; + col +-------------------------------------------------------------------------------------------------------------------------------------------------------------- + \xc30d04070302a199ee38bea0320b75d23c01577bb3ffb315d67eecbeca3e40e869cea65efbf0b470f805549af905f94d94c447fbfb8113f585fc86b30c0bd784b10c9857322dc00d556aa8de14 +(1 row) + + =# SELECT pgp_sym_decrypt(col, + pg_unwrap('\xb2c89f76f04f95d029f179e0fc3df4ed7254127b5562a9e27d42d1cd037c942dea65ce7c0750c520fa4f4e90481c9eb7e1e42a068248c262c1a6f25c6eab64303b1154ccc9a14361223641aab4a7aabe')) as col + FROM tbl; + col +-------------- + secret data +(1 row) + + + + The data 'secret data' is practically encrypted by the + user secret 'my secret passward' but using wrap and + unwrap functions user don't need to know the actual user secret during + operation. + + + + + Key Rotation Process + + + Encryption keys in general are not interminable, the longer the same key is in use, + the chance of it being breached increases. Performing key rotation on a regular basis + help meet standardized security practices such as  + PCI-DSS and it is a good practice in security to limit the number of encrypted + bytes available for a specific key version. The key lifetimse are based on key length, + key strength, algorithm and total number of bytes enciphered. The key management systems + provides a efficient method to perform key rotation. + + + + Please be aware that the phrase "key rotation" here only refers to the + rotation of KEK and HMAC keys. The 3 application level encryption keys (SQL, Block and WAL + levels) are not rotated; they will in fact be the same before and after a "key + rotation." This can be justified because the actual keys are never stored anywhere + physically, presented to user or captured in logging. What is being rotated here is the KEK + and HMAC keys who are responsible for encapsulating and restoring the actual application level + encryption keys. + + + + Since both KEK and HMAC keys are derived from a cluster passphrase, the "key rotation" + ultimately refers to the rotation of cluster passphrase and deriving a new KEK and + HMAC keys from the new cluster passphrase. The new set of KEK and HMAC keys can then be used to + encapsulate all 3 application level encryptions keys and store the new results in pg_cryptokeys + directory. + + + + To complete the cluster passphrase rotation, user needs to follow the steps below: + + + + + Ensure the PostgreSQL server is running correctly with KMS enabled. Passphrase rotation cannot + be completed with the server shut down. + + + + + Update cluster_passphrase_command parameter in the postgresql.conf + such that the new command will return a new cluster passphrase. + + + + + In a psql session, execute pg_rotate_cluster_passphrase() SQL  + function to initiate the rotation. The function returns true upon successful key rotation and false if + otherwise. + + +postgres=> SELECT pg_rotate_cluster_passphrase(); +pg_rotate_cluster_passphrase +------------------------------ + t +(1 row) + + + + + Upon successful cluster passphrase rotation, all application level keys (SQL, WAL and Block Levels) will be + re-encapsulated by the new KEK and hashed by the new HMAC key derived from the new cluster passphrase. The + new encapsulated application level keys will be stored in pg_cryptokeys directory. Please note that the + actual application keys are the same as before; the rotation process only changes the KEK and HMAC key that + are used to encapsulate and verify the actual application level keys. This way, there is no need to decrypt + all the encrypted data with the old keys and re-encrypt them with the new. + + + In case of a crash during the cluster passphrase rotation process, the key management system is able to recover + to the previous sets of application level keys the next time server starts up. This is possible because the + key rotation and encapsulation process are done on a separate tempory key directory called + pg_cryptokeys_tmp and it will replace pg_cryptokeys and be deleted only when + everything is successfully finished. If the server starts with pg_cryptokeys_tmp folder present, it would + indicate that previous attempt of cluster passphrase rotation was not completed. In this case, the server + will discard pg_cryptokeys_tmp folder and load the keys in pg_cryptokeys as usual. + + +