Skip to content

Commit 01c6280

Browse files
committed
Decouple from the io.vertx.ext.auth.impl.jose.JWT and extract in a separate package
1 parent 05d4f82 commit 01c6280

File tree

9 files changed

+378
-259
lines changed

9 files changed

+378
-259
lines changed

vertx-auth-common/src/main/java/io/vertx/ext/auth/impl/jose/JWK.java

Lines changed: 43 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
import io.vertx.ext.auth.PubSecKeyOptions;
2424
import io.vertx.ext.auth.impl.CertificateHelper;
2525
import io.vertx.ext.auth.impl.asn.ASN1;
26+
import io.vertx.ext.auth.impl.jose.algo.MacSigningAlgorithm;
27+
import io.vertx.ext.auth.impl.jose.algo.PubKeySigningAlgorithm;
28+
import io.vertx.ext.auth.impl.jose.algo.Signer;
29+
import io.vertx.ext.auth.impl.jose.algo.SigningAlgorithm;
2630

2731
import javax.crypto.Mac;
2832
import javax.crypto.spec.SecretKeySpec;
@@ -33,8 +37,11 @@
3337
import java.security.cert.*;
3438
import java.security.spec.*;
3539
import java.util.*;
40+
import java.util.concurrent.Callable;
3641
import java.util.regex.Matcher;
3742
import java.util.regex.Pattern;
43+
import java.util.stream.Collectors;
44+
import java.util.stream.Stream;
3845

3946
import static io.vertx.ext.auth.impl.Codec.base64MimeDecode;
4047
import static io.vertx.ext.auth.impl.Codec.base64UrlDecode;
@@ -60,65 +67,7 @@
6067
*/
6168
public final class JWK {
6269

63-
private static final Logger LOG = LoggerFactory.getLogger(JWK.class);
64-
65-
private static final Map<String, List<String>> ALG_ALIAS = new HashMap<String, List<String>>() {{
66-
put("HS256", Arrays.asList(
67-
// JCE
68-
"HMacSHA256",
69-
// OID
70-
"1.2.840.113549.2.9"));
71-
put("HS384", Arrays.asList(
72-
// JCE
73-
"HMacSHA384",
74-
// OID
75-
"1.2.840.113549.2.10"));
76-
put("HS512", Arrays.asList(
77-
// JCE
78-
"HMacSHA512",
79-
// OID
80-
"1.2.840.113549.2.11"));
81-
put("RS256", Arrays.asList(
82-
// JCE
83-
"SHA256withRSA",
84-
// OID
85-
"1.2.840.113549.1.1.11"));
86-
put("RS384", Arrays.asList(
87-
// JCE
88-
"SHA384withRSA",
89-
// OID
90-
"1.2.840.113549.1.1.12"));
91-
put("RS512", Arrays.asList(
92-
// JCE
93-
"SHA512withRSA",
94-
// OID
95-
"1.2.840.113549.1.1.13"));
96-
put("ES256K", Collections.singletonList("SHA256withECDSA"));
97-
put("ES256", Arrays.asList(
98-
// JCE
99-
"SHA256withECDSA",
100-
// OID
101-
"1.2.840.10045.4.3.2"));
102-
put("ES384", Arrays.asList(
103-
// JCE
104-
"SHA384withECDSA",
105-
// OID
106-
"1.2.840.10045.4.3.3"));
107-
put("ES512", Arrays.asList(
108-
// JCE
109-
"SHA512withECDSA",
110-
// OID
111-
"1.2.840.10045.4.3.4"));
112-
}};
113-
114-
private static boolean invalidAlgAlias(String alg, String alias) {
115-
for (String expected : ALG_ALIAS.get(alias)) {
116-
if (alg.equalsIgnoreCase(expected)) {
117-
return false;
118-
}
119-
}
120-
return true;
121-
}
70+
static final Logger LOG = LoggerFactory.getLogger(JWK.class);
12271

12372
// JSON JWK properties
12473
private final String kid;
@@ -136,61 +85,22 @@ private static boolean invalidAlgAlias(String alg, String alias) {
13685
private final SigningAlgorithm signingAlgorithm;
13786

13887
public static List<JWK> load(KeyStore keyStore, String keyStorePassword, Map<String, String> passwordProtection) {
139-
final List<JWK> keys = new ArrayList<>();
140-
141-
// load MACs
142-
for (String alias : Arrays.asList("HS256", "HS384", "HS512")) {
143-
try {
144-
char[] password = password(keyStorePassword, passwordProtection, alias);
145-
final Key secretKey = keyStore.getKey(alias, password);
146-
// key store does not have the requested algorithm
147-
if (secretKey == null) {
148-
continue;
149-
}
150-
// test the algorithm
151-
String alg = secretKey.getAlgorithm();
152-
// the algorithm cannot be null, and it cannot be different from the alias list
153-
if (invalidAlgAlias(alg, alias)) {
154-
LOG.warn("The key algorithm does not match: {" + alias + ": " + alg + "}");
155-
continue;
156-
}
157-
// algorithm is valid
158-
Mac mac = Mac.getInstance(alg);
159-
mac.init(secretKey);
160-
keys.add(new JWK(alias, mac));
161-
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | InvalidKeyException e) {
162-
LOG.warn("Failed to load key for algorithm: " + alias, e);
163-
}
164-
}
165-
166-
for (String alias : Arrays.asList("RS256", "RS384", "RS512", "ES256K", "ES256", "ES384", "ES512")) {
88+
final List<Callable<SigningAlgorithm>> keys = SigningAlgorithm.create(keyStore, keyStorePassword, passwordProtection);
89+
return keys.stream().flatMap(fact -> {
16790
try {
168-
// Key pairs on keystores are stored with a certificate, so we use it to load a key pair
169-
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);
170-
// not found
171-
if (certificate == null) {
172-
continue;
91+
SigningAlgorithm algo = fact.call();
92+
if (algo instanceof PubKeySigningAlgorithm) {
93+
PubKeySigningAlgorithm psa = (PubKeySigningAlgorithm) algo;
94+
return Stream.of(new JWK(psa.name(), psa.id(), psa));
95+
} else {
96+
MacSigningAlgorithm msa = (MacSigningAlgorithm) algo;
97+
return Stream.of(new JWK(msa));
17398
}
174-
// start validation
175-
certificate.checkValidity();
176-
// verify that the algorithms match
177-
String alg = certificate.getSigAlgName();
178-
// the algorithm cannot be null, and it cannot be different from the alias list
179-
if (invalidAlgAlias(alg, alias)) {
180-
LOG.warn("The key algorithm does not match: {" + alias + ": " + alg + "}");
181-
continue;
182-
}
183-
// algorithm is valid
184-
char[] password = password(keyStorePassword, passwordProtection, alias);
185-
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password);
186-
keys.add(new JWK(alias, certificate, privateKey));
187-
} catch (ClassCastException | KeyStoreException | CertificateExpiredException | CertificateNotYetValidException |
188-
NoSuchAlgorithmException | UnrecoverableKeyException e) {
189-
LOG.warn("Failed to load key for algorithm: " + alias, e);
99+
} catch (Exception e) {
100+
LOG.warn("Failed to load key for algorithm", e);
101+
return Stream.empty();
190102
}
191-
}
192-
193-
return keys;
103+
}).collect(Collectors.toList());
194104
}
195105

196106
private static char[] password(String keyStorePassword, Map<String, String> passwordProtection, String alias) {
@@ -330,78 +240,69 @@ private static PubKeySigningAlgorithm parsePEM(String alg, KeyFactory kf, String
330240
case "CERTIFICATE":
331241
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
332242
publicKey = cf.generateCertificate(new ByteArrayInputStream(pem.getBytes(StandardCharsets.US_ASCII))).getPublicKey();
333-
return createPubKeySigningAlgorithm(alg, null, publicKey);
243+
return PubKeySigningAlgorithm.createPubKeySigningAlgorithm(alg, publicKey);
334244
case "PUBLIC KEY":
335245
case "PUBLIC RSA KEY":
336246
case "RSA PUBLIC KEY":
337247
publicKey = kf.generatePublic(new X509EncodedKeySpec(base64MimeDecode(buffer.getBytes())));
338-
return createPubKeySigningAlgorithm(alg, null, publicKey);
248+
return PubKeySigningAlgorithm.createPubKeySigningAlgorithm(alg, publicKey);
339249
case "PRIVATE KEY":
340250
case "PRIVATE RSA KEY":
341251
case "RSA PRIVATE KEY":
342252
privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(base64MimeDecode(buffer.getBytes())));
343-
return createPubKeySigningAlgorithm(alg, privateKey, null);
253+
return PubKeySigningAlgorithm.createPubKeySigningAlgorithm(alg, privateKey);
344254
default:
345255
throw new IllegalStateException("Invalid PEM content: " + kind);
346256
}
347257
}
348258

349-
private JWK(String algorithm, Mac mac) throws NoSuchAlgorithmException {
259+
private JWK(MacSigningAlgorithm algo) throws NoSuchAlgorithmException {
350260

351261
kid = null;
352-
label = algorithm + "#" + mac.hashCode();
262+
label = algo.name() + "#" + algo.mac().hashCode();
353263
use = null;
354264
kty = "oct";
355265

356-
switch (algorithm) {
266+
switch (algo.name()) {
357267
case "HS256":
358268
case "HS384":
359269
case "HS512":
360-
this.signingAlgorithm = new MacSigningAlgorithm(algorithm, mac);
270+
this.signingAlgorithm = algo;
361271
break;
362272
default:
363-
throw new NoSuchAlgorithmException("Unknown algorithm: " + algorithm);
273+
throw new NoSuchAlgorithmException("Unknown algorithm: " + algo.name());
364274
}
365275
}
366276

367-
private JWK(String algorithm, X509Certificate certificate, PrivateKey privateKey) throws NoSuchAlgorithmException {
277+
private JWK(String algorithm, String id, PubKeySigningAlgorithm algo) throws NoSuchAlgorithmException {
368278

369279
kid = null;
370-
label = privateKey != null ? algorithm + '#' + certificate.hashCode() + "-" + privateKey.hashCode() : algorithm + '#' + certificate.hashCode();
280+
label = algo.canSign() ? algorithm + '#' + id + "-" + algo.privateKey().hashCode() : algorithm + '#' + id;
371281
use = null;
372282

373-
PublicKey publicKey = certificate.getPublicKey();
374-
375-
boolean ec;
376283
switch (algorithm) {
377284
case "RS256":
378285
case "RS384":
379286
case "RS512":
380287
kty = "RSA";
381-
ec= false;
288+
signingAlgorithm = algo;
382289
break;
383290
case "PS256":
384291
case "PS384":
385292
case "PS512":
386293
kty = "RSASSA";
387-
ec= false;
294+
signingAlgorithm = algo;
388295
break;
389296
case "ES256":
390297
case "ES384":
391298
case "ES512":
392299
case "ES256K":
393300
kty = "EC";
394-
ec= true;
301+
signingAlgorithm = wrapECAlgo(algo);
395302
break;
396303
default:
397304
throw new NoSuchAlgorithmException("Unknown algorithm: " + algorithm);
398305
}
399-
PubKeySigningAlgorithm algo = createPubKeySigningAlgorithm(algorithm, privateKey, publicKey);
400-
if (ec) {
401-
signingAlgorithm = wrapECAlgo(algo);
402-
} else {
403-
signingAlgorithm = algo;
404-
}
405306
}
406307

407308
private static SigningAlgorithm wrapECAlgo(PubKeySigningAlgorithm signingAlgo) {
@@ -414,6 +315,10 @@ public String name() {
414315
return signingAlgo.name();
415316
}
416317
@Override
318+
public String id() {
319+
return signingAlgo.id();
320+
}
321+
@Override
417322
public boolean canSign() {
418323
return signingAlgo.canSign();
419324
}
@@ -422,8 +327,8 @@ public boolean canVerify() {
422327
return signingAlgo.canVerify();
423328
}
424329
@Override
425-
public Signer signer() throws GeneralSecurityException {
426-
Signer signer = signingAlgo.signer();
330+
public io.vertx.ext.auth.impl.jose.algo.Signer signer() throws GeneralSecurityException {
331+
io.vertx.ext.auth.impl.jose.algo.Signer signer = signingAlgo.signer();
427332
int len = getSignatureLength(signingAlgo.name(), signingAlgo.publicKey());
428333
return new Signer() {
429334
@Override
@@ -442,10 +347,6 @@ public boolean verify(byte[] expected, byte[] payload) throws GeneralSecurityExc
442347
};
443348
}
444349

445-
private static PubKeySigningAlgorithm createPubKeySigningAlgorithm(String alg, PrivateKey privateKey, PublicKey publicKey) {
446-
return new PubKeySigningAlgorithm(alg, privateKey, publicKey);
447-
}
448-
449350
public JWK(JsonObject json) {
450351
kid = json.getString("kid");
451352
use = json.getString("use");
@@ -568,7 +469,7 @@ private static PubKeySigningAlgorithm createRSA(String alg, JsonObject json) thr
568469
}
569470

570471
if (publicKey != null || privateKey != null) {
571-
return createPubKeySigningAlgorithm(alg, privateKey, publicKey);
472+
return PubKeySigningAlgorithm.createPubKeySigningAlgorithm(alg, privateKey, publicKey);
572473
}
573474
return null;
574475
}
@@ -593,7 +494,7 @@ private static PubKeySigningAlgorithm createEC(String alg, JsonObject json) thro
593494
}
594495

595496
if (publicKey != null || privateKey != null) {
596-
return createPubKeySigningAlgorithm(alg, privateKey, publicKey);
497+
return PubKeySigningAlgorithm.createPubKeySigningAlgorithm(alg, privateKey, publicKey);
597498
}
598499

599500
return null;
@@ -667,7 +568,7 @@ private static PubKeySigningAlgorithm createOKP(String alg, JsonObject json) thr
667568
}
668569

669570
if (publicKey != null || privateKey != null) {
670-
return createPubKeySigningAlgorithm(alg, privateKey, publicKey);
571+
return PubKeySigningAlgorithm.createPubKeySigningAlgorithm(alg, privateKey, publicKey);
671572
} else {
672573
return null;
673574
}

0 commit comments

Comments
 (0)