Skip to content

Commit 8932e02

Browse files
committed
Fall back to BouncyCastle explicitly instead of modifying JCA provider context
1 parent a9f1c29 commit 8932e02

File tree

5 files changed

+51
-55
lines changed

5 files changed

+51
-55
lines changed

webauthn-server-core/src/main/java/com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ private boolean verifySignature(JsonWebSignatureCustom jws) {
107107

108108
Signature signatureVerifier;
109109
try {
110-
signatureVerifier = Signature.getInstance(signatureAlgorithmName);
110+
signatureVerifier = Crypto.getSignature(signatureAlgorithmName);
111111
} catch (NoSuchAlgorithmException e) {
112112
throw ExceptionUtil.wrapAndLog(log, "Failed to get a Signature instance for " + signatureAlgorithmName, e);
113113
}

webauthn-server-core/src/main/java/com/yubico/webauthn/Crypto.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,21 @@
3535
import java.math.BigInteger;
3636
import java.nio.charset.StandardCharsets;
3737
import java.security.GeneralSecurityException;
38+
import java.security.KeyFactory;
39+
import java.security.NoSuchAlgorithmException;
40+
import java.security.Provider;
3841
import java.security.PublicKey;
3942
import java.security.Signature;
4043
import java.security.cert.X509Certificate;
4144
import java.security.spec.ECFieldFp;
4245
import java.security.spec.ECParameterSpec;
4346
import java.security.spec.EllipticCurve;
4447
import lombok.experimental.UtilityClass;
48+
import lombok.extern.slf4j.Slf4j;
49+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
4550

4651
@UtilityClass
52+
@Slf4j
4753
final class Crypto
4854
{
4955
// Values from https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/mathematical-routines-for-the-nist-prime-elliptic-curves.cfm
@@ -54,6 +60,47 @@ final class Crypto
5460
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948", 10),
5561
new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291", 10));
5662

63+
/*
64+
* TODO: Delete this in the next major version release
65+
*/
66+
private static class BouncyCastleLoader {
67+
private static Provider getProvider() {
68+
return new BouncyCastleProvider();
69+
}
70+
}
71+
72+
/*
73+
* TODO: Delete this in the next major version release
74+
*/
75+
public static KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException {
76+
try {
77+
return KeyFactory.getInstance(algorithm);
78+
} catch (NoSuchAlgorithmException e) {
79+
log.debug("Caught {}. Attempting fallback to BouncyCastle...", e.toString());
80+
try {
81+
return KeyFactory.getInstance(algorithm, BouncyCastleLoader.getProvider());
82+
} catch (NoSuchAlgorithmException e2) {
83+
throw e;
84+
}
85+
}
86+
}
87+
88+
/*
89+
* TODO: Delete this in the next major version release
90+
*/
91+
public static Signature getSignature(String algorithm) throws NoSuchAlgorithmException {
92+
try {
93+
return Signature.getInstance(algorithm);
94+
} catch (NoSuchAlgorithmException e) {
95+
log.debug("Caught {}. Attempting fallback to BouncyCastle...", e.toString());
96+
try {
97+
return Signature.getInstance(algorithm, BouncyCastleLoader.getProvider());
98+
} catch (NoSuchAlgorithmException e2) {
99+
throw e;
100+
}
101+
}
102+
}
103+
57104
static boolean isP256(ECParameterSpec params) {
58105
return P256.equals(params.getCurve());
59106
}

webauthn-server-core/src/main/java/com/yubico/webauthn/PackedAttestationStatementVerifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ private boolean verifyX5cSignature(AttestationObject attestationObject, ByteArra
173173
final String signatureAlgorithmName = WebAuthnCodecs.getJavaAlgorithmName(sigAlg);
174174
Signature signatureVerifier;
175175
try {
176-
signatureVerifier = Signature.getInstance(signatureAlgorithmName);
176+
signatureVerifier = Crypto.getSignature(signatureAlgorithmName);
177177
} catch (NoSuchAlgorithmException e) {
178178
throw ExceptionUtil.wrapAndLog(log, "Failed to get a Signature instance for " + signatureAlgorithmName, e);
179179
}

webauthn-server-core/src/main/java/com/yubico/webauthn/RelyingParty.java

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,9 @@
5050
import lombok.NonNull;
5151
import lombok.Value;
5252
import lombok.extern.slf4j.Slf4j;
53-
import org.bouncycastle.jce.provider.BouncyCastleProvider;
54-
5553
import java.net.MalformedURLException;
5654
import java.net.URL;
57-
import java.security.KeyFactory;
58-
import java.security.NoSuchAlgorithmException;
5955
import java.security.SecureRandom;
60-
import java.security.Security;
6156
import java.util.ArrayList;
6257
import java.util.Arrays;
6358
import java.util.Collections;
@@ -73,11 +68,6 @@
7368
* This class has no mutable state. An instance of this class may therefore be thought of as a container for specialized
7469
* versions (function closures) of these four operations rather than a stateful object.
7570
* </p>
76-
*
77-
* NOTE: Constructing this class may cause side effects in some cases.
78-
* If the JCA security providers do not provide the <code>RSA</code>, <code>EC</code> and <code>EdDSA</code> algorithms,
79-
* this class will attempt to create and add a {@link BouncyCastleProvider} to the global JCA context.
80-
* If the {@link BouncyCastleProvider} class does not exist in the classpath, a warning will be logged.
8171
*/
8272
@Slf4j
8373
@Builder(toBuilder = true)
@@ -424,8 +414,6 @@ private RelyingParty(
424414
this.allowUnrequestedExtensions = allowUnrequestedExtensions;
425415
this.allowUntrustedAttestation = allowUntrustedAttestation;
426416
this.validateSignatureCounter = validateSignatureCounter;
427-
428-
tryLoadBouncyCastleIfNeeded();
429417
}
430418

431419
private static ByteArray generateChallenge() {
@@ -434,45 +422,6 @@ private static ByteArray generateChallenge() {
434422
return new ByteArray(bytes);
435423
}
436424

437-
/*
438-
* TODO: Delete this in the next major version release
439-
*/
440-
private static void tryLoadBouncyCastleIfNeeded() {
441-
String[] algs = new String[]{"RSA", "EC", "EdDSA"};
442-
443-
for (String alg : algs) {
444-
try {
445-
KeyFactory.getInstance(alg);
446-
} catch (NoSuchAlgorithmException e) {
447-
log.debug("JCA algorithm provider not available: {}. Attempting to load BouncyCastle...", alg);
448-
try {
449-
BouncyCastleLoader.loadBouncyCastleProvider();
450-
log.debug("Successfully loaded BouncyCastle provider.");
451-
} catch (NoClassDefFoundError e2) {
452-
log.info("Failed to load BouncyCastle security provider. Some key decoding and/or signature verification may fail.", e2);
453-
}
454-
break;
455-
}
456-
}
457-
458-
for (String alg : algs) {
459-
try {
460-
KeyFactory.getInstance(alg);
461-
} catch (NoSuchAlgorithmException e) {
462-
log.warn("JCA algorithm provider not available: {}. Keys and signatures using this algorithm will cause crashes.", alg);
463-
}
464-
}
465-
}
466-
467-
/*
468-
* TODO: Delete this in the next major version release
469-
*/
470-
private static class BouncyCastleLoader {
471-
private static void loadBouncyCastleProvider() {
472-
Security.addProvider(new BouncyCastleProvider());
473-
}
474-
}
475-
476425
public PublicKeyCredentialCreationOptions startRegistration(StartRegistrationOptions startRegistrationOptions) {
477426
PublicKeyCredentialCreationOptionsBuilder builder = PublicKeyCredentialCreationOptions.builder()
478427
.rp(identity)

webauthn-server-core/src/main/java/com/yubico/webauthn/WebAuthnCodecs.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ private static PublicKey importCoseRsaPublicKey(CBORObject cose) throws NoSuchAl
8686
new BigInteger(1, cose.get(CBORObject.FromObject(-1)).GetByteString()),
8787
new BigInteger(1, cose.get(CBORObject.FromObject(-2)).GetByteString())
8888
);
89-
return KeyFactory.getInstance("RSA").generatePublic(spec);
89+
return Crypto.getKeyFactory("RSA").generatePublic(spec);
9090
}
9191

9292
private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException {
@@ -109,7 +109,7 @@ private static PublicKey importCoseEd25519PublicKey(CBORObject cose) throws Inva
109109
.concat(new ByteArray(new byte[]{ 0x03, (byte) (rawKey.size() + 1), 0}))
110110
.concat(rawKey);
111111

112-
KeyFactory kFact = KeyFactory.getInstance("EdDSA");
112+
KeyFactory kFact = Crypto.getKeyFactory("EdDSA");
113113
return kFact.generatePublic(new X509EncodedKeySpec(x509Key.getBytes()));
114114
}
115115

0 commit comments

Comments
 (0)