Keys

Reading PEM formatted keys

Before explaining digital signatures, you need to read public/private keypairs and convert them to usable objects. Buddy has limited support for reading:

  • RSA keypair
  • ECDSA keypair

RSA Keypair

An RSA keypair is obviously used for RSA encryption/decryption, but it is also used for making digital signatures with RSA-derived algorithms.

Read keys:

(require '[buddy.core.keys :as keys])

;; The last parameter is optional and is only mandatory
;; if a private key is encrypted.
(def privkey (keys/private-key "test/_files/privkey.3des.rsa.pem" "secret")
(def pubkey (keys/public-key "test/_files/pubkey.3des.rsa.pem"))

Generate a RSA Keypair using openssl:

# Generate AES-256 encrypted private key
openssl genrsa -aes256 -out privkey.pem 2048

# Generate public key from previously created private key.
openssl rsa -pubout -in privkey.pem -out pubkey.pem

ECDSA Keypair

Like RSA keypairs, ECDSA is also used for making digital signatures and can be read like in the RSA examples.

Read keys:

(require '[buddy.core.keys :as keys])

;; The last parameter is optional and is only mandatory
;; if a private key is encrypted.
(def privkey (keys/private-key "test/_files/privkey.ecdsa.pem" "secret")
(def pubkey (keys/public-key "test/_files/pubkey.ecdsa.pem"))

Generate a ECDSA Keypair using openssl:

# Generate a params file
openssl ecparam -name prime256v1 -out ecparams.pem

# Generate a private key from params file
openssl ecparam -in ecparams.pem -genkey -noout -out ecprivkey.pem

# Generate a public key from private key
openssl ec -in ecprivkey.pem -pubout -out ecpubkey.pem

Json Web Key (JWK)

A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key of different types.

buddy-core provides functions for reading and saving JCA keys in JWK format

Currently supported JWK key types are

  • RSA key pairs (No RSA-CRT support yet)
  • OKP key pairs (Ed25519)
  • EC key pairs (P-256, P-384, P-521 curves)

Example of JWS signing for Ed25519 keys:

(require '[buddy.core.keys :as keys])

(def edkey {:kty "OKP",
            :crv "Ed25519",
            :d "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
            :x "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"})

(def privkey (keys/jwk->private-key edkey))

You can also convert from PEM to JWK like this:

(require '[buddy.core.keys :as keys])

(def prv (keys/private-key "private.pem"))
(def pub (keys/public-key "public.pem"))

;; JWK requires both public and private keys for export
(def jwk (keys/jwk prv pub))
(def jwk-pub (keys/public-key->jwk pub))

Also, you can generate and save keys in JWK format like this

(require '[buddy.core.keys :as keys])
(import 'java.security.KeyPairGenerator)
(import 'java.security.SecureRandom)

(defn generate-keypair-ed25519
  []
  (let [kg (KeyPairGenerator/getInstance "EdDSA" "EdDSA")]
    (.initialize kg
                 256
                 ;; JDK8 only, use getInstance on JDK7 (make sure it's true random source)
                 (SecureRandom/getInstanceStrong))
    (.genKeyPair kg)))

(let [pair (generate-keypair-ed25519)]
  (keys/jwk (.getPrivate pair) (.getPublic pair)))

;; =>
;; {:kty "OKP",
;;  :crv "Ed25519",
;;  :d "5q3yhCdSDMj9Za9jJE0vhfExlTV8JeSe6XnfblAFkPY",
;;  :x "JbbhB16SaghHiGHx3FutVMfVTgu9-SCtZGfZyoDZSbQ"}

You can also calculate JWK thumbprint using jwk-thumbprint function

(require '[buddy.core.keys :as keys])
(require '[buddy.core.codecs :as codecs])

(def edkey {:kty "OKP",
            :crv "Ed25519",
            :d "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
            :x "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"})

(-> (keys/jwk-thumbprint edkey)
    (codecs/bytes->hex))

;; => "90facafea9b1556698540f70c0117a22ea37bd5cf3ed3c47093c1707282b4b89"