I'm working with a dev team who are implementing encryption at rest at the application level. It's for particularly sensitive fields inside an RDB. (The underlying DB storage has an additional layer of encryption, but that's off topic here.) We're using a Spring's AesBytesEncryptor and related classes for that.
We have not fully solved key rotation yet, and i'm investigating how to do that in a secure manner. We'd like to use a separate key-rotation tool that would run isolated from the application that consumes the data. That key-rotation tool will access the DB asynchronously, and perform re-encryption in small chunks. (Not in a single DB transaction that would re-encrypt all data.)
In order to do so, we plan that the application can be configured with multiple (well, two) keys at the same time. It would always use the first key to encrypt new data. But it could decrypt old data with either key, depending on whether the key-rotation tool has already re-encrypted the particular field.
Now the question is, whether we need to store additional key versioning meta-data in the DB? That would tell the application which decryption key to use for any chunk of data.
I'm hoping to get around that by relying on some form checksum instead. The application would just try decryption keys in turn, until it gets a decrypted value that does not break the checksum.
Imho, Spring's AesBytesEncryptor might already be providing all that we need. But the reliability will depend on what mode we use. Here's my thoughts about the two supported modes:
Mode AES/CBC/PKCS5Padding:
Does not have a checksum per-se, but the padding will likely break when attempting decryption with the wrong key. However this depends on the size of the padding, which depends on plaintext data size. Worst case there might be as little as 1byte of padding. So chances to hit the correct padding with wrong decryption key might be as high as 1/256. That seems too risky to rely on.
Mode AES/GCM/NoPadding:
Here the GCM "authentication tag" serves as a form of checksum. As far as I understands it's always 128bits (16 bytes) long for AES. That means chances to hit a valid authentication tag with the wrong decryption key are roughly 1/(2^128). That is practically never, so I think it should be suitable for our application.
Are the above assumptions correct? What did I get wrong?
Do you think it's robust enough to try multiple decryption keys in turn during key rotation, when using AES/GCM/NoPadding? Or do we need to store key versioning information alongside the encrypted data after all?
Or, is there an even better way to solve key rotation in our scenario?