Appropriate key for AES 256 GCM I am updating a system that has Rails' OpenSSL on one side and Python's cryptography.io on the other.

The Rails side has to generate keys for the AES-256-GCM and here are two methods that appear to work:

SecureRandom.hex
# =&gt; "3d499a7b8f57773281ca2d47698a0062"

OpenSSL::Cipher::Cipher.new("aes-256-gcm").random_key
# =&gt; "\x1A\xAC\x8F\xC4mX\xAE\xDE\x04\xE1\x83\x8F\xFF^\x8C3KJ\xBE\vt\xE4\xBC\x14B\xDEro\xB8\xF5Yy"


Due to the transmission, it is currently easier to use SecureRandom.hex, but I believe that its reduced charset would make it less secure for any given length. Is this a valid concern (meaning I should spend more time making the OpenSSL version work), or is it reasonable to use SecureRandom.hex to generate keys?
The different charsets are simply the result of different encodings. A secure random number generator will generate a sequence of bits indistinguishable from pure random. These bits can then be encoded in several ways. In your case the SecureRandom.hex is returning base64 encoded bits, while OpenSSL::Cipher::Cipher.new("aes-256-gcm").random_key is returning ASCII encoded bits (where bit sequences that have no ASCII representation are prefixed with \x and hex encoded).

These two methods do not, in fact, return the same number of random bits:

In [7]: from binascii import a2b_base64

In [8]: openssl = b'\x1A\xAC\x8F\xC4mX\xAE\xDE\x04\xE1\x83\x8F\xFF^\x8C3KJ\xBE\vt\xE4\xBC\x14B\xDEro\xB8\xF5Yy'

In [9]: len(openssl)
Out[9]: 32

In [10]: secrand = a2b_base64('Z6sBQRGKll7FEKz3ebfUiGupwhPS3QVm')

In [11]: len(secrand)
Out[11]: 24


As you can see, SecureRandom.hex generates 24 bytes = 192 bits, and OpenSSL::Cipher::Cipher.new("aes-256-gcm").random_key generates 32 bytes = 256 bits.

Since AES-256-GCM requires a 256 bit key I recommend using OpenSSL::Cipher::Cipher.new("aes-256-gcm").random_key.


EDIT

Since the value SecureRandom.hex returns in the question was updated to be a hexidecimal encoding the amount of random bits it generates is no longer 192. It actually generates less random bits now, since a base64 encoded string can encode more bits than an equally long hex encoded string.

In [1]: from binascii import unhexlify

In [2]: k = unhexlify('3d499a7b8f57773281ca2d47698a0062')

In [3]: len(k)
Out[3]: 16


So now we have 16 bytes = 128 bits.



In general, assuming a secure random number generator, the number of bytes of entropy that an encoded string of length $n$ has is:


ASCII - $n$
base64 - $\frac{3n}{4}$
hex - $\frac{n}{2}$
binary - $\frac{n}{8}$


You can multiply any of those terms by 8 to get the equivalent values in bits rather than bytes.