Encryption Key Rotation

Key rotation is recommended for security. This procedure shows how to encrypt data with an initial version of a key, then create a new key, encrypt all of the data with the new key, and drop the old key. This cycle of steps is a simple solution for rotating keys periodically.

For example, create a staff table that contains employee ID and social security number (SSN) columns. Assume that the SSN values need to be encrypted and protected from decryption by some users. In this case, both the ID values and the SSN values are unique. If your encrypted table does not have a unique ID, you can generate one by using a sequence.

  1. Create the staff table:
    premdb=# create table staff(id bigint, ssn varchar(100));
    CREATE TABLE
  2. Insert some rows:
    premdb=# insert into staff values(91234,'111-01-0987');
    INSERT 0 1
    premdb=# insert into staff values(85673, '222-99-4321');
    INSERT 0 1
    premdb=# select * from staff;
      id   |     ssn     
    -------+-------------
     91234 | 111-01-0987
     85673 | 222-99-4321
    (2 rows)
  3. Create a key:
    premdb=# create key ssnkey0 secret '0f5689f32';
    CREATE KEY
    
  4. Create staff_ks, an encrypted version of the staff table:
    premdb=# create table staff_ks(id bigint, ssn varchar(100));
    CREATE TABLE
    Note: Make sure the column that will hold encrypted values is wide enough. When a social security number, for example, is encrypted, it will require a much wider column.
  5. Insert rows into the encrypted table,
    premdb=# insert into staff_ks(ssn) 
    select concat('v0', '-', encrypt_ks(ssn, ssnkey0)) from staff;
    INSERT 0 2
    premdb=# select * from staff_ks;                                                                                                                                   id   |        ssn         
    -------+--------------------
     91234 | v0-w70ijTHGijjZrh0
     85673 | v0-vBmijvHIizDX|p0
    (2 rows)

    Use a prefix of the form xy- for the encrypted strings. In this example, v0- is the prefix. The hyphen character is never used in encrypted strings, so a prefix that ends with - is a good choice.

  6. Decrypt the encrypted values to verify that encryption and decryption are working.
    premdb=# select decrypt_ks(substr(ssn,4),ssnkey0) from staff_ks;
     decrypt_ks  
    -------------
     111-01-0987
     222-99-4321
    (2 rows)
  7. Add some new rows to the staff table.
    premdb=# truncate table staff;
    TRUNCATE TABLE
    premdb=# insert into staff values(13579, '451-90-3421');
    INSERT 0 1
    premdb=# insert into staff values(97684, '455-66-2356');
    INSERT 0 1
    premdb=# select * from staff;
      id   |     ssn     
    -------+-------------
     13579 | 451-90-3421
     97684 | 455-66-2356
    (2 rows)
  8. Create a new key:
    premdb=# create key ssnkey1 secret '41f55e912f';
    CREATE KEY
    premdb=# select * from sys.key;
     key_id |   name   | schema_id | owner_id |         creation_time         
    --------+----------+-----------+----------+-------------------------------
      16450 | ssnkey0  |      2200 |    16444 | 2019-11-12 11:52:51.871322-08
      16516 | ssnkey1  |      2200 |    16444 | 2019-11-13 13:28:03.204117-08
    (4 rows)
    
  9. Update the encrypted table, using the new key, ssnkey1, for the new rows.
    premdb=# insert into staff_ks(id, ssn) 
    select id, concat('v1', '-', encrypt_ks(ssn, ssnkey1)) from staff;
    INSERT 0 2
    premdb=# select * from staff_ks;
      id   |        ssn         
    -------+--------------------
     13579 | v1-hHUpDsQqa1yDwA3
     97684 | v1-hHUoDAwra5CCzM3
     91234 | v0-w70ijTHGijjZrh0
     85673 | v0-vBmijvHIizDX|p0
    (4 rows)
  10. Decrypt the rows with a CASE expression that applies the appropriate key to the v0 and v1 rows.
    premdb=# select id, case left(ssn,3)
    when 'v0-' then decrypt_ks(substr(ssn,4), ssnkey0)
    when 'v1-' then decrypt_ks(substr(ssn,4), ssnkey1)
    else 'missing key'
    end
    from staff_ks;
      id   |    case     
    -------+-------------
     13579 | 451-90-3421
     97684 | 455-66-2356
     91234 | 111-01-0987
     85673 | 222-99-4321
    (4 rows)
    
  11. Update the staff_ks table so that the original old key, ssnkey0, is no longer needed. The first update encrypts the v0- values with the new key and removes the v0 prefix. The second statement removes the v1- prefix from the rows that are already encrypted with the new key.
    premdb=# update staff_ks 
    set ssn=encrypt_ks(decrypt_ks(substr(ssn,4),ssnkey0),ssnkey1) 
    where left(ssn,3)='v0-';
    UPDATE 2
    premdb=# select * from staff_ks;
      id   |        ssn         
    -------+--------------------
     91234 | k1UpDIAqaDiEmI3
     85673 | jDkpDsAsaTCCwA3
     13579 | v1-hHUpDsQqa1yDwA3
     97684 | v1-hHUoDAwra5CCzM3
    (4 rows)
    
    premdb=# update staff_ks 
    set ssn=substr(ssn,4) 
    where left(ssn,3)='v1-';
    UPDATE 2
    premdb=# select * from staff_ks;
      id   |       ssn       
    -------+-----------------
     13579 | hHUpDsQqa1yDwA3
     97684 | hHUoDAwra5CCzM3
     91234 | k1UpDIAqaDiEmI3
     85673 | jDkpDsAsaTCCwA3
    (4 rows)
    
  12. Drop the old key.
    premdb=# drop key ssnkey0 cascade;
    DROP KEY