Asymmetric cryptography signers

It is common to divide cryptography in three categories:

  1. Symmetric cryptography (also called symmetric-key algorithms):
    • Clear text is passed to an algorithm that uses a key for encrypting and decrypting to generate cipher text;
    • Examples include: AES (Advanced Encryption Standard), Blowfish, DES (Data Encryption Standard), etc;
    • In the BEAM ecosystem, it is represented by the :crypto.supports()[:ciphers].
  2. Asymmetric cryptography (also called public-key cryptography):
    • It uses a pair of keys where one is private (only known to the owner) and the other is public;
    • Clear text is passed to an algorithm plus the private key and generates a cipher text;
    • The cipher text can be verified by the public key, that is, we can check that it was generated by the private key.
  3. Hash cryptography (also called one-way functions):
    • Clear text is passed to a function that may or may not receive a key to generate irreversible cipher text.

In the JWA specification we have algorithms that use both symmetric and asymmetric cryptography functions. Let's see them (taken from the specification):

HS256HMAC using SHA-256Required
HS384HMAC using SHA-384Optional
HS512HMAC using SHA-512Optional
RS256RSASSA-PKCS1-v1_5 usingRecommended
RS384RSASSA-PKCS1-v1_5 usingOptional
RS512RSASSA-PKCS1-v1_5 usingOptional
ES256ECDSA using P-256 and SHA-256Recommended+
ES384ECDSA using P-384 and SHA-384Optional
ES512ECDSA using P-521 and SHA-512Optional
PS256RSASSA-PSS using SHA-256 andOptional
MGF1 with SHA-256
PS384RSASSA-PSS using SHA-384 andOptional
MGF1 with SHA-384
PS512RSASSA-PSS using SHA-512 andOptional
MGF1 with SHA-512

(removed the none algorithm we don't support**)

Besides the HSxxx family of algorithms, all others use asymmetric cryptography.

Using asymmetric algorithms

What is nice about public-key cryptography is that you don't need to "share a key" between parties. The one with the private key can generate tokens and publish his public key online so that anybody can check that token was generated by the owner of the private key.

In Joken that usually means a private key can be used to call Joken.encode_and_sign and Joken.verify. If only have the public key, then you can only call Joken.verify.

There are some specifications that use this: OpenID Connect being one very famous. It specifies that authentication servers must publish their keys in JWK (JSON Web Key) format on a public URL. Here are some examples:

We have a Joken Hook that can fetch these keys, turn them into signers and verify tokens. This is JokenJwks.

KEY formats

Different algorithms use different key formats. For example, RSA is based on huge prime numbers arithmetic. Its keys contain data about these prime numbers and other variables in the RSA function specification.

Elliptic curve cryptography is based on elliptic curve arithmetic. Its keys represent the function that generates infinite points on a curve specification.

Because of those differences, we usually have a container for each key type. There is a specification for how to represent these keys in JSON format. That is the JSON Web Key specification. It has a JSON format for each type of key.

We recommend reading on the appendix examples for each type of key. Here is the appendix of the JSON Web Key specification with examples for public and private RSA and EC keys.

Let's see some examples on parsing asymmetric RSA keys with Joken:

RSxxx keys

This algorithm uses the RSASSA-PKCS1-v1_5 that uses SHA2 hash algorithms. The base for this algorithm is the RSA public key standard. So, to use this algorithm we need a pair of RSA keys. There are many ways to generate these keys in different environments and is outside the scope of this library. Here is one of these ways just for an example:

➜  joken git:(master) openssl genrsa -out mykey.pem 4096
Generating RSA private key, 4096 bit long modulus (2 primes)

This will generate a private RSA key in PEM (privacy enhanced mail) format on a file named mykey.pem. With this key we can sign and verify a token signature.

To use it with Joken we can call one of the Joken.Signer.create variants:

  1. With the RAW PEM contents:

    signer = Joken.Signer.create(%{"pem" => pem_contents})
  2. With the pem file in the configuration:

    use Mix.Config
    config :joken,
      my_rsa_key: [
        signer_alg: "RS256",
        key_pem: """
        -----BEGIN RSA PRIVATE KEY-----
        -----END RSA PRIVATE KEY-----
    signer = Joken.Signer.parse_config(:my_rsa_key)
  3. With the key in JWK format:

    # example of key parameters from
    # This is for demonstration purposes. We don't allow those parameters in the map like
    # "alg", "kid", "kty". Although they are part of the JWK Set specification.
    key = %{
      "alg" => "RS256",
      "e" => "AQAB",
      "kid" => "2011-04-29",
      "kty" => "RSA",
      "n" => "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
    signer = Joken.Signer.create(key)
  4. With the key in JWK format on the config:

    use Mix.Config
    config :joken,
      my_rsa_public_key: [
        signer_alg: "RS256",
        key_map: %{
          "alg" => "RS256",
          "e" => "AQAB",
          "kid" => "2011-04-29",
          "kty" => "RSA",
          "n" => "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
    signer = Joken.Signer.parse_config(:my_rsa_public_key)

Other algorithms

Joken has a test suite that runs all supported algorithms. Please have a look at other algorithms from our test suite.

Also, check :erlang-jose documentation about keys: