Phill
Phill

Reputation: 18794

What's the best approach for generating a new API key?

So with lots of different services around now, Google APIs, Twitter API, Facebook API, etc etc.

Each service has an API key, like:

AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q

All the keys vary in length and the characters they contain, I'm wondering what the best approach is for generating an API key?

I'm not asking for a specific language, just the general approach to creating keys, should they be an encryption of details of the users app, or a hash, or a hash of a random string, etc. Should we worry about hash algorithm (MSD, SHA1, bcrypt) etc?

Edit: I've spoke to a few friends (email/twitter) and they recommended just using a GUID with the dashes stripped.

This seems a little hacky to me though, hoping to get some more ideas.

Upvotes: 157

Views: 170657

Answers (11)

lepoing
lepoing

Reputation: 66

I'd like to add an implementation in Java, that uses class KeyPairGenerator to gen a public key using RSA, then convert it to base64. Lastly you can also pretty your API key by eliminating all the slash char and extract only the last part of the generated public key.

import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;

public static String generateMyAPIKey() throws NoSuchAlgorithmException {
   KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
   keyGen.initialize(1024);
   byte[] publicKey = keyGen.genKeyPair().getPublic().getEncoded();
   String base64Binary = DatatypeConverter.printBase64Binary(publicKey).replaceAll("/", "");

   return base64Binary.substring(base64Binary.length() - 32);
}

Upvotes: 1

Chris Chiasson
Chris Chiasson

Reputation: 816

2023 Note: In Chrome, the default new tab page does not allow the use of the cryptography module in the console, so please use a different page.

Update, in Chrome's console and Node.js, you can issue:

crypto.randomUUID()

Example output:

4f9d5fe0-a964-4f11-af99-6c40de98af77

Original answer (stronger):

You could try your web browser console by opening a new tab on any site (see 2023 note at the top of this answer), hitting CTRL + SHIFT + i on Chrome, and then entering the following immediately invoked function expression (IIFE):

(async function (){
  let k = await window.crypto.subtle.generateKey(
    {name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]);
  const jwk = await crypto.subtle.exportKey("jwk", k)
  console.log(jwk.k)
})()

Example output:

gv4Gp1OeZhF5eBNU7vDjDL-yqZ6vrCfdCzF7HGVMiCs

References:

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/exportKey

I'll confess that I mainly wrote this for myself for future reference...

Upvotes: 18

Andrew E
Andrew E

Reputation: 8337

Yet another more updated version of previous answers - but more compact and new-javascripty than before!

#!/usr/bin/env node
const { subtle } = require('crypto').webcrypto

subtle
  .generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'])
  .then(key => subtle.exportKey('jwk', key))
  .then(jwk => console.log(jwk.k))

Run:

$ node /tmp/genkey.js
Mtra_qEFS7F76HrpgDAP2rBsb4pJ4w2hTL8UUyxalRA

Upvotes: 4

Dana Scott
Dana Scott

Reputation: 21

One popular way is to generate a random string using a cryptographically secure pseudo-random number generator (CSPRNG) and then encode this string with base64 encoding. This can provide a high level of security, as the keys are difficult to guess, and can be of virtually any length.

The other approach, as your friends suggested, is to use a GUID/UUID. It's true that some might find this to be a little "hacky", but in practice, it works well.

As for the hashing algorithms, if you choose to go the hashing route, it's generally recommended to use a strong algorithm like SHA256 or SHA3. Algorithms like MD5 and SHA1 are considered to be broken and should not be used for new systems.

Here is the Python code:

import secrets
import base64

def generate_api_key():
    # Generate 32 random bytes
    random_bytes = secrets.token_bytes(32)

    # Convert those bytes into a URL-safe base64 string
    api_key = base64.urlsafe_b64encode(random_bytes).decode("utf-8")

    return api_key

print(generate_api_key())

Upvotes: 1

Joe
Joe

Reputation: 2440

I liked Chris Chiasson's use of crypto.subtle in a browser, but wanted a command-line version. If you have NodeJS installed, this can be saved in a file (e.g., mkapikey.js):

#!/usr/bin/env node
crypto.subtle.generateKey({name:"AES-GCM", length:256},true,
["encrypt","decrypt"]).then(key => {crypto.subtle.exportKey("jwk",key).
then(jwk => {console.log(jwk.k)})});

Either chmod +x to an executable or invoke with node mkapikey.js.

chmod +x mkapikey.js
./mkapikey.js
4SvnAK87DD0t3WMS878UCY-obmADtPeBn6X4gFOtUig

Upvotes: 2

Victor
Victor

Reputation: 3551

In the terminal, you can use openssl like so:

openssl rand -hex 32

Example output:

1e846f3fcf103f64ca10fa4eac73bfae32ef10750bf4eae29132dc099526c561

Upvotes: 13

Edward Brey
Edward Brey

Reputation: 41658

If you want an API key with only alphanumeric characters, you can use a variant of the base64-random approach, only using a base-62 encoding instead. The base-62 encoder is based on this.

public static string CreateApiKey()
{
    var bytes = new byte[256 / 8];
    using (var random = RandomNumberGenerator.Create())
        random.GetBytes(bytes);
    return ToBase62String(bytes);
}

static string ToBase62String(byte[] toConvert)
{
    const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    BigInteger dividend = new BigInteger(toConvert);
    var builder = new StringBuilder();
    while (dividend != 0) {
        dividend = BigInteger.DivRem(dividend, alphabet.Length, out BigInteger remainder);
        builder.Insert(0, alphabet[Math.Abs(((int)remainder))]);
    }
    return builder.ToString();
}

Upvotes: 10

Edward Brey
Edward Brey

Reputation: 41658

Use a random number generator designed for cryptography. Then base-64 encode the number.

This is a C# example:

var key = new byte[32];
using (var generator = RandomNumberGenerator.Create())
    generator.GetBytes(key);
string apiKey = Convert.ToBase64String(key);

Upvotes: 93

Mike Samuel
Mike Samuel

Reputation: 120516

API keys need to have the properties that they:

  • uniquely identify an authorized API user -- the "key" part of "API key"
  • authenticate that user -- cannot be guessed/forged
  • can be revoked if a user misbehaves -- typically they key into a database that can have a record deleted.

Typically you will have thousands or millions of API keys not billions, so they do not need to:

  • Reliably store information about the API user because that can be stored in your database.

As such, one way to generate an API key is to take two pieces of information:

  1. a serial number to guarantee uniqueness
  2. enough random bits to pad out the key

and sign them using a private secret.

The counter guarantees that they uniquely identify the user, and the signing prevents forgery. Revocability requires checking that the key is still valid in the database before doing anything that requires API-key authorization.

A good GUID generator is a pretty good approximation of an incremented counter if you need to generate keys from multiple data centers or don't have otherwise a good distributed way to assign serial numbers.


or a hash of a random string

Hashing doesn't prevent forgery. Signing is what guarantees that the key came from you.

Upvotes: 40

Dave Van den Eynde
Dave Van den Eynde

Reputation: 17415

An API key should be some random value. Random enough that it can't be predicted. It should not contain any details of the user or account that it's for. Using UUIDs is a good idea, if you're certain that the IDs created are random.

Earlier versions of Windows produced predictable GUIDs, for example, but this is an old story.

Upvotes: 1

Adam Ralph
Adam Ralph

Reputation: 29956

I use UUIDs, formatted in lower case without dashes.

Generation is easy since most languages have it built in.

API keys can be compromised, in which case a user may want to cancel their API key and generate a new one, so your key generation method must be able to satisfy this requirement.

Upvotes: 8

Related Questions