Security these days is bigger than ever. This statement needs no proof. It seems like it is getting easier and easier to get access to ‘secure data’. As system grows it becomes really hard to secure all endpoints. This is why encrypting data is now the hot topic in the database world. Even if an evil adversary get access to the data there is nothing he can do with it, really.
OK, we all agree we need encryption, ssl, etc. Let’s say we have it all. We are secure, it is all great. Well, almost great … Each of these mechanisms needs keys. Those keys need to be stored securely, best under the DBA pillow or somewhere far from the system itself.
We need a key store then. With MySQL 5.7.11 we introduced a keyring service into the server. When we deploy 5.7.11 the only user of the keyring service is InnoDB. However the keyring service is open to all, of course :).
Keyring service is a ‘front-end’ interface and can use different keyrings as back-end. We ship two keyrings:
- keyring_file (Community Server plugin starting from 5.7.11) stores keys in unencrypted flat file, so you have to be sure you store it in a secure location (usb key maybe?).
- keyring_okv (Enterprise Server plugin starting from 5.7.12) stores keys in Oracle Key Vault Server.
The keyrings guarantee that each operation either succeed or there is no change made to the keyring. Before you start using keyring service, make sure that there is a keyring plugin loaded.
Before we start summarizing all the keyring service’s functions, please note that each const char* is a null terminated string and the length of the keys is express in bytes not bits! (i.e. 128 bit key == 16 bytes key). Key is identified by a key_id/user_id pair. Each such pair must be unique. OK, now let’s see how these functions look like.
Storing the key
In order to store a key call:
1
2
|
my_bool my_key_store(const char *key_id, const char *key_type, const char *user_id, const void *key, size_t key_len); |
Let’s say we want to store a key called key_1 and we want assign it to user Robert. The key will be of type AES and 128 bit long. We call:
1 |
my_key_store('key_1', 'AES', 'Robert', '0123456789012345', 16); |
As mentioned above, 16 refers to 16 bytes, which is 128 bits. If everything went OK we should receive FALSE as the result.
What if we received TRUE? Best is to see the server log for indication of what could have gone wrong. Most commonly it would be:
- key_1 for user Robert already exists (key_id='key_1', user='Robert' is not an unique pair).
In case of keyring_okv – the plugin failed to establish a connection with OKV server. The other way of adding a key into the keyring is to ask keyring to generate a key for us.
Generating a key
1
2
|
my_bool my_key_generate(const char *key_id, const char *key_type, const char *user_id, size_t key_len); |
In order to generate an AES 128 key for user Kamil, we call:
1 |
my_key_generate('key_1', 'AES', 'Kamil', 16); |
As usual if function returned FALSE – no error, TRUE – error.
On successful execution this function generates a new AES-128 bit long key and stores it into the keyring with key_id='key_1', user='Kamil'. In case of keyring_okv plugin being back-end we can be extra sure that the key was generated with use of high enough entropy as the key will be generated by OKV server.
Fetching a key
Now we know how to store a key, let us fetch it:
1
2
|
my_bool my_key_fetch(const char *key_id, const char **key_type, const char* user_id, void **key, size_t *key_len); |
Let us say we want to fetch a Robert’s key_1 key:
1
2
3
|
char *key_type, *key; size_t key_length; my_key_fetch("key_1", &key_type, "Robert", &key, &key_length); |
Please note that memory needed for key_type and key is allocated by the keyring. Please make sure that you free these memory when no longer needed, i.e.
1
2
|
my_free(key_type); my_free(key); |
In our case key should be equal to 0123456789012345 and key_len should be equal to 16.
The return value this time is slightly bit more complicated.
mysql_key_fetch will return FALSE on no error and when a key was not found in the keyring. However when a key was not found in the keyring, *key will also be set to NULL. For instance:
1
2
|
my_key_fetch("key_x", &key_type,"Artur", &key, &key_length); assert(*key == NULL); // This should be true. |
Of course in this case you do not have to free key or key_type.
Removing key
There is one think left we need to discuss – removing key from the keyring. In order to do so we call:
1 |
my_bool my_key_remove(const char *key_id, const char *user_id); |
For instance to remove Robert’s key_1:
1 |
my_key_remove("key_1", "Robert"); |
As usual FALSE – success, TRUE – no error.
As it was said keyring service is just a ‘front-end’ for keyrings. When we get error from the service it is coming from the keyring itself unless there is no keyring loaded.
This framework is quite easy to extend. Let say you want to add your own keyring plugin – in order to do so your plugin has to implement the interface listed above.
Also this service interface can be used by all other plugins too.
That’s all folks.