Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@

<properties>
<java.version>11</java.version>
<jjwt.version>0.11.5</jjwt.version>
<bouncycastle.version>1.76</bouncycastle.version>
<jackson.version>2.15.2</jackson.version>
<slf4j.version>2.0.5</slf4j.version>
<junit-jupiter.version>5.9.2</junit-jupiter.version>
<assertj.version>3.24.2</assertj.version>
<mockito.version>5.2.0</mockito.version>
<jjwt.version>0.12.5</jjwt.version>
<bouncycastle.version>1.77</bouncycastle.version>
<jackson.version>2.17.0</jackson.version>
<slf4j.version>2.0.9</slf4j.version>
<junit-jupiter.version>5.10.2</junit-jupiter.version>
<assertj.version>3.25.3</assertj.version>
<mockito.version>5.11.0</mockito.version>
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<maven-source-plugin.version>3.3.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.6.2</maven-javadoc-plugin.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@
import eu.webeid.security.exceptions.AuthTokenParseException;
import eu.webeid.security.exceptions.AuthTokenSignatureValidationException;
import eu.webeid.security.exceptions.ChallengeNullOrEmptyException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.DefaultSignatureValidatorFactory;
import io.jsonwebtoken.impl.crypto.SignatureValidator;
import io.jsonwebtoken.security.SignatureException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.security.DefaultVerifySecureDigestRequest;
import io.jsonwebtoken.security.SignatureAlgorithm;
import io.jsonwebtoken.security.VerifySecureDigestRequest;

import java.io.ByteArrayInputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
Expand Down Expand Up @@ -73,25 +74,20 @@ public void validate(String algorithm, String signature, PublicKey publicKey, St
throw new AuthTokenParseException("Unsupported signature algorithm");
}

SignatureAlgorithm signatureAlgorithm;
final SignatureAlgorithm signatureAlgorithm = (SignatureAlgorithm) Jwts.SIG.get().forKey(algorithm);
if (signatureAlgorithm == null) {
// Should not happen, see ALLOWED_SIGNATURE_ALGORITHMS check above.
throw new AuthTokenParseException("JJWT does not support signature algorithm: " + algorithm);
}
MessageDigest hashAlgorithm;
try {
signatureAlgorithm = SignatureAlgorithm.forName(algorithm);
hashAlgorithm = hashAlgorithmForName(algorithm);
} catch (SignatureException e) {
// Should not happen, see ALLOWED_SIGNATURE_ALGORITHMS check above.
throw new AuthTokenParseException("Invalid signature algorithm", e);
} catch (NoSuchAlgorithmException e) {
throw new AuthTokenParseException("Invalid hash algorithm", e);
}
if (signatureAlgorithm == null || signatureAlgorithm == SignatureAlgorithm.NONE) {
// Should not happen, see ALLOWED_SIGNATURE_ALGORITHMS check above.
throw new AuthTokenParseException("Invalid signature algorithm");
throw new AuthTokenParseException("Invalid hash algorithm", e);
}
Objects.requireNonNull(hashAlgorithm, "hashAlgorithm");
signatureAlgorithm.assertValidVerificationKey(publicKey);
final SignatureValidator signatureValidator = DefaultSignatureValidatorFactory.INSTANCE
.createSignatureValidator(signatureAlgorithm, publicKey);

final byte[] decodedSignature = decodeBase64(signature);

final byte[] originHash = hashAlgorithm.digest(originBytes);
Expand All @@ -100,9 +96,13 @@ public void validate(String algorithm, String signature, PublicKey publicKey, St

// Note that in case of ECDSA, the eID card outputs raw R||S, but JCA's SHA384withECDSA signature
// validation implementation requires the signature in DER encoding.
// JJWT's EllipticCurveProvider.transcodeSignatureToDER() internally takes care of transcoding
// raw R||S to DER as needed inside EllipticCurveProvider.isValid().
if (!signatureValidator.isValid(concatSignedFields, decodedSignature)) {
// JJWT internally takes care of transcoding raw R||S to DER as needed.
final VerifySecureDigestRequest<PublicKey> verificationRequest =
new DefaultVerifySecureDigestRequest<>(
new ByteArrayInputStream(concatSignedFields),
null, null,
publicKey, decodedSignature);
if (!signatureAlgorithm.verify(verificationRequest)) {
throw new AuthTokenSignatureValidationException();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package eu.webeid.security.validator;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import eu.webeid.security.authtoken.WebEidAuthToken;
import eu.webeid.security.certificate.CertificateLoader;
import eu.webeid.security.certificate.CertificateValidator;
Expand Down Expand Up @@ -57,7 +58,7 @@ final class AuthTokenValidatorImpl implements AuthTokenValidator {
private static final int TOKEN_MAX_LENGTH = 10000;
private static final Logger LOG = LoggerFactory.getLogger(AuthTokenValidatorImpl.class);

private static final ObjectMapper objectMapper = new ObjectMapper();
private static final ObjectReader OBJECT_READER = new ObjectMapper().readerFor(WebEidAuthToken.class);

private final AuthTokenValidationConfiguration configuration;
private final SubjectCertificateValidatorBatch simpleSubjectCertificateValidators;
Expand Down Expand Up @@ -138,7 +139,7 @@ private void validateTokenLength(String authToken) throws AuthTokenParseExceptio

private WebEidAuthToken parseToken(String authToken) throws AuthTokenParseException {
try {
final WebEidAuthToken token = objectMapper.readValue(authToken, WebEidAuthToken.class);
final WebEidAuthToken token = OBJECT_READER.readValue(authToken);
if (token == null) {
throw new AuthTokenParseException("Web eID authentication token is null");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package eu.webeid.security.validator;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import eu.webeid.security.certificate.CertificateLoader;
import org.junit.jupiter.api.Test;
import eu.webeid.security.authtoken.WebEidAuthToken;
Expand All @@ -36,9 +37,9 @@

class AuthTokenSignatureValidatorTest {

private static final ObjectMapper objectMapper = new ObjectMapper();
private static final ObjectReader OBJECT_READER = new ObjectMapper().readerFor(WebEidAuthToken.class);

static String VALID_RS256_AUTH_TOKEN = "{\"algorithm\":\"RS256\"," +
private static final String VALID_RS256_AUTH_TOKEN = "{\"algorithm\":\"RS256\"," +
"\"unverifiedCertificate\":\"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\"," +
"\"issuerApp\":\"https://web-eid.eu/web-eid-app/releases/2.0.0+0\"," +
"\"signature\":\"xsjXsQvVYXWcdV0YPhxLthJxtf0//R8p9WFFlYJGRARrl1ruyoAUwl0xeHgeZOKeJtwiCYCNWJzCG3VM3ydgt92bKhhk1u0JXIPVqvOkmDY72OCN4q73Y8iGSPVTgjk93TgquHlodf7YcqZNhutwNNf3oldHEWJD5zmkdwdpBFXgeOwTAdFwGljDQZbHr3h1Dr+apUDuloS0WuIzUuu8YXN2b8lh8FCTlF0G0DEjhHd/MGx8dbe3UTLHmD7K9DXv4zLJs6EF9i2v/C10SIBQDkPBSVPqMxCDPECjbEPi2+ds94eU7ThOhOQlFFtJ4KjQNTUa2crSixH7cYZF2rNNmA==\"," +
Expand All @@ -49,7 +50,7 @@ void whenValidES384Signature_thenSucceeds() throws Exception {
final AuthTokenSignatureValidator signatureValidator =
new AuthTokenSignatureValidator(URI.create("https://ria.ee"));

final WebEidAuthToken authToken = objectMapper.readValue(VALID_AUTH_TOKEN, WebEidAuthToken.class);
final WebEidAuthToken authToken = OBJECT_READER.readValue(VALID_AUTH_TOKEN);
final X509Certificate x509Certificate = CertificateLoader.decodeCertificateFromBase64(authToken.getUnverifiedCertificate());

assertThatCode(() -> signatureValidator
Expand All @@ -62,7 +63,7 @@ void whenValidRS256Signature_thenSucceeds() throws Exception {
final AuthTokenSignatureValidator signatureValidator =
new AuthTokenSignatureValidator(URI.create("https://ria.ee"));

final WebEidAuthToken authToken = objectMapper.readValue(VALID_RS256_AUTH_TOKEN, WebEidAuthToken.class);
final WebEidAuthToken authToken = OBJECT_READER.readValue(VALID_RS256_AUTH_TOKEN);
final X509Certificate x509Certificate = CertificateLoader.decodeCertificateFromBase64(authToken.getUnverifiedCertificate());

assertThatCode(() -> signatureValidator
Expand Down
Binary file modified src/test/resources/ocsp_response_with_2_responses.der
Binary file not shown.