Skip to content

Commit

Permalink
Add Signer.getKid() so the JWTEncoder can add the 'kid' header automa…
Browse files Browse the repository at this point in the history
…tically if desired.
  • Loading branch information
Daniel DeGroff committed Apr 25, 2019
1 parent f52df99 commit 72e5c5b
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 19 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
FusionAuth JWT Changes

Changes in 3.1.0

* Added Signer.getKid() with a default impl to throw UnsupportedOperationException(), this allows the JWTEncoder.encode to add a 'kid' by default. This makes it more consistent with the JWTDecoder.

Changes in 3.0.4
* Add PEM.encode(Certificate certificate) and PEMEncoder.encode(Certificate certificate)

Expand Down
2 changes: 1 addition & 1 deletion build.savant
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ savantVersion = "1.0.0"
jacksonVersion = "2.9.8"
jacksonAnnotationVersion = "2.9.6"

project(group: "io.fusionauth", name: "fusionauth-jwt", version: "3.0.4", licenses: ["ApacheV2_0"]) {
project(group: "io.fusionauth", name: "fusionauth-jwt", version: "3.1.0", licenses: ["ApacheV2_0"]) {

workflow {
standard()
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.fusionauth</groupId>
<artifactId>fusionauth-jwt</artifactId>
<version>3.0.4</version>
<version>3.1.0</version>
<packaging>jar</packaging>

<name>FusionAuth JWT</name>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/fusionauth/jwt/JWTEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class JWTEncoder {
* @return the encoded JWT string.
*/
public String encode(JWT jwt, Signer signer) {
return encode(jwt, signer, null);
return encode(jwt, signer, h -> h.set("kid", signer.getKid()));
}

/**
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/io/fusionauth/jwt/Signer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, FusionAuth, All Rights Reserved
* Copyright (c) 2016-2019, FusionAuth, All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,6 +32,15 @@ public interface Signer {
*/
Algorithm getAlgorithm();

/**
* Return the kid used for this signer.
*
* @return the kid
*/
default String getKid() {
throw new UnsupportedOperationException();
}

/**
* Sign the provided message and return the signature.
*
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/fusionauth/jwt/UnsecuredSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public Algorithm getAlgorithm() {
return Algorithm.none;
}

@Override
public String getKid() {
return null;
}

@Override
public byte[] sign(String payload) {
return new byte[0];
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/fusionauth/jwt/domain/Header.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ public String get(String name) {
*/
@JsonAnySetter
public Header set(String name, String value) {
if (name == null || value == null) {
return this;
}

properties.put(name, value);
return this;
}
Expand Down
51 changes: 46 additions & 5 deletions src/main/java/io/fusionauth/jwt/ec/ECSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@
public class ECSigner implements Signer {
private final Algorithm algorithm;

private ECPrivateKey privateKey;
private final String kid;

private ECSigner(Algorithm algorithm, String privateKey) {
private final ECPrivateKey privateKey;

private ECSigner(Algorithm algorithm, String privateKey, String kid) {
Objects.requireNonNull(algorithm);
Objects.requireNonNull(privateKey);

this.algorithm = algorithm;
this.kid = kid;
PEM pem = PEM.decode(privateKey);
if (pem.privateKey == null) {
throw new MissingPrivateKeyException("The provided PEM encoded string did not contain a private key.");
Expand All @@ -57,14 +60,36 @@ private ECSigner(Algorithm algorithm, String privateKey) {
this.privateKey = pem.getPrivateKey();
}

/**
* Build a new EC signer using a SHA-256 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new EC signer.
*/
public static ECSigner newSHA256Signer(String privateKey, String kid) {
return new ECSigner(Algorithm.ES256, privateKey, kid);
}

/**
* Build a new EC signer using a SHA-256 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @return a new EC signer.
*/
public static ECSigner newSHA256Signer(String privateKey) {
return new ECSigner(Algorithm.ES256, privateKey);
return new ECSigner(Algorithm.ES256, privateKey, null);
}

/**
* Build a new EC signer using a SHA-384 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new EC signer.
*/
public static ECSigner newSHA384Signer(String privateKey, String kid) {
return new ECSigner(Algorithm.ES384, privateKey, kid);
}

/**
Expand All @@ -74,7 +99,18 @@ public static ECSigner newSHA256Signer(String privateKey) {
* @return a new EC signer.
*/
public static ECSigner newSHA384Signer(String privateKey) {
return new ECSigner(Algorithm.ES384, privateKey);
return new ECSigner(Algorithm.ES384, privateKey, null);
}

/**
* Build a new EC signer using a SHA-512 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new EC signer.
*/
public static ECSigner newSHA512Signer(String privateKey, String kid) {
return new ECSigner(Algorithm.ES512, privateKey, kid);
}

/**
Expand All @@ -84,14 +120,19 @@ public static ECSigner newSHA384Signer(String privateKey) {
* @return a new EC signer.
*/
public static ECSigner newSHA512Signer(String privateKey) {
return new ECSigner(Algorithm.ES512, privateKey);
return new ECSigner(Algorithm.ES512, privateKey, null);
}

@Override
public Algorithm getAlgorithm() {
return algorithm;
}

@Override
public String getKid() {
return kid;
}

@Override
public byte[] sign(String message) {
Objects.requireNonNull(message);
Expand Down
50 changes: 45 additions & 5 deletions src/main/java/io/fusionauth/jwt/hmac/HMACSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,51 @@
* @author Daniel DeGroff
*/
public class HMACSigner implements Signer {

private final Algorithm algorithm;

private final String kid;

private byte[] secret;

private HMACSigner(Algorithm algorithm, String secret) {
private HMACSigner(Algorithm algorithm, String secret, String kid) {
Objects.requireNonNull(algorithm);
Objects.requireNonNull(secret);

this.algorithm = algorithm;
this.kid = kid;
this.secret = secret.getBytes(StandardCharsets.UTF_8);
}

/**
* Build a new HMAC signer using a SHA-256 hash.
*
* @param secret The secret used to generate the HMAC hash.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new HMAC signer.
*/
public static HMACSigner newSHA256Signer(String secret, String kid) {
return new HMACSigner(Algorithm.HS256, secret, kid);
}

/**
* Build a new HMAC signer using a SHA-256 hash.
*
* @param secret The secret used to generate the HMAC hash.
* @return a new HMAC signer.
*/
public static HMACSigner newSHA256Signer(String secret) {
return new HMACSigner(Algorithm.HS256, secret);
return new HMACSigner(Algorithm.HS256, secret, null);
}

/**
* Build a new HMAC signer using a SHA-384 hash.
*
* @param secret The secret used to generate the HMAC hash.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new HMAC signer.
*/
public static HMACSigner newSHA384Signer(String secret, String kid) {
return new HMACSigner(Algorithm.HS384, secret, kid);
}

/**
Expand All @@ -63,7 +87,18 @@ public static HMACSigner newSHA256Signer(String secret) {
* @return a new HMAC signer.
*/
public static HMACSigner newSHA384Signer(String secret) {
return new HMACSigner(Algorithm.HS384, secret);
return new HMACSigner(Algorithm.HS384, secret, null);
}

/**
* Build a new HMAC signer using a SHA-512 hash.
*
* @param secret The secret used to generate the HMAC hash.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new HMAC signer.
*/
public static HMACSigner newSHA512Signer(String secret, String kid) {
return new HMACSigner(Algorithm.HS512, secret, kid);
}

/**
Expand All @@ -73,14 +108,19 @@ public static HMACSigner newSHA384Signer(String secret) {
* @return a new HMAC signer.
*/
public static HMACSigner newSHA512Signer(String secret) {
return new HMACSigner(Algorithm.HS512, secret);
return new HMACSigner(Algorithm.HS512, secret, null);
}

@Override
public Algorithm getAlgorithm() {
return algorithm;
}

@Override
public String getKid() {
return kid;
}

@Override
public byte[] sign(String message) {
Objects.requireNonNull(message);
Expand Down
51 changes: 46 additions & 5 deletions src/main/java/io/fusionauth/jwt/rsa/RSASigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@
public class RSASigner implements Signer {
private final Algorithm algorithm;

private RSAPrivateKey privateKey;
private final String kid;

private RSASigner(Algorithm algorithm, String privateKey) {
private final RSAPrivateKey privateKey;

private RSASigner(Algorithm algorithm, String privateKey, String kid) {
Objects.requireNonNull(algorithm);
Objects.requireNonNull(privateKey);

this.algorithm = algorithm;
this.kid = kid;
PEM pem = PEM.decode(privateKey);
if (pem.privateKey == null) {
throw new MissingPrivateKeyException("The provided PEM encoded string did not contain a private key.");
Expand All @@ -58,14 +61,36 @@ private RSASigner(Algorithm algorithm, String privateKey) {
}
}

/**
* Build a new RSA signer using a SHA-256 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new RSA signer.
*/
public static RSASigner newSHA256Signer(String privateKey, String kid) {
return new RSASigner(Algorithm.RS256, privateKey, kid);
}

/**
* Build a new RSA signer using a SHA-256 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @return a new RSA signer.
*/
public static RSASigner newSHA256Signer(String privateKey) {
return new RSASigner(Algorithm.RS256, privateKey);
return new RSASigner(Algorithm.RS256, privateKey, null);
}

/**
* Build a new RSA signer using a SHA-384 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new RSA signer.
*/
public static RSASigner newSHA384Signer(String privateKey, String kid) {
return new RSASigner(Algorithm.RS384, privateKey, kid);
}

/**
Expand All @@ -75,7 +100,18 @@ public static RSASigner newSHA256Signer(String privateKey) {
* @return a new RSA signer.
*/
public static RSASigner newSHA384Signer(String privateKey) {
return new RSASigner(Algorithm.RS384, privateKey);
return new RSASigner(Algorithm.RS384, privateKey, null);
}

/**
* Build a new RSA signer using a SHA-512 hash.
*
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
* @return a new RSA signer.
*/
public static RSASigner newSHA512Signer(String privateKey, String kid) {
return new RSASigner(Algorithm.RS512, privateKey, kid);
}

/**
Expand All @@ -85,14 +121,19 @@ public static RSASigner newSHA384Signer(String privateKey) {
* @return a new RSA signer.
*/
public static RSASigner newSHA512Signer(String privateKey) {
return new RSASigner(Algorithm.RS512, privateKey);
return new RSASigner(Algorithm.RS512, privateKey, null);
}

@Override
public Algorithm getAlgorithm() {
return algorithm;
}

@Override
public String getKid() {
return kid;
}

public byte[] sign(String message) {
Objects.requireNonNull(message);

Expand Down
Loading

0 comments on commit 72e5c5b

Please sign in to comment.