Skip to content

Commit 72e5c5b

Browse files
author
Daniel DeGroff
committed
Add Signer.getKid() so the JWTEncoder can add the 'kid' header automatically if desired.
1 parent f52df99 commit 72e5c5b

File tree

11 files changed

+213
-19
lines changed

11 files changed

+213
-19
lines changed

CHANGES

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
FusionAuth JWT Changes
22

3+
Changes in 3.1.0
4+
5+
* 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.
6+
37
Changes in 3.0.4
48
* Add PEM.encode(Certificate certificate) and PEMEncoder.encode(Certificate certificate)
59

build.savant

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ savantVersion = "1.0.0"
1818
jacksonVersion = "2.9.8"
1919
jacksonAnnotationVersion = "2.9.6"
2020

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

2323
workflow {
2424
standard()

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>io.fusionauth</groupId>
88
<artifactId>fusionauth-jwt</artifactId>
9-
<version>3.0.4</version>
9+
<version>3.1.0</version>
1010
<packaging>jar</packaging>
1111

1212
<name>FusionAuth JWT</name>

src/main/java/io/fusionauth/jwt/JWTEncoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class JWTEncoder {
3838
* @return the encoded JWT string.
3939
*/
4040
public String encode(JWT jwt, Signer signer) {
41-
return encode(jwt, signer, null);
41+
return encode(jwt, signer, h -> h.set("kid", signer.getKid()));
4242
}
4343

4444
/**

src/main/java/io/fusionauth/jwt/Signer.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, FusionAuth, All Rights Reserved
2+
* Copyright (c) 2016-2019, FusionAuth, All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,6 +32,15 @@ public interface Signer {
3232
*/
3333
Algorithm getAlgorithm();
3434

35+
/**
36+
* Return the kid used for this signer.
37+
*
38+
* @return the kid
39+
*/
40+
default String getKid() {
41+
throw new UnsupportedOperationException();
42+
}
43+
3544
/**
3645
* Sign the provided message and return the signature.
3746
*

src/main/java/io/fusionauth/jwt/UnsecuredSigner.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public Algorithm getAlgorithm() {
3030
return Algorithm.none;
3131
}
3232

33+
@Override
34+
public String getKid() {
35+
return null;
36+
}
37+
3338
@Override
3439
public byte[] sign(String payload) {
3540
return new byte[0];

src/main/java/io/fusionauth/jwt/domain/Header.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ public String get(String name) {
7070
*/
7171
@JsonAnySetter
7272
public Header set(String name, String value) {
73+
if (name == null || value == null) {
74+
return this;
75+
}
76+
7377
properties.put(name, value);
7478
return this;
7579
}

src/main/java/io/fusionauth/jwt/ec/ECSigner.java

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,16 @@
3939
public class ECSigner implements Signer {
4040
private final Algorithm algorithm;
4141

42-
private ECPrivateKey privateKey;
42+
private final String kid;
4343

44-
private ECSigner(Algorithm algorithm, String privateKey) {
44+
private final ECPrivateKey privateKey;
45+
46+
private ECSigner(Algorithm algorithm, String privateKey, String kid) {
4547
Objects.requireNonNull(algorithm);
4648
Objects.requireNonNull(privateKey);
4749

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

63+
/**
64+
* Build a new EC signer using a SHA-256 hash.
65+
*
66+
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
67+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
68+
* @return a new EC signer.
69+
*/
70+
public static ECSigner newSHA256Signer(String privateKey, String kid) {
71+
return new ECSigner(Algorithm.ES256, privateKey, kid);
72+
}
73+
6074
/**
6175
* Build a new EC signer using a SHA-256 hash.
6276
*
6377
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
6478
* @return a new EC signer.
6579
*/
6680
public static ECSigner newSHA256Signer(String privateKey) {
67-
return new ECSigner(Algorithm.ES256, privateKey);
81+
return new ECSigner(Algorithm.ES256, privateKey, null);
82+
}
83+
84+
/**
85+
* Build a new EC signer using a SHA-384 hash.
86+
*
87+
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
88+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
89+
* @return a new EC signer.
90+
*/
91+
public static ECSigner newSHA384Signer(String privateKey, String kid) {
92+
return new ECSigner(Algorithm.ES384, privateKey, kid);
6893
}
6994

7095
/**
@@ -74,7 +99,18 @@ public static ECSigner newSHA256Signer(String privateKey) {
7499
* @return a new EC signer.
75100
*/
76101
public static ECSigner newSHA384Signer(String privateKey) {
77-
return new ECSigner(Algorithm.ES384, privateKey);
102+
return new ECSigner(Algorithm.ES384, privateKey, null);
103+
}
104+
105+
/**
106+
* Build a new EC signer using a SHA-512 hash.
107+
*
108+
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
109+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
110+
* @return a new EC signer.
111+
*/
112+
public static ECSigner newSHA512Signer(String privateKey, String kid) {
113+
return new ECSigner(Algorithm.ES512, privateKey, kid);
78114
}
79115

80116
/**
@@ -84,14 +120,19 @@ public static ECSigner newSHA384Signer(String privateKey) {
84120
* @return a new EC signer.
85121
*/
86122
public static ECSigner newSHA512Signer(String privateKey) {
87-
return new ECSigner(Algorithm.ES512, privateKey);
123+
return new ECSigner(Algorithm.ES512, privateKey, null);
88124
}
89125

90126
@Override
91127
public Algorithm getAlgorithm() {
92128
return algorithm;
93129
}
94130

131+
@Override
132+
public String getKid() {
133+
return kid;
134+
}
135+
95136
@Override
96137
public byte[] sign(String message) {
97138
Objects.requireNonNull(message);

src/main/java/io/fusionauth/jwt/hmac/HMACSigner.java

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,51 @@
3333
* @author Daniel DeGroff
3434
*/
3535
public class HMACSigner implements Signer {
36-
3736
private final Algorithm algorithm;
3837

38+
private final String kid;
39+
3940
private byte[] secret;
4041

41-
private HMACSigner(Algorithm algorithm, String secret) {
42+
private HMACSigner(Algorithm algorithm, String secret, String kid) {
4243
Objects.requireNonNull(algorithm);
4344
Objects.requireNonNull(secret);
4445

4546
this.algorithm = algorithm;
47+
this.kid = kid;
4648
this.secret = secret.getBytes(StandardCharsets.UTF_8);
4749
}
4850

51+
/**
52+
* Build a new HMAC signer using a SHA-256 hash.
53+
*
54+
* @param secret The secret used to generate the HMAC hash.
55+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
56+
* @return a new HMAC signer.
57+
*/
58+
public static HMACSigner newSHA256Signer(String secret, String kid) {
59+
return new HMACSigner(Algorithm.HS256, secret, kid);
60+
}
61+
4962
/**
5063
* Build a new HMAC signer using a SHA-256 hash.
5164
*
5265
* @param secret The secret used to generate the HMAC hash.
5366
* @return a new HMAC signer.
5467
*/
5568
public static HMACSigner newSHA256Signer(String secret) {
56-
return new HMACSigner(Algorithm.HS256, secret);
69+
return new HMACSigner(Algorithm.HS256, secret, null);
70+
}
71+
72+
/**
73+
* Build a new HMAC signer using a SHA-384 hash.
74+
*
75+
* @param secret The secret used to generate the HMAC hash.
76+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
77+
* @return a new HMAC signer.
78+
*/
79+
public static HMACSigner newSHA384Signer(String secret, String kid) {
80+
return new HMACSigner(Algorithm.HS384, secret, kid);
5781
}
5882

5983
/**
@@ -63,7 +87,18 @@ public static HMACSigner newSHA256Signer(String secret) {
6387
* @return a new HMAC signer.
6488
*/
6589
public static HMACSigner newSHA384Signer(String secret) {
66-
return new HMACSigner(Algorithm.HS384, secret);
90+
return new HMACSigner(Algorithm.HS384, secret, null);
91+
}
92+
93+
/**
94+
* Build a new HMAC signer using a SHA-512 hash.
95+
*
96+
* @param secret The secret used to generate the HMAC hash.
97+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
98+
* @return a new HMAC signer.
99+
*/
100+
public static HMACSigner newSHA512Signer(String secret, String kid) {
101+
return new HMACSigner(Algorithm.HS512, secret, kid);
67102
}
68103

69104
/**
@@ -73,14 +108,19 @@ public static HMACSigner newSHA384Signer(String secret) {
73108
* @return a new HMAC signer.
74109
*/
75110
public static HMACSigner newSHA512Signer(String secret) {
76-
return new HMACSigner(Algorithm.HS512, secret);
111+
return new HMACSigner(Algorithm.HS512, secret, null);
77112
}
78113

79114
@Override
80115
public Algorithm getAlgorithm() {
81116
return algorithm;
82117
}
83118

119+
@Override
120+
public String getKid() {
121+
return kid;
122+
}
123+
84124
@Override
85125
public byte[] sign(String message) {
86126
Objects.requireNonNull(message);

src/main/java/io/fusionauth/jwt/rsa/RSASigner.java

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,16 @@
3939
public class RSASigner implements Signer {
4040
private final Algorithm algorithm;
4141

42-
private RSAPrivateKey privateKey;
42+
private final String kid;
4343

44-
private RSASigner(Algorithm algorithm, String privateKey) {
44+
private final RSAPrivateKey privateKey;
45+
46+
private RSASigner(Algorithm algorithm, String privateKey, String kid) {
4547
Objects.requireNonNull(algorithm);
4648
Objects.requireNonNull(privateKey);
4749

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

64+
/**
65+
* Build a new RSA signer using a SHA-256 hash.
66+
*
67+
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
68+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
69+
* @return a new RSA signer.
70+
*/
71+
public static RSASigner newSHA256Signer(String privateKey, String kid) {
72+
return new RSASigner(Algorithm.RS256, privateKey, kid);
73+
}
74+
6175
/**
6276
* Build a new RSA signer using a SHA-256 hash.
6377
*
6478
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
6579
* @return a new RSA signer.
6680
*/
6781
public static RSASigner newSHA256Signer(String privateKey) {
68-
return new RSASigner(Algorithm.RS256, privateKey);
82+
return new RSASigner(Algorithm.RS256, privateKey, null);
83+
}
84+
85+
/**
86+
* Build a new RSA signer using a SHA-384 hash.
87+
*
88+
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
89+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
90+
* @return a new RSA signer.
91+
*/
92+
public static RSASigner newSHA384Signer(String privateKey, String kid) {
93+
return new RSASigner(Algorithm.RS384, privateKey, kid);
6994
}
7095

7196
/**
@@ -75,7 +100,18 @@ public static RSASigner newSHA256Signer(String privateKey) {
75100
* @return a new RSA signer.
76101
*/
77102
public static RSASigner newSHA384Signer(String privateKey) {
78-
return new RSASigner(Algorithm.RS384, privateKey);
103+
return new RSASigner(Algorithm.RS384, privateKey, null);
104+
}
105+
106+
/**
107+
* Build a new RSA signer using a SHA-512 hash.
108+
*
109+
* @param privateKey The private key PEM expected to be in PKCS#1 or PKCS#8 format.
110+
* @param kid The key identifier. This will be used by the JWTEncoder to write the 'kid' header.
111+
* @return a new RSA signer.
112+
*/
113+
public static RSASigner newSHA512Signer(String privateKey, String kid) {
114+
return new RSASigner(Algorithm.RS512, privateKey, kid);
79115
}
80116

81117
/**
@@ -85,14 +121,19 @@ public static RSASigner newSHA384Signer(String privateKey) {
85121
* @return a new RSA signer.
86122
*/
87123
public static RSASigner newSHA512Signer(String privateKey) {
88-
return new RSASigner(Algorithm.RS512, privateKey);
124+
return new RSASigner(Algorithm.RS512, privateKey, null);
89125
}
90126

91127
@Override
92128
public Algorithm getAlgorithm() {
93129
return algorithm;
94130
}
95131

132+
@Override
133+
public String getKid() {
134+
return kid;
135+
}
136+
96137
public byte[] sign(String message) {
97138
Objects.requireNonNull(message);
98139

0 commit comments

Comments
 (0)