From bf3aba7f02a85f93ed28144ab38a0bd894d73289 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 2 Mar 2026 14:01:24 +0200 Subject: [PATCH 01/36] Restore legacy signing algorithms SHA256_WITH_RSA_ENCRYPTION, SHA384_WITH_RSA_ENCRYPTION and SHA512_WITH_RSA_ENCRYPTION --- ...nkAuthenticationSessionRequestBuilder.java | 3 + ...iceLinkSignatureSessionRequestBuilder.java | 5 +- ...icationSignatureSessionRequestBuilder.java | 5 +- ...onAuthenticationSessionRequestBuilder.java | 3 + ...icationSignatureSessionRequestBuilder.java | 5 +- .../ee/sk/smartid/SignatureAlgorithm.java | 82 ++++++++++++++++++- .../smartid/SignatureResponseValidator.java | 23 ++++-- .../sk/smartid/SignatureValueValidator.java | 19 ++++- .../smartid/SignatureValueValidatorImpl.java | 37 ++++++++- .../RawDigestSignatureProtocolParameters.java | 8 +- ...thenticationSessionRequestBuilderTest.java | 11 ++- ...inkSignatureSessionRequestBuilderTest.java | 16 ++++ ...thenticationSessionRequestBuilderTest.java | 13 ++- .../SignatureResponseValidatorTest.java | 14 ++++ .../SignatureValueValidatorImplTest.java | 25 ++++++ 15 files changed, 245 insertions(+), 24 deletions(-) diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java index a7451f7e..def0e949 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java @@ -303,6 +303,9 @@ private void validateSignatureParameters() { if (signatureAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be set"); } + if (!signatureAlgorithm.isUsedForAuthentication()) { + throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be an algorithm supported for authentication (e.g. RSASSA_PSS). Legacy RSA algorithms are only supported for signing"); + } if (hashAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'hashAlgorithm' must be set"); } diff --git a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java index 56687739..31a5063d 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java @@ -287,9 +287,12 @@ private DeviceLinkSessionResponse initSignatureSession(DeviceLinkSignatureSessio } private DeviceLinkSignatureSessionRequest createSignatureSessionRequest() { + SignatureAlgorithmParameters algorithmParams = signatureAlgorithm.isLegacyRsa() + ? null + : new SignatureAlgorithmParameters(digestInput.hashAlgorithm().getAlgorithmName()); var signatureProtocolParameters = new RawDigestSignatureProtocolParameters(digestInput.getDigestInBase64(), signatureAlgorithm.getAlgorithmName(), - new SignatureAlgorithmParameters(digestInput.hashAlgorithm().getAlgorithmName())); + algorithmParams); return new DeviceLinkSignatureSessionRequest(relyingPartyUUID, relyingPartyName, certificateLevel != null ? certificateLevel.name() : null, diff --git a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java index 67785957..34887904 100644 --- a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java @@ -257,9 +257,12 @@ private void validateRequestParameters() { } private LinkedSignatureSessionRequest createSessionRequest() { + SignatureAlgorithmParameters algorithmParams = signatureAlgorithm.isLegacyRsa() + ? null + : new SignatureAlgorithmParameters(digestInput.hashAlgorithm().getAlgorithmName()); var rawDigestParams = new RawDigestSignatureProtocolParameters(digestInput.getDigestInBase64(), signatureAlgorithm.getAlgorithmName(), - new SignatureAlgorithmParameters(digestInput.hashAlgorithm().getAlgorithmName())); + algorithmParams); return new LinkedSignatureSessionRequest(relyingPartyUUID, relyingPartyName, certificateLevel != null ? certificateLevel.name() : null, diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java index b694ffb5..6d14a37c 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java @@ -275,6 +275,9 @@ private void validateSignatureParameters() { if (signatureAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be set"); } + if (!signatureAlgorithm.isUsedForAuthentication()) { + throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be an algorithm supported for authentication (e.g. RSASSA_PSS). Legacy RSA algorithms are only supported for signing"); + } if (hashAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'hashAlgorithm' must be set"); } diff --git a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java index ead4a53b..8a518037 100644 --- a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java @@ -265,9 +265,12 @@ private NotificationSignatureSessionResponse initSignatureSession(NotificationSi } private NotificationSignatureSessionRequest createSignatureSessionRequest() { + SignatureAlgorithmParameters algorithmParams = signatureAlgorithm.isLegacyRsa() + ? null + : new SignatureAlgorithmParameters(digestInput.hashAlgorithm().getAlgorithmName()); var signatureProtocolParameters = new RawDigestSignatureProtocolParameters(digestInput.getDigestInBase64(), signatureAlgorithm.getAlgorithmName(), - new SignatureAlgorithmParameters(digestInput.hashAlgorithm().getAlgorithmName())); + algorithmParams); return new NotificationSignatureSessionRequest(relyingPartyUUID, relyingPartyName, diff --git a/src/main/java/ee/sk/smartid/SignatureAlgorithm.java b/src/main/java/ee/sk/smartid/SignatureAlgorithm.java index f6a33872..7f6ae9bc 100644 --- a/src/main/java/ee/sk/smartid/SignatureAlgorithm.java +++ b/src/main/java/ee/sk/smartid/SignatureAlgorithm.java @@ -30,19 +30,53 @@ /** * Signature algorithms supported by Smart-ID API. + *

+ * Algorithms are divided by use case: + *

+ * RSASSA-PKCS#1 v1.5 algorithms do not use {@code signatureAlgorithmParameters} in the API request or response. */ public enum SignatureAlgorithm { /** * RSASSA-PSS (RSA Probabilistic Signature Scheme) as defined in PKCS #1 v2.1. - * This algorithm provides probabilistic signature generation for enhanced security. + * Supported for both authentication and signing. */ - RSASSA_PSS("rsassa-pss"); + RSASSA_PSS("rsassa-pss", false, true), + + /** + * RSASSA-PKCS#1 v1.5 with SHA-256. Signing only; no signatureAlgorithmParameters. + */ + SHA256_WITH_RSA_ENCRYPTION("sha256WithRSAEncryption", true, false, "SHA256withRSA"), + + /** + * RSASSA-PKCS#1 v1.5 with SHA-384. Signing only; no signatureAlgorithmParameters. + */ + SHA384_WITH_RSA_ENCRYPTION("sha384WithRSAEncryption", true, false, "SHA384withRSA"), + + /** + * RSASSA-PKCS#1 v1.5 with SHA-512. Signing only; no signatureAlgorithmParameters. + */ + SHA512_WITH_RSA_ENCRYPTION("sha512WithRSAEncryption", true, false, "SHA512withRSA"); private final String algorithmName; + private final boolean legacyRsa; + private final boolean usedForAuthentication; + private final String jceAlgorithmName; - SignatureAlgorithm(String algorithmName) { + SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication) { + this(algorithmName, legacyRsa, usedForAuthentication, algorithmName); + } + + SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication, String jceAlgorithmName) { this.algorithmName = algorithmName; + this.legacyRsa = legacyRsa; + this.usedForAuthentication = usedForAuthentication; + this.jceAlgorithmName = jceAlgorithmName; } /** @@ -54,6 +88,36 @@ public String getAlgorithmName() { return algorithmName; } + /** + * Returns whether this algorithm is RSASSA-PKCS#1 v1.5 (legacy RSA). + * Such algorithms do not use or require {@code signatureAlgorithmParameters} in requests or responses. + * + * @return true for SHA256/SHA384/SHA512 with RSA encryption + */ + public boolean isLegacyRsa() { + return legacyRsa; + } + + /** + * Returns whether this algorithm is supported for authentication sessions. + * Only RSASSA-PSS is supported for authentication. + * + * @return true if the algorithm may be used for authentication + */ + public boolean isUsedForAuthentication() { + return usedForAuthentication; + } + + /** + * Returns the JCE standard algorithm name for {@link java.security.Signature#getInstance(String)}. + * For legacy RSA algorithms this is the name used to verify the signature (e.g. SHA256withRSA). + * + * @return the JCE algorithm name + */ + public String getJceAlgorithmName() { + return jceAlgorithmName; + } + /** * Checks if the provided signature algorithm is supported. * @@ -65,6 +129,18 @@ public static boolean isSupported(String signatureAlgorithm) { .anyMatch(s -> s.getAlgorithmName().equals(signatureAlgorithm)); } + /** + * Returns whether the given algorithm name is a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm. + * + * @param signatureAlgorithm the signature algorithm name + * @return true if legacy RSA, false otherwise + */ + public static boolean isLegacyRsa(String signatureAlgorithm) { + return Arrays.stream(SignatureAlgorithm.values()) + .filter(s -> s.getAlgorithmName().equals(signatureAlgorithm)) + .anyMatch(SignatureAlgorithm::isLegacyRsa); + } + /** * Converts a string representation of a signature algorithm to its corresponding enum value. * diff --git a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java index 63b85de4..4557855d 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java @@ -110,15 +110,18 @@ public SignatureResponse validate(SessionStatus sessionStatus, signatureResponse.setEndResult(sessionResult.getEndResult()); signatureResponse.setSignatureValueInBase64(sessionSignature.getValue()); signatureResponse.setAlgorithmName(sessionSignature.getSignatureAlgorithm()); + signatureResponse.setSignatureAlgorithm(SignatureAlgorithm.fromString(sessionSignature.getSignatureAlgorithm())); - SessionSignatureAlgorithmParameters signatureAlgorithmParameters = sessionSignature.getSignatureAlgorithmParameters(); - var rsaSsaPssParams = new RsaSsaPssParameters(); - rsaSsaPssParams.setDigestHashAlgorithm(HashAlgorithm.fromString(signatureAlgorithmParameters.getHashAlgorithm()).orElse(null)); - rsaSsaPssParams.setMaskGenAlgorithm(MaskGenAlgorithm.ID_MGF1); - rsaSsaPssParams.setMaskHashAlgorithm(HashAlgorithm.fromString(signatureAlgorithmParameters.getMaskGenAlgorithm().getParameters().getHashAlgorithm()).orElse(null)); - rsaSsaPssParams.setSaltLength(signatureAlgorithmParameters.getSaltLength()); - rsaSsaPssParams.setTrailerField(TrailerField.BC); - signatureResponse.setRsaSsaPssParameters(rsaSsaPssParams); + if (!SignatureAlgorithm.isLegacyRsa(sessionSignature.getSignatureAlgorithm())) { + SessionSignatureAlgorithmParameters signatureAlgorithmParameters = sessionSignature.getSignatureAlgorithmParameters(); + var rsaSsaPssParams = new RsaSsaPssParameters(); + rsaSsaPssParams.setDigestHashAlgorithm(HashAlgorithm.fromString(signatureAlgorithmParameters.getHashAlgorithm()).orElse(null)); + rsaSsaPssParams.setMaskGenAlgorithm(MaskGenAlgorithm.ID_MGF1); + rsaSsaPssParams.setMaskHashAlgorithm(HashAlgorithm.fromString(signatureAlgorithmParameters.getMaskGenAlgorithm().getParameters().getHashAlgorithm()).orElse(null)); + rsaSsaPssParams.setSaltLength(signatureAlgorithmParameters.getSaltLength()); + rsaSsaPssParams.setTrailerField(TrailerField.BC); + signatureResponse.setRsaSsaPssParameters(rsaSsaPssParams); + } signatureResponse.setFlowType(FlowType.fromString(sessionSignature.getFlowType())); signatureResponse.setCertificate(CertificateParser.parseX509Certificate(certificate.getValue())); @@ -234,7 +237,9 @@ private static void validateRawDigestSignature(SessionStatus sessionStatus) { validateSignatureValue(signature.getValue()); validateSignatureAlgorithmName(signature.getSignatureAlgorithm()); validateFlowType(signature.getFlowType()); - validateSignatureAlgorithmParameters(signature.getSignatureAlgorithmParameters()); + if (!SignatureAlgorithm.isLegacyRsa(signature.getSignatureAlgorithm())) { + validateSignatureAlgorithmParameters(signature.getSignatureAlgorithmParameters()); + } } private static void validateSignatureValue(String value) { diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidator.java b/src/main/java/ee/sk/smartid/SignatureValueValidator.java index 5df475af..c83006a8 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidator.java @@ -36,16 +36,31 @@ public interface SignatureValueValidator { /** - * Validates the signature value against the calculated signature value. + * Validates the signature value (RSASSA-PSS) against the calculated signature value. * * @param signatureValue the signature value to validate * @param payload the original data that was signed * @param certificate X509 certificate used for signature validation - * @param rsaSsaPssParameters signature parameters used for creating signature value + * @param rsaSsaPssParameters signature parameters used for creating signature value * @throws UnprocessableSmartIdResponseException when there are any issue with validating the signature value */ void validate(byte[] signatureValue, byte[] payload, X509Certificate certificate, RsaSsaPssParameters rsaSsaPssParameters); + + /** + * Validates the signature value (RSASSA-PKCS#1 v1.5) against the digest/payload. + * Use this when the signature session used a legacy RSA algorithm (e.g. sha256WithRSAEncryption). + * + * @param signatureValue the signature value to validate + * @param payload the digest or data that was signed (typically the hash that was sent to Smart-ID) + * @param certificate X509 certificate used for signature validation + * @param signatureAlgorithmName Smart-ID API algorithm name (e.g. sha512WithRSAEncryption) + * @throws UnprocessableSmartIdResponseException when there are any issue with validating the signature value + */ + void validate(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + String signatureAlgorithmName); } diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java index 678f5f9d..a007d148 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java @@ -41,8 +41,8 @@ import ee.sk.smartid.exception.permanent.SmartIdClientException; /** - * Implementation of {@link SignatureValueValidator} that uses RSASSA-PSS signature algorithm - * to validate the signature value in the authentication and signature session status response. + * Implementation of {@link SignatureValueValidator} that validates signature values + * for both RSASSA-PSS (authentication and signing) and RSASSA-PKCS#1 v1.5 (signing only). */ public final class SignatureValueValidatorImpl implements SignatureValueValidator { @@ -66,6 +66,39 @@ public void validate(byte[] signatureValue, } } + @Override + public void validate(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + String signatureAlgorithmName) { + if (signatureValue == null) { + throw new SmartIdClientException("Parameter 'signatureValue' is not provided"); + } + if (payload == null) { + throw new SmartIdClientException("Parameter 'payload' is not provided"); + } + if (certificate == null) { + throw new SmartIdClientException("Parameter 'certificate' is not provided"); + } + if (signatureAlgorithmName == null || signatureAlgorithmName.isBlank()) { + throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); + } + if (!SignatureAlgorithm.isLegacyRsa(signatureAlgorithmName)) { + throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signatureAlgorithmName + "' is not a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm; use validate(..., RsaSsaPssParameters) for RSASSA-PSS"); + } + try { + SignatureAlgorithm algorithm = SignatureAlgorithm.fromString(signatureAlgorithmName); + Signature signature = Signature.getInstance(algorithm.getJceAlgorithmName()); + signature.initVerify(certificate.getPublicKey()); + signature.update(payload); + if (!signature.verify(signatureValue)) { + throw new UnprocessableSmartIdResponseException("Provided signature value does not match the calculated signature value"); + } + } catch (GeneralSecurityException ex) { + throw new UnprocessableSmartIdResponseException("Signature value validation failed", ex); + } + } + private Signature getSignature(RsaSsaPssParameters rsaSsaPssParameters) { try { var params = new PSSParameterSpec(rsaSsaPssParameters.getDigestHashAlgorithm().getAlgorithmName(), diff --git a/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java b/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java index 16e9fe9b..c780ebb2 100644 --- a/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java +++ b/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java @@ -28,14 +28,16 @@ import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonInclude; + /** * Parameters for protocol RAW_DIGEST_SIGNATURE * * @param digest Required. The digest to be signed, Base64 encoded. - * @param signatureAlgorithm Required. The signature algorithm. Supported value is RSASSA-PSS. - * @param signatureAlgorithmParameters Required. The parameters for signature algorithm. + * @param signatureAlgorithm Required. The signature algorithm (e.g. rsassa-pss, sha512WithRSAEncryption). + * @param signatureAlgorithmParameters Required for RSASSA-PSS. Omitted for RSASSA-PKCS#1 v1.5 algorithms; must be null in that case so it is not serialized. */ public record RawDigestSignatureProtocolParameters(String digest, String signatureAlgorithm, - SignatureAlgorithmParameters signatureAlgorithmParameters) implements Serializable { + @JsonInclude(JsonInclude.Include.NON_NULL) SignatureAlgorithmParameters signatureAlgorithmParameters) implements Serializable { } \ No newline at end of file diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java index e5e007a2..31d23539 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java @@ -145,7 +145,7 @@ void initAuthenticationSession_certificateLevel_ok(AuthenticationCertificateLeve } @ParameterizedTest - @EnumSource + @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS"}) void initAuthenticationSession_signatureAlgorithm_ok(SignatureAlgorithm signatureAlgorithm) { when(connector.initAnonymousDeviceLinkAuthentication(any(DeviceLinkAuthenticationSessionRequest.class))) .thenReturn(toDeviceLinkAuthenticationResponse()); @@ -280,6 +280,15 @@ void initAuthenticationSession_signatureAlgorithmIsSetToNull_throwException() { assertEquals("Value for 'signatureAlgorithm' must be set", exception.getMessage()); } + @ParameterizedTest + @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void initAuthenticationSession_legacyRsaSignatureAlgorithm_throwException(SignatureAlgorithm signatureAlgorithm) { + DeviceLinkAuthenticationSessionRequestBuilder builder = toDeviceLinkRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); + + var exception = assertThrows(SmartIdRequestSetupException.class, builder::initAuthenticationSession); + assertTrue(exception.getMessage().contains("supported for authentication")); + } + @ParameterizedTest @NullAndEmptySource void initAuthenticationSession_interactionsIsEmpty_throwException(List interactions) { diff --git a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java index 4d1cb1e2..d1f73f87 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java @@ -28,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -175,6 +176,21 @@ void initSignatureSession_withSignatureAlgorithm_setsCorrectAlgorithm() { assertEquals(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); } + @ParameterizedTest + @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void initSignatureSession_withLegacyRsaAlgorithm_omitsSignatureAlgorithmParameters(SignatureAlgorithm signatureAlgorithm) { + when(connector.initDeviceLinkSignature(any(DeviceLinkSignatureSessionRequest.class), any(SemanticsIdentifier.class))).thenReturn(mockSignatureSessionResponse()); + var deviceLinkSessionRequestBuilder = toDeviceLinkSignatureSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); + + deviceLinkSessionRequestBuilder.initSignatureSession(); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(DeviceLinkSignatureSessionRequest.class); + verify(connector).initDeviceLinkSignature(requestCaptor.capture(), any(SemanticsIdentifier.class)); + DeviceLinkSignatureSessionRequest capturedRequest = requestCaptor.getValue(); + assertEquals(signatureAlgorithm.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + assertNull(capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters()); + } + @ParameterizedTest @EnumSource(HashAlgorithm.class) void initSignatureSession_withSignableHash(HashAlgorithm hashAlgorithm) { diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java index dab26384..87ccd153 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java @@ -29,6 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -58,6 +59,7 @@ import ee.sk.smartid.common.notification.interactions.NotificationInteraction; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; import ee.sk.smartid.exception.permanent.SmartIdClientException; +import ee.sk.smartid.exception.permanent.SmartIdRequestSetupException; import ee.sk.smartid.rest.SmartIdConnector; import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionRequest; import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionResponse; @@ -133,7 +135,7 @@ void initAuthenticationSession_certificateLevel_ok(AuthenticationCertificateLeve } @ParameterizedTest - @EnumSource + @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS"}) void initAuthenticationSession_signatureAlgorithm_ok(SignatureAlgorithm signatureAlgorithm) { when(connector.initNotificationAuthentication(any(NotificationAuthenticationSessionRequest.class), any(String.class))).thenReturn(toNotificationAuthenticationResponse()); NotificationAuthenticationSessionRequestBuilder builder = toNotificationAuthenticationSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); @@ -147,6 +149,15 @@ void initAuthenticationSession_signatureAlgorithm_ok(SignatureAlgorithm signatur assertEquals(signatureAlgorithm.getAlgorithmName(), request.signatureProtocolParameters().signatureAlgorithm()); } + @ParameterizedTest + @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void initAuthenticationSession_legacyRsaSignatureAlgorithm_throwException(SignatureAlgorithm signatureAlgorithm) { + NotificationAuthenticationSessionRequestBuilder builder = toNotificationAuthenticationSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); + + var exception = assertThrows(SmartIdRequestSetupException.class, builder::initAuthenticationSession); + assertTrue(exception.getMessage().contains("supported for authentication")); + } + @ParameterizedTest @EnumSource void initAuthenticationSession_hashAlgorithm_ok(HashAlgorithm expectedHashAlgorithm) { diff --git a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java index 5f7d52d2..42a8d5a7 100644 --- a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java @@ -109,6 +109,20 @@ void validate_nqSigning_ok() { assertEquals("OK", response.getEndResult()); } + @Test + void validate_legacyRsaSignature_ok_withoutSignatureAlgorithmParameters() { + SessionStatus sessionStatus = toQualifiedSignatureSessionStatus("RAW_DIGEST_SIGNATURE", SignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION.getAlgorithmName()); + sessionStatus.setSignatureProtocol("RAW_DIGEST_SIGNATURE"); + sessionStatus.getSignature().setSignatureAlgorithmParameters(null); + + SignatureResponse response = signatureResponseValidator.validate(sessionStatus, CertificateLevel.QUALIFIED); + + assertEquals("OK", response.getEndResult()); + assertEquals(SignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION.getAlgorithmName(), response.getAlgorithmName()); + assertEquals(SignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION, response.getSignatureAlgorithm()); + assertTrue(response.getRsaSsaPssParameters() == null); + } + @Test void validate_stateParameterMissing() { SessionStatus sessionStatus = toQualifiedSignatureSessionStatus("RAW_DIGEST_SIGNATURE", "rsassa-pss"); diff --git a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java index 4ab8d7ac..9f62c32b 100644 --- a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java @@ -29,6 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; @@ -43,6 +44,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.EnumSource; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; import ee.sk.smartid.exception.permanent.SmartIdClientException; @@ -97,6 +99,29 @@ void validateSignatureValue_constructedPayloadDoesNotMatchTheSignature_throwExce assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); } + @ParameterizedTest + @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_legacyRsa_invalidSignature_throwException(SignatureAlgorithm algorithm) throws CertificateException { + var ex = assertThrows(UnprocessableSmartIdResponseException.class, + () -> signatureValueValidator.validate( + "invalidSignature".getBytes(StandardCharsets.UTF_8), + PAYLOAD, + CertificateUtil.toX509CertificateFromEncodedString(CERT), + algorithm.getAlgorithmName())); + assertTrue(ex.getMessage().contains("Signature value validation failed") || ex.getMessage().contains("does not match")); + } + + @Test + void validate_legacyRsa_nonLegacyAlgorithmName_throwException() throws CertificateException { + var ex = assertThrows(UnprocessableSmartIdResponseException.class, + () -> signatureValueValidator.validate( + SIGNATURE_VALUE, + PAYLOAD, + CertificateUtil.toX509CertificateFromEncodedString(CERT), + SignatureAlgorithm.RSASSA_PSS.getAlgorithmName())); + assertTrue(ex.getMessage().contains("not a legacy RSA")); + } + private static RsaSsaPssParameters toRsaSsaPssParameters() { RsaSsaPssParameters rsaSsaPssParameters = new RsaSsaPssParameters(); rsaSsaPssParameters.setDigestHashAlgorithm(HashAlgorithm.SHA_512); From c754b9740677c2c2fd18e94047fdb14bf449a277 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 2 Mar 2026 14:06:05 +0200 Subject: [PATCH 02/36] Use the document number actually returned by the demo service --- .../java/ee/sk/smartid/integration/ReadmeIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index b3619a6f..82807e75 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -759,7 +759,7 @@ void signature_withSemanticsIdentifier() { SignatureResponse signatureResponse = validator.validate(signatureSessionStatus, certificateLevel); assertEquals("OK", signatureResponse.getEndResult()); - assertEquals("PNOEE-40504040001-DEMO-Q", signatureResponse.getDocumentNumber()); + assertEquals("PNOEE-40504040001-DEM0-Q", signatureResponse.getDocumentNumber()); assertEquals(CertificateLevel.QUALIFIED, signatureResponse.getCertificateLevel()); assertEquals(CertificateLevel.QSCD, signatureResponse.getRequestedCertificateLevel()); assertEquals("confirmationMessage", signatureResponse.getInteractionFlowUsed()); From 884341274a1e5510c1dff1161172175615b9b130 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 2 Mar 2026 14:14:16 +0200 Subject: [PATCH 03/36] Change SignatureResponseValidatorTest.validate_legacyRsaSignature_ok_withoutSignatureAlgorithmParameters to ParameterizedTest --- .../sk/smartid/SignatureResponseValidatorTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java index 42a8d5a7..03530da5 100644 --- a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java @@ -27,6 +27,7 @@ */ import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -109,18 +110,19 @@ void validate_nqSigning_ok() { assertEquals("OK", response.getEndResult()); } - @Test - void validate_legacyRsaSignature_ok_withoutSignatureAlgorithmParameters() { - SessionStatus sessionStatus = toQualifiedSignatureSessionStatus("RAW_DIGEST_SIGNATURE", SignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION.getAlgorithmName()); + @ParameterizedTest + @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_legacyRsaSignature_ok_withoutSignatureAlgorithmParameters(SignatureAlgorithm signatureAlgorithm) { + SessionStatus sessionStatus = toQualifiedSignatureSessionStatus("RAW_DIGEST_SIGNATURE", signatureAlgorithm.getAlgorithmName()); sessionStatus.setSignatureProtocol("RAW_DIGEST_SIGNATURE"); sessionStatus.getSignature().setSignatureAlgorithmParameters(null); SignatureResponse response = signatureResponseValidator.validate(sessionStatus, CertificateLevel.QUALIFIED); assertEquals("OK", response.getEndResult()); - assertEquals(SignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION.getAlgorithmName(), response.getAlgorithmName()); - assertEquals(SignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION, response.getSignatureAlgorithm()); - assertTrue(response.getRsaSsaPssParameters() == null); + assertEquals(signatureAlgorithm.getAlgorithmName(), response.getAlgorithmName()); + assertEquals(signatureAlgorithm, response.getSignatureAlgorithm()); + assertNull(response.getRsaSsaPssParameters()); } @Test From 3cd083817c39907e8914dbebba1f171cfbf3acc9 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 2 Mar 2026 15:32:44 +0200 Subject: [PATCH 04/36] Fix SmartIdRestIntegrationTest by using data that Smart-ID demo service accepts --- .../integration/SmartIdRestIntegrationTest.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java index 69ba46f4..2f30d371 100644 --- a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java @@ -35,7 +35,6 @@ import org.bouncycastle.util.encoders.Base64; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -85,7 +84,6 @@ void setUp() { smartIdConnector = new SmartIdRestConnector("https://sid.demo.sk.ee/smart-id-rp/v3/"); } - @Disabled("Testing device-link flows with demo accounts is not yet possible") @Nested class DeviceLink { @@ -108,7 +106,7 @@ void initAnonymousDeviceLinkAuthentication() { void initDeviceLinkAuthentication_withDocumentNumber() { DeviceLinkAuthenticationSessionRequest request = toDeviceLinkAuthenticationSessionRequest(); - DeviceLinkSessionResponse sessionResponse = smartIdConnector.initDeviceLinkAuthentication(request, "PNOEE-40504040001-MOCK-Q"); + DeviceLinkSessionResponse sessionResponse = smartIdConnector.initDeviceLinkAuthentication(request, "PNOEE-40404040009-MOCK-Q"); assertTrue(UUID_PATTERN.matcher(sessionResponse.sessionID()).matches()); assertTrue(SESSION_TOKEN_PATTERN.matcher(sessionResponse.sessionToken()).matches()); @@ -217,7 +215,7 @@ void initDeviceLinkSignature_withDocumentNumber() { null ); - DeviceLinkSessionResponse sessionResponse = smartIdConnector.initDeviceLinkSignature(request, "PNOEE-40504040001-MOCK-Q"); + DeviceLinkSessionResponse sessionResponse = smartIdConnector.initDeviceLinkSignature(request, "PNOEE-40404040009-MOCK-Q"); assertTrue(UUID_PATTERN.matcher(sessionResponse.sessionID()).matches()); assertTrue(SESSION_TOKEN_PATTERN.matcher(sessionResponse.sessionToken()).matches()); @@ -231,7 +229,7 @@ void initDeviceLinkSignature_withDocumentNumber() { class NotificationBasedRequests { private static final SemanticsIdentifier SEMANTICS_IDENTIFIER = new SemanticsIdentifier("PNOEE-40504040001"); - private static final String DOCUMENT_NUMBER = "PNOEE-40504040001-DEMO-Q"; + private static final String DOCUMENT_NUMBER = "PNOEE-50001029996-DEMO-Q"; @Nested class Authentication { @@ -258,7 +256,7 @@ private static NotificationAuthenticationSessionRequest toAuthenticationRequest( var signatureParameters = new AcspV2SignatureProtocolParameters( RpChallengeGenerator.generate().toBase64EncodedValue(), SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), - new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())); + new SignatureAlgorithmParameters(HashAlgorithm.SHA_512.getAlgorithmName())); return new NotificationAuthenticationSessionRequest(RELYING_PARTY_UUID, RELYING_PARTY_NAME, From ba974c69ac1b5c4422473040c2c39d5ad67a5a21 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 3 Mar 2026 13:53:51 +0200 Subject: [PATCH 05/36] Fix ReadmeIntegrationTest.NotificationBasedExamples.signature_withDocumentNumber by using document number that Smart-ID demo service accepts --- .../java/ee/sk/smartid/integration/ReadmeIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 82807e75..606fae30 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -768,7 +768,7 @@ void signature_withSemanticsIdentifier() { @Test void signature_withDocumentNumber() { - String documentNumber = "PNOEE-40504040001-DEMO-Q"; + String documentNumber = "PNOEE-50001029996-DEMO-Q"; CertificateLevel certificateLevel = CertificateLevel.QSCD; // Query the certificate by document number to be used for creating the DataToSign From 567b49ae9c7ee6a20b3ab4d43efcf53e89210c77 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 3 Mar 2026 14:23:36 +0200 Subject: [PATCH 06/36] Add into NotificationBasedExamples integration tests for signing with SHA512_WITH_RSA_ENCRYPTION --- .../integration/ReadmeIntegrationTest.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 606fae30..e1648bfd 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -50,6 +50,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import ee.sk.smartid.AuthenticationCertificateLevel; import ee.sk.smartid.AuthenticationIdentity; @@ -72,6 +74,7 @@ import ee.sk.smartid.RpChallengeGenerator; import ee.sk.smartid.SessionType; import ee.sk.smartid.SignableData; +import ee.sk.smartid.SignatureAlgorithm; import ee.sk.smartid.SignatureCertificatePurposeValidator; import ee.sk.smartid.SignatureCertificatePurposeValidatorFactory; import ee.sk.smartid.SignatureCertificatePurposeValidatorFactoryImpl; @@ -693,8 +696,10 @@ void certificateChoice_withSemanticIdentifier() { assertEquals(CertificateLevel.QUALIFIED, response.getCertificateLevel()); } - @Test - void signature_withSemanticsIdentifier() { + @ParameterizedTest + // For signature algorithms SHA256_WITH_RSA_ENCRYPTION and SHA384_WITH_RSA_ENCRYPTION demo service returns HTTP 400 Bad Request + @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS", "SHA512_WITH_RSA_ENCRYPTION"}) + void signature_withSemanticsIdentifier(SignatureAlgorithm signatureAlgorithm) { var semanticIdentifier = new SemanticsIdentifier( // 3 character identity type // (PAS-passport, IDC-national identity card or PNO - (national) personal number) @@ -736,6 +741,7 @@ void signature_withSemanticsIdentifier() { NotificationSignatureSessionResponse signatureSessionResponse = smartIdClient.createNotificationSignature() .withCertificateLevel(certificateLevel) + .withSignatureAlgorithm(signatureAlgorithm) .withSignableData(signableData) .withSemanticsIdentifier(semanticsIdentifier) .withInteractions(List.of( @@ -766,8 +772,10 @@ void signature_withSemanticsIdentifier() { assertNotNull(signatureResponse.getCertificate()); } - @Test - void signature_withDocumentNumber() { + @ParameterizedTest + // For signature algorithms SHA256_WITH_RSA_ENCRYPTION and SHA384_WITH_RSA_ENCRYPTION demo service returns HTTP 400 Bad Request + @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS", "SHA512_WITH_RSA_ENCRYPTION"}) + void signature_withDocumentNumber(SignatureAlgorithm signatureAlgorithm) { String documentNumber = "PNOEE-50001029996-DEMO-Q"; CertificateLevel certificateLevel = CertificateLevel.QSCD; @@ -796,6 +804,7 @@ void signature_withDocumentNumber() { NotificationSignatureSessionResponse signatureSessionResponse = smartIdClient.createNotificationSignature() .withCertificateLevel(certificateLevel) + .withSignatureAlgorithm(signatureAlgorithm) .withSignableData(signableData) .withDocumentNumber(documentNumber) .withInteractions(List.of( From 1c37f46b167e562641e21e870857a854fe9c6a07 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 3 Mar 2026 22:19:19 +0200 Subject: [PATCH 07/36] Add SignatureAlgorithm.hashAlgorithmForLegacy for convenient usage in SignableData as calculateHash() needs correct HashAlgorithm --- .../ee/sk/smartid/SignatureAlgorithm.java | 24 +++++++++++++++---- .../integration/ReadmeIntegrationTest.java | 16 ++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/main/java/ee/sk/smartid/SignatureAlgorithm.java b/src/main/java/ee/sk/smartid/SignatureAlgorithm.java index 7f6ae9bc..c30cf38d 100644 --- a/src/main/java/ee/sk/smartid/SignatureAlgorithm.java +++ b/src/main/java/ee/sk/smartid/SignatureAlgorithm.java @@ -51,32 +51,38 @@ public enum SignatureAlgorithm { /** * RSASSA-PKCS#1 v1.5 with SHA-256. Signing only; no signatureAlgorithmParameters. */ - SHA256_WITH_RSA_ENCRYPTION("sha256WithRSAEncryption", true, false, "SHA256withRSA"), + SHA256_WITH_RSA_ENCRYPTION("sha256WithRSAEncryption", true, false, "SHA256withRSA", HashAlgorithm.SHA_256), /** * RSASSA-PKCS#1 v1.5 with SHA-384. Signing only; no signatureAlgorithmParameters. */ - SHA384_WITH_RSA_ENCRYPTION("sha384WithRSAEncryption", true, false, "SHA384withRSA"), + SHA384_WITH_RSA_ENCRYPTION("sha384WithRSAEncryption", true, false, "SHA384withRSA", HashAlgorithm.SHA_384), /** * RSASSA-PKCS#1 v1.5 with SHA-512. Signing only; no signatureAlgorithmParameters. */ - SHA512_WITH_RSA_ENCRYPTION("sha512WithRSAEncryption", true, false, "SHA512withRSA"); + SHA512_WITH_RSA_ENCRYPTION("sha512WithRSAEncryption", true, false, "SHA512withRSA", HashAlgorithm.SHA_512); private final String algorithmName; private final boolean legacyRsa; private final boolean usedForAuthentication; private final String jceAlgorithmName; + private final HashAlgorithm hashAlgorithmForLegacy; SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication) { - this(algorithmName, legacyRsa, usedForAuthentication, algorithmName); + this(algorithmName, legacyRsa, usedForAuthentication, algorithmName, null); } SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication, String jceAlgorithmName) { + this(algorithmName, legacyRsa, usedForAuthentication, jceAlgorithmName, null); + } + + SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication, String jceAlgorithmName, HashAlgorithm hashAlgorithmForLegacy) { this.algorithmName = algorithmName; this.legacyRsa = legacyRsa; this.usedForAuthentication = usedForAuthentication; this.jceAlgorithmName = jceAlgorithmName; + this.hashAlgorithmForLegacy = hashAlgorithmForLegacy; } /** @@ -118,6 +124,16 @@ public String getJceAlgorithmName() { return jceAlgorithmName; } + /** + * Returns the hash algorithm for legacy RSA algorithms. Used when creating {@link SignableData} + * so that {@link SignableData#calculateHash()} uses the correct hash for the signature algorithm. + * + * @return the hash algorithm for legacy algorithms, or null for RSASSA_PSS + */ + public HashAlgorithm getHashAlgorithmForLegacy() { + return hashAlgorithmForLegacy; + } + /** * Checks if the provided signature algorithm is supported. * diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index e1648bfd..c109af33 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -697,8 +697,7 @@ void certificateChoice_withSemanticIdentifier() { } @ParameterizedTest - // For signature algorithms SHA256_WITH_RSA_ENCRYPTION and SHA384_WITH_RSA_ENCRYPTION demo service returns HTTP 400 Bad Request - @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS", "SHA512_WITH_RSA_ENCRYPTION"}) + @EnumSource(SignatureAlgorithm.class) void signature_withSemanticsIdentifier(SignatureAlgorithm signatureAlgorithm) { var semanticIdentifier = new SemanticsIdentifier( // 3 character identity type @@ -729,8 +728,9 @@ void signature_withSemanticsIdentifier(SignatureAlgorithm signatureAlgorithm) { CertificateChoiceResponse response = certificateChoiceResponseValidator.validate(certificateSessionStatus, certificateLevel); // For example use digidoc4j use SignatureBuilder to create DataToSign using certificateChoiceResponse.getCertificate(); - // Create the signable data - var signableData = new SignableData("dataToSign".getBytes(), HashAlgorithm.SHA_512); + // Create the signable data so calculateHash() uses the correct hash for the signature algorithm + HashAlgorithm hashForDigest = signatureAlgorithm.isLegacyRsa() ? signatureAlgorithm.getHashAlgorithmForLegacy() : HashAlgorithm.SHA_512; + var signableData = new SignableData("dataToSign".getBytes(), hashForDigest); // Create the Semantics Identifier var semanticsIdentifier = new SemanticsIdentifier( @@ -773,8 +773,7 @@ void signature_withSemanticsIdentifier(SignatureAlgorithm signatureAlgorithm) { } @ParameterizedTest - // For signature algorithms SHA256_WITH_RSA_ENCRYPTION and SHA384_WITH_RSA_ENCRYPTION demo service returns HTTP 400 Bad Request - @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS", "SHA512_WITH_RSA_ENCRYPTION"}) + @EnumSource(SignatureAlgorithm.class) void signature_withDocumentNumber(SignatureAlgorithm signatureAlgorithm) { String documentNumber = "PNOEE-50001029996-DEMO-Q"; @@ -799,8 +798,9 @@ void signature_withDocumentNumber(SignatureAlgorithm signatureAlgorithm) { // For example use digidoc4j with SignatureBuilder to create DataToSign using `certificateByDocumentNumber.certificate()` - // Create the signable data - var signableData = new SignableData("dataToSign".getBytes(), HashAlgorithm.SHA_512); + // Create the signable data so calculateHash() uses the correct hash for the signature algorithm + HashAlgorithm hashForDigest = signatureAlgorithm.isLegacyRsa() ? signatureAlgorithm.getHashAlgorithmForLegacy() : HashAlgorithm.SHA_512; + var signableData = new SignableData("dataToSign".getBytes(), hashForDigest); NotificationSignatureSessionResponse signatureSessionResponse = smartIdClient.createNotificationSignature() .withCertificateLevel(certificateLevel) From 4e26682b184ff9a4fa6b67450332739971b6eb8b Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Wed, 4 Mar 2026 10:54:11 +0200 Subject: [PATCH 08/36] Split SignatureAlgorithm into AuthenticationSignatureAlgorithm and SigningSignatureAlgorithm --- .../AuthenticationResponseMapperImpl.java | 2 +- .../AuthenticationSignatureAlgorithm.java | 84 +++++++++++++++++++ ...nkAuthenticationSessionRequestBuilder.java | 7 +- ...iceLinkSignatureSessionRequestBuilder.java | 4 +- ...icationSignatureSessionRequestBuilder.java | 4 +- ...onAuthenticationSessionRequestBuilder.java | 7 +- ...icationSignatureSessionRequestBuilder.java | 4 +- .../ee/sk/smartid/RsaSsaPssParameters.java | 10 +-- .../java/ee/sk/smartid/SignatureResponse.java | 6 +- .../smartid/SignatureResponseValidator.java | 10 +-- .../smartid/SignatureValueValidatorImpl.java | 8 +- ...hm.java => SigningSignatureAlgorithm.java} | 64 +++++--------- ...nkAuthenticationResponseValidatorTest.java | 4 +- ...thenticationSessionRequestBuilderTest.java | 13 +-- ...inkSignatureSessionRequestBuilderTest.java | 10 +-- ...onAuthenticationResponseValidatorTest.java | 4 +- ...thenticationSessionRequestBuilderTest.java | 13 +-- ...ionSignatureSessionRequestBuilderTest.java | 8 +- .../SignatureResponseValidatorTest.java | 6 +- .../SignatureValueValidatorImplTest.java | 6 +- .../java/ee/sk/smartid/SmartIdClientTest.java | 10 +-- .../integration/ReadmeIntegrationTest.java | 10 +-- .../SmartIdRestIntegrationTest.java | 13 +-- 23 files changed, 172 insertions(+), 135 deletions(-) create mode 100644 src/main/java/ee/sk/smartid/AuthenticationSignatureAlgorithm.java rename src/main/java/ee/sk/smartid/{SignatureAlgorithm.java => SigningSignatureAlgorithm.java} (67%) diff --git a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java index 75639014..b4ee0740 100644 --- a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java +++ b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java @@ -177,7 +177,7 @@ private static void validateSignature(SessionSignature sessionSignature) { if (StringUtil.isEmpty(sessionSignature.getSignatureAlgorithm())) { throw new UnprocessableSmartIdResponseException("Authentication session status field 'signature.signatureAlgorithm' is empty"); } - if (!SignatureAlgorithm.isSupported(sessionSignature.getSignatureAlgorithm())) { + if (!AuthenticationSignatureAlgorithm.isSupported(sessionSignature.getSignatureAlgorithm())) { logger.error("Authentication session status field 'signature.signatureAlgorithm' has invalid value: {}", sessionSignature.getSignatureAlgorithm()); throw new UnprocessableSmartIdResponseException("Authentication session status field 'signature.signatureAlgorithm' has unsupported value"); } diff --git a/src/main/java/ee/sk/smartid/AuthenticationSignatureAlgorithm.java b/src/main/java/ee/sk/smartid/AuthenticationSignatureAlgorithm.java new file mode 100644 index 00000000..633febd7 --- /dev/null +++ b/src/main/java/ee/sk/smartid/AuthenticationSignatureAlgorithm.java @@ -0,0 +1,84 @@ +package ee.sk.smartid; + +/*- + * #%L + * Smart ID sample Java client + * %% + * Copyright (C) 2018 - 2026 SK ID Solutions AS + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Arrays; + +/** + * Signature algorithms supported for authentication sessions. + *

+ * Currently only RSASSA-PSS is allowed by the Smart-ID API. + */ +public enum AuthenticationSignatureAlgorithm { + + /** + * RSASSA-PSS (RSA Probabilistic Signature Scheme) as defined in PKCS #1 v2.1. + */ + RSASSA_PSS("rsassa-pss"); + + private final String algorithmName; + + AuthenticationSignatureAlgorithm(String algorithmName) { + this.algorithmName = algorithmName; + } + + /** + * Provides the signature algorithm name as used in the Smart-ID API. + * + * @return the signature algorithm name + */ + public String getAlgorithmName() { + return algorithmName; + } + + /** + * Checks if the provided signature algorithm is supported for authentication. + * + * @param signatureAlgorithm the signature algorithm name to check + * @return true if the signature algorithm is supported, false otherwise + */ + public static boolean isSupported(String signatureAlgorithm) { + return Arrays.stream(AuthenticationSignatureAlgorithm.values()) + .anyMatch(s -> s.getAlgorithmName().equals(signatureAlgorithm)); + } + + /** + * Converts a string representation of a signature algorithm to its corresponding enum value. + * + * @param signatureAlgorithm the signature algorithm name + * @return the corresponding AuthenticationSignatureAlgorithm enum value + * @throws IllegalArgumentException if the provided signature algorithm is not supported + */ + public static AuthenticationSignatureAlgorithm fromString(String signatureAlgorithm) { + return Arrays + .stream(AuthenticationSignatureAlgorithm.values()) + .filter(s -> s.getAlgorithmName().equals(signatureAlgorithm)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Invalid authentication signatureAlgorithm value: " + signatureAlgorithm)); + } +} + diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java index def0e949..35afaa84 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java @@ -59,7 +59,7 @@ public class DeviceLinkAuthenticationSessionRequestBuilder { private String relyingPartyName; private AuthenticationCertificateLevel certificateLevel = AuthenticationCertificateLevel.QUALIFIED; private String rpChallenge; - private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSASSA_PSS; + private AuthenticationSignatureAlgorithm signatureAlgorithm = AuthenticationSignatureAlgorithm.RSASSA_PSS; private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA3_512; private List interactions; private Boolean shareMdClientIpAddress; @@ -136,7 +136,7 @@ public DeviceLinkAuthenticationSessionRequestBuilder withRpChallenge(String rpCh * @param signatureAlgorithm the signature algorithm * @return this builder */ - public DeviceLinkAuthenticationSessionRequestBuilder withSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { + public DeviceLinkAuthenticationSessionRequestBuilder withSignatureAlgorithm(AuthenticationSignatureAlgorithm signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; return this; } @@ -303,9 +303,6 @@ private void validateSignatureParameters() { if (signatureAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be set"); } - if (!signatureAlgorithm.isUsedForAuthentication()) { - throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be an algorithm supported for authentication (e.g. RSASSA_PSS). Legacy RSA algorithms are only supported for signing"); - } if (hashAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'hashAlgorithm' must be set"); } diff --git a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java index 31a5063d..4df3adbb 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java @@ -63,7 +63,7 @@ public class DeviceLinkSignatureSessionRequestBuilder { private Set capabilities; private List interactions; private Boolean shareMdClientIpAddress; - private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSASSA_PSS; + private SigningSignatureAlgorithm signatureAlgorithm = SigningSignatureAlgorithm.RSASSA_PSS; private String initialCallbackUrl; private DigestInput digestInput; @@ -183,7 +183,7 @@ public DeviceLinkSignatureSessionRequestBuilder withShareMdClientIpAddress(boole * @param signatureAlgorithm the signature algorithm * @return this builder */ - public DeviceLinkSignatureSessionRequestBuilder withSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { + public DeviceLinkSignatureSessionRequestBuilder withSignatureAlgorithm(SigningSignatureAlgorithm signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; return this; } diff --git a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java index 34887904..ee1afc04 100644 --- a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java @@ -54,7 +54,7 @@ public class LinkedNotificationSignatureSessionRequestBuilder { private String relyingPartyName; private String documentNumber; private DigestInput digestInput; - private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSASSA_PSS; + private SigningSignatureAlgorithm signatureAlgorithm = SigningSignatureAlgorithm.RSASSA_PSS; private String linkedSessionID; private List interactions; private CertificateLevel certificateLevel; @@ -151,7 +151,7 @@ public LinkedNotificationSignatureSessionRequestBuilder withSignableHash(Signabl * @param signatureAlgorithm The signature algorithm * @return this builder */ - public LinkedNotificationSignatureSessionRequestBuilder withSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { + public LinkedNotificationSignatureSessionRequestBuilder withSignatureAlgorithm(SigningSignatureAlgorithm signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; return this; } diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java index 6d14a37c..c8832d82 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java @@ -57,7 +57,7 @@ public class NotificationAuthenticationSessionRequestBuilder { private String relyingPartyName; private AuthenticationCertificateLevel certificateLevel; private String rpChallenge; - private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSASSA_PSS; + private AuthenticationSignatureAlgorithm signatureAlgorithm = AuthenticationSignatureAlgorithm.RSASSA_PSS; private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA3_512; private List interactions; private Boolean shareMdClientIpAddress; @@ -130,7 +130,7 @@ public NotificationAuthenticationSessionRequestBuilder withRpChallenge(String rp * @param signatureAlgorithm the signature algorithm * @return this builder */ - public NotificationAuthenticationSessionRequestBuilder withSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { + public NotificationAuthenticationSessionRequestBuilder withSignatureAlgorithm(AuthenticationSignatureAlgorithm signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; return this; } @@ -275,9 +275,6 @@ private void validateSignatureParameters() { if (signatureAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be set"); } - if (!signatureAlgorithm.isUsedForAuthentication()) { - throw new SmartIdRequestSetupException("Value for 'signatureAlgorithm' must be an algorithm supported for authentication (e.g. RSASSA_PSS). Legacy RSA algorithms are only supported for signing"); - } if (hashAlgorithm == null) { throw new SmartIdRequestSetupException("Value for 'hashAlgorithm' must be set"); } diff --git a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java index 8a518037..4cc1f62a 100644 --- a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java @@ -69,7 +69,7 @@ public class NotificationSignatureSessionRequestBuilder { private Set capabilities; private List interactions; private Boolean shareMdClientIpAddress; - private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSASSA_PSS; + private SigningSignatureAlgorithm signatureAlgorithm = SigningSignatureAlgorithm.RSASSA_PSS; private DigestInput digestInput; /** @@ -186,7 +186,7 @@ public NotificationSignatureSessionRequestBuilder withShareMdClientIpAddress(boo * @param signatureAlgorithm the signature algorithm * @return this builder */ - public NotificationSignatureSessionRequestBuilder withSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { + public NotificationSignatureSessionRequestBuilder withSignatureAlgorithm(SigningSignatureAlgorithm signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; return this; } diff --git a/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java b/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java index 81bc797f..feaaa8ba 100644 --- a/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java +++ b/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java @@ -31,7 +31,7 @@ */ public class RsaSsaPssParameters { - private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSASSA_PSS; + private final String signatureAlgorithmName = "rsassa-pss"; private HashAlgorithm digestHashAlgorithm; private MaskGenAlgorithm maskGenAlgorithm; @@ -85,12 +85,12 @@ public void setTrailerField(TrailerField trailerField) { } /** - * Gets the signature algorithm + * Gets the signature algorithm name * - * @return the signature algorithm; see {@link SignatureAlgorithm} + * @return the signature algorithm name */ - public SignatureAlgorithm getSignatureAlgorithm() { - return signatureAlgorithm; + public String getSignatureAlgorithmName() { + return signatureAlgorithmName; } /** diff --git a/src/main/java/ee/sk/smartid/SignatureResponse.java b/src/main/java/ee/sk/smartid/SignatureResponse.java index 31e933d5..62770df9 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponse.java +++ b/src/main/java/ee/sk/smartid/SignatureResponse.java @@ -40,7 +40,7 @@ public class SignatureResponse implements Serializable { private String endResult; private String signatureValueInBase64; private String algorithmName; - private SignatureAlgorithm signatureAlgorithm; + private SigningSignatureAlgorithm signatureAlgorithm; private FlowType flowType; private X509Certificate certificate; private CertificateLevel requestedCertificateLevel; @@ -124,7 +124,7 @@ public void setAlgorithmName(String algorithmName) { * * @return the signature algorithm */ - public SignatureAlgorithm getSignatureAlgorithm() { + public SigningSignatureAlgorithm getSignatureAlgorithm() { return signatureAlgorithm; } @@ -133,7 +133,7 @@ public SignatureAlgorithm getSignatureAlgorithm() { * * @param signatureAlgorithm the signature algorithm */ - public void setSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { + public void setSignatureAlgorithm(SigningSignatureAlgorithm signatureAlgorithm) { this.signatureAlgorithm = signatureAlgorithm; } diff --git a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java index 4557855d..4464c4d3 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java @@ -110,9 +110,9 @@ public SignatureResponse validate(SessionStatus sessionStatus, signatureResponse.setEndResult(sessionResult.getEndResult()); signatureResponse.setSignatureValueInBase64(sessionSignature.getValue()); signatureResponse.setAlgorithmName(sessionSignature.getSignatureAlgorithm()); - signatureResponse.setSignatureAlgorithm(SignatureAlgorithm.fromString(sessionSignature.getSignatureAlgorithm())); + signatureResponse.setSignatureAlgorithm(SigningSignatureAlgorithm.fromString(sessionSignature.getSignatureAlgorithm())); - if (!SignatureAlgorithm.isLegacyRsa(sessionSignature.getSignatureAlgorithm())) { + if (!SigningSignatureAlgorithm.isLegacyRsa(sessionSignature.getSignatureAlgorithm())) { SessionSignatureAlgorithmParameters signatureAlgorithmParameters = sessionSignature.getSignatureAlgorithmParameters(); var rsaSsaPssParams = new RsaSsaPssParameters(); rsaSsaPssParams.setDigestHashAlgorithm(HashAlgorithm.fromString(signatureAlgorithmParameters.getHashAlgorithm()).orElse(null)); @@ -237,7 +237,7 @@ private static void validateRawDigestSignature(SessionStatus sessionStatus) { validateSignatureValue(signature.getValue()); validateSignatureAlgorithmName(signature.getSignatureAlgorithm()); validateFlowType(signature.getFlowType()); - if (!SignatureAlgorithm.isLegacyRsa(signature.getSignatureAlgorithm())) { + if (!SigningSignatureAlgorithm.isLegacyRsa(signature.getSignatureAlgorithm())) { validateSignatureAlgorithmParameters(signature.getSignatureAlgorithmParameters()); } } @@ -256,8 +256,8 @@ private static void validateSignatureAlgorithmName(String signatureAlgorithm) { throw new UnprocessableSmartIdResponseException("Signature session status field 'signature.signatureAlgorithm' is missing"); } - if (!SignatureAlgorithm.isSupported(signatureAlgorithm)) { - List possibleValues = Arrays.stream(SignatureAlgorithm.values()).map(SignatureAlgorithm::getAlgorithmName).toList(); + if (!SigningSignatureAlgorithm.isSupported(signatureAlgorithm)) { + List possibleValues = Arrays.stream(SigningSignatureAlgorithm.values()).map(SigningSignatureAlgorithm::getAlgorithmName).toList(); logger.error("Signature session status field 'signature.signatureAlgorithm' has unsupported value: {}. Possible values: {}", signatureAlgorithm, possibleValues); throw new UnprocessableSmartIdResponseException("Signature session status field 'signature.signatureAlgorithm' has unsupported value"); } diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java index a007d148..e9eefaa1 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java @@ -83,11 +83,11 @@ public void validate(byte[] signatureValue, if (signatureAlgorithmName == null || signatureAlgorithmName.isBlank()) { throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); } - if (!SignatureAlgorithm.isLegacyRsa(signatureAlgorithmName)) { + if (!SigningSignatureAlgorithm.isLegacyRsa(signatureAlgorithmName)) { throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signatureAlgorithmName + "' is not a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm; use validate(..., RsaSsaPssParameters) for RSASSA-PSS"); } try { - SignatureAlgorithm algorithm = SignatureAlgorithm.fromString(signatureAlgorithmName); + SigningSignatureAlgorithm algorithm = SigningSignatureAlgorithm.fromString(signatureAlgorithmName); Signature signature = Signature.getInstance(algorithm.getJceAlgorithmName()); signature.initVerify(certificate.getPublicKey()); signature.update(payload); @@ -106,11 +106,11 @@ private Signature getSignature(RsaSsaPssParameters rsaSsaPssParameters) { new MGF1ParameterSpec(rsaSsaPssParameters.getMaskHashAlgorithm().getAlgorithmName()), rsaSsaPssParameters.getSaltLength(), rsaSsaPssParameters.getTrailerField().getPssSpecValue()); - var signature = Signature.getInstance(rsaSsaPssParameters.getSignatureAlgorithm().getAlgorithmName()); + var signature = Signature.getInstance(rsaSsaPssParameters.getSignatureAlgorithmName()); signature.setParameter(params); return signature; } catch (NoSuchAlgorithmException ex) { - logger.error("Invalid signature algorithm was provided: {}", rsaSsaPssParameters.getSignatureAlgorithm()); + logger.error("Invalid signature algorithm name was provided: {}", rsaSsaPssParameters.getSignatureAlgorithmName()); throw new UnprocessableSmartIdResponseException("Invalid signature algorithm was provided", ex); } catch (InvalidAlgorithmParameterException ex) { throw new UnprocessableSmartIdResponseException("Invalid signature algorithm parameters were provided", ex); diff --git a/src/main/java/ee/sk/smartid/SignatureAlgorithm.java b/src/main/java/ee/sk/smartid/SigningSignatureAlgorithm.java similarity index 67% rename from src/main/java/ee/sk/smartid/SignatureAlgorithm.java rename to src/main/java/ee/sk/smartid/SigningSignatureAlgorithm.java index c30cf38d..f3e66dbf 100644 --- a/src/main/java/ee/sk/smartid/SignatureAlgorithm.java +++ b/src/main/java/ee/sk/smartid/SigningSignatureAlgorithm.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,58 +29,43 @@ import java.util.Arrays; /** - * Signature algorithms supported by Smart-ID API. + * Signature algorithms supported for signing sessions. *

- * Algorithms are divided by use case: - *

    - *
  • Authentication: only {@link #RSASSA_PSS} is supported for authentication sessions.
  • - *
  • Signing: {@link #RSASSA_PSS} and the RSASSA-PKCS#1 v1.5 algorithms - * ({@link #SHA256_WITH_RSA_ENCRYPTION}, {@link #SHA384_WITH_RSA_ENCRYPTION}, {@link #SHA512_WITH_RSA_ENCRYPTION}) - * are supported for signature sessions.
  • - *
- * RSASSA-PKCS#1 v1.5 algorithms do not use {@code signatureAlgorithmParameters} in the API request or response. + * Includes RSASSA-PSS and RSASSA-PKCS#1 v1.5 algorithms. The latter are marked as legacy RSA. */ -public enum SignatureAlgorithm { +public enum SigningSignatureAlgorithm { /** * RSASSA-PSS (RSA Probabilistic Signature Scheme) as defined in PKCS #1 v2.1. - * Supported for both authentication and signing. */ - RSASSA_PSS("rsassa-pss", false, true), + RSASSA_PSS("rsassa-pss", false, "rsassa-pss", null), /** * RSASSA-PKCS#1 v1.5 with SHA-256. Signing only; no signatureAlgorithmParameters. */ - SHA256_WITH_RSA_ENCRYPTION("sha256WithRSAEncryption", true, false, "SHA256withRSA", HashAlgorithm.SHA_256), + SHA256_WITH_RSA_ENCRYPTION("sha256WithRSAEncryption", true, "SHA256withRSA", HashAlgorithm.SHA_256), /** * RSASSA-PKCS#1 v1.5 with SHA-384. Signing only; no signatureAlgorithmParameters. */ - SHA384_WITH_RSA_ENCRYPTION("sha384WithRSAEncryption", true, false, "SHA384withRSA", HashAlgorithm.SHA_384), + SHA384_WITH_RSA_ENCRYPTION("sha384WithRSAEncryption", true, "SHA384withRSA", HashAlgorithm.SHA_384), /** * RSASSA-PKCS#1 v1.5 with SHA-512. Signing only; no signatureAlgorithmParameters. */ - SHA512_WITH_RSA_ENCRYPTION("sha512WithRSAEncryption", true, false, "SHA512withRSA", HashAlgorithm.SHA_512); + SHA512_WITH_RSA_ENCRYPTION("sha512WithRSAEncryption", true, "SHA512withRSA", HashAlgorithm.SHA_512); private final String algorithmName; private final boolean legacyRsa; - private final boolean usedForAuthentication; private final String jceAlgorithmName; private final HashAlgorithm hashAlgorithmForLegacy; - SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication) { - this(algorithmName, legacyRsa, usedForAuthentication, algorithmName, null); - } - - SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication, String jceAlgorithmName) { - this(algorithmName, legacyRsa, usedForAuthentication, jceAlgorithmName, null); - } - - SignatureAlgorithm(String algorithmName, boolean legacyRsa, boolean usedForAuthentication, String jceAlgorithmName, HashAlgorithm hashAlgorithmForLegacy) { + SigningSignatureAlgorithm(String algorithmName, + boolean legacyRsa, + String jceAlgorithmName, + HashAlgorithm hashAlgorithmForLegacy) { this.algorithmName = algorithmName; this.legacyRsa = legacyRsa; - this.usedForAuthentication = usedForAuthentication; this.jceAlgorithmName = jceAlgorithmName; this.hashAlgorithmForLegacy = hashAlgorithmForLegacy; } @@ -104,16 +89,6 @@ public boolean isLegacyRsa() { return legacyRsa; } - /** - * Returns whether this algorithm is supported for authentication sessions. - * Only RSASSA-PSS is supported for authentication. - * - * @return true if the algorithm may be used for authentication - */ - public boolean isUsedForAuthentication() { - return usedForAuthentication; - } - /** * Returns the JCE standard algorithm name for {@link java.security.Signature#getInstance(String)}. * For legacy RSA algorithms this is the name used to verify the signature (e.g. SHA256withRSA). @@ -135,13 +110,13 @@ public HashAlgorithm getHashAlgorithmForLegacy() { } /** - * Checks if the provided signature algorithm is supported. + * Checks if the provided signature algorithm is supported for signing. * * @param signatureAlgorithm the signature algorithm name to check * @return true if the signature algorithm is supported, false otherwise */ public static boolean isSupported(String signatureAlgorithm) { - return Arrays.stream(SignatureAlgorithm.values()) + return Arrays.stream(SigningSignatureAlgorithm.values()) .anyMatch(s -> s.getAlgorithmName().equals(signatureAlgorithm)); } @@ -152,23 +127,24 @@ public static boolean isSupported(String signatureAlgorithm) { * @return true if legacy RSA, false otherwise */ public static boolean isLegacyRsa(String signatureAlgorithm) { - return Arrays.stream(SignatureAlgorithm.values()) + return Arrays.stream(SigningSignatureAlgorithm.values()) .filter(s -> s.getAlgorithmName().equals(signatureAlgorithm)) - .anyMatch(SignatureAlgorithm::isLegacyRsa); + .anyMatch(SigningSignatureAlgorithm::isLegacyRsa); } /** * Converts a string representation of a signature algorithm to its corresponding enum value. * * @param signatureAlgorithm the signature algorithm name - * @return the corresponding SignatureAlgorithm enum value + * @return the corresponding SigningSignatureAlgorithm enum value * @throws IllegalArgumentException if the provided signature algorithm is not supported */ - public static SignatureAlgorithm fromString(String signatureAlgorithm) { + public static SigningSignatureAlgorithm fromString(String signatureAlgorithm) { return Arrays - .stream(SignatureAlgorithm.values()) + .stream(SigningSignatureAlgorithm.values()) .filter(s -> s.getAlgorithmName().equals(signatureAlgorithm)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Invalid signatureAlgorithm value: " + signatureAlgorithm)); } } + diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java index 1f1ba798..2c60534c 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java @@ -231,7 +231,7 @@ private static SessionStatus toSessionStatus(String certificateValue, signature.setUserChallenge(userChallengeVerifier); signature.setValue(toBase64("signatureValue")); signature.setFlowType(flowType.getDescription()); - signature.setSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName()); + signature.setSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS.getAlgorithmName()); signature.setSignatureAlgorithmParameters(sessionSignatureAlgorithmParameters); var cert = new SessionCertificate(); @@ -254,7 +254,7 @@ private static DeviceLinkAuthenticationSessionRequest toAuthenticationSessionReq "DEMO", certificateLevel, SignatureProtocol.ACSP_V2, - new AcspV2SignatureProtocolParameters("rpChallenge", SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())), + new AcspV2SignatureProtocolParameters("rpChallenge", AuthenticationSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())), InteractionUtil.encodeToBase64(List.of(new Interaction(DeviceLinkInteractionType.DISPLAY_TEXT_AND_PIN.getCode(), "Log in?", null))), null, null, diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java index 31d23539..4eae47bd 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java @@ -145,8 +145,8 @@ void initAuthenticationSession_certificateLevel_ok(AuthenticationCertificateLeve } @ParameterizedTest - @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS"}) - void initAuthenticationSession_signatureAlgorithm_ok(SignatureAlgorithm signatureAlgorithm) { + @EnumSource(value = AuthenticationSignatureAlgorithm.class, names = {"RSASSA_PSS"}) + void initAuthenticationSession_signatureAlgorithm_ok(AuthenticationSignatureAlgorithm signatureAlgorithm) { when(connector.initAnonymousDeviceLinkAuthentication(any(DeviceLinkAuthenticationSessionRequest.class))) .thenReturn(toDeviceLinkAuthenticationResponse()); @@ -280,15 +280,6 @@ void initAuthenticationSession_signatureAlgorithmIsSetToNull_throwException() { assertEquals("Value for 'signatureAlgorithm' must be set", exception.getMessage()); } - @ParameterizedTest - @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void initAuthenticationSession_legacyRsaSignatureAlgorithm_throwException(SignatureAlgorithm signatureAlgorithm) { - DeviceLinkAuthenticationSessionRequestBuilder builder = toDeviceLinkRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); - - var exception = assertThrows(SmartIdRequestSetupException.class, builder::initAuthenticationSession); - assertTrue(exception.getMessage().contains("supported for authentication")); - } - @ParameterizedTest @NullAndEmptySource void initAuthenticationSession_interactionsIsEmpty_throwException(List interactions) { diff --git a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java index d1f73f87..b9484d89 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java @@ -163,7 +163,7 @@ void initSignatureSession_withRequestProperties() { @Test void initSignatureSession_withSignatureAlgorithm_setsCorrectAlgorithm() { when(connector.initDeviceLinkSignature(any(DeviceLinkSignatureSessionRequest.class), any(SemanticsIdentifier.class))).thenReturn(mockSignatureSessionResponse()); - var deviceLinkSessionRequestBuilder = toDeviceLinkSignatureSessionRequestBuilder(b -> b.withSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS)); + var deviceLinkSessionRequestBuilder = toDeviceLinkSignatureSessionRequestBuilder(b -> b.withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS)); DeviceLinkSessionResponse signatureSessionResponse = deviceLinkSessionRequestBuilder.initSignatureSession(); @@ -173,12 +173,12 @@ void initSignatureSession_withSignatureAlgorithm_setsCorrectAlgorithm() { verify(connector).initDeviceLinkSignature(requestCaptor.capture(), any(SemanticsIdentifier.class)); DeviceLinkSignatureSessionRequest capturedRequest = requestCaptor.getValue(); - assertEquals(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); } @ParameterizedTest - @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void initSignatureSession_withLegacyRsaAlgorithm_omitsSignatureAlgorithmParameters(SignatureAlgorithm signatureAlgorithm) { + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void initSignatureSession_withLegacyRsaAlgorithm_omitsSignatureAlgorithmParameters(SigningSignatureAlgorithm signatureAlgorithm) { when(connector.initDeviceLinkSignature(any(DeviceLinkSignatureSessionRequest.class), any(SemanticsIdentifier.class))).thenReturn(mockSignatureSessionResponse()); var deviceLinkSessionRequestBuilder = toDeviceLinkSignatureSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); @@ -274,7 +274,7 @@ void initSignatureSession_withDefaultAlgorithmWhenNoSignatureAlgorithmSet() { verify(connector).initDeviceLinkSignature(requestCaptor.capture(), any(SemanticsIdentifier.class)); DeviceLinkSignatureSessionRequest capturedRequest = requestCaptor.getValue(); - assertEquals(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); } @Test diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java index 8ae7b76f..1b066692 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java @@ -149,7 +149,7 @@ private static NotificationAuthenticationSessionRequest toAuthenticationSessionR certificateLevel, SignatureProtocol.ACSP_V2.name(), new AcspV2SignatureProtocolParameters("3mhDkd0ulDR/WVZx678FcrNw4pUhrZxcQsmejf8jQ1HtSp3GAxCH/Fi9EEiuULp44G/KNKONPXZELqCSZw4AoA==", - SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + AuthenticationSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())), "W3sidHlwZSI6ImRpc3BsYXlUZXh0QW5kUElOIiwiZGlzcGxheVRleHQ2MCI6IkxvZyBpbiB3aXRoIFNtYXJ0LUlEIGRlbW8/In1d", null, @@ -180,7 +180,7 @@ private static SessionStatus toSessionsStatus(String certificateValue, String ce signature.setUserChallenge("RvrVNS1GJYCsuEnEqPCdHHn5vl65F3XiBjmxB4zSosw"); signature.setValue(signatureValue); signature.setFlowType(FlowType.NOTIFICATION.getDescription()); - signature.setSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName()); + signature.setSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS.getAlgorithmName()); signature.setSignatureAlgorithmParameters(sessionSignatureAlgorithmParameters); var cert = new SessionCertificate(); diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java index 87ccd153..abbddca1 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java @@ -135,8 +135,8 @@ void initAuthenticationSession_certificateLevel_ok(AuthenticationCertificateLeve } @ParameterizedTest - @EnumSource(value = SignatureAlgorithm.class, names = {"RSASSA_PSS"}) - void initAuthenticationSession_signatureAlgorithm_ok(SignatureAlgorithm signatureAlgorithm) { + @EnumSource(value = AuthenticationSignatureAlgorithm.class, names = {"RSASSA_PSS"}) + void initAuthenticationSession_signatureAlgorithm_ok(AuthenticationSignatureAlgorithm signatureAlgorithm) { when(connector.initNotificationAuthentication(any(NotificationAuthenticationSessionRequest.class), any(String.class))).thenReturn(toNotificationAuthenticationResponse()); NotificationAuthenticationSessionRequestBuilder builder = toNotificationAuthenticationSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); @@ -149,15 +149,6 @@ void initAuthenticationSession_signatureAlgorithm_ok(SignatureAlgorithm signatur assertEquals(signatureAlgorithm.getAlgorithmName(), request.signatureProtocolParameters().signatureAlgorithm()); } - @ParameterizedTest - @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void initAuthenticationSession_legacyRsaSignatureAlgorithm_throwException(SignatureAlgorithm signatureAlgorithm) { - NotificationAuthenticationSessionRequestBuilder builder = toNotificationAuthenticationSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); - - var exception = assertThrows(SmartIdRequestSetupException.class, builder::initAuthenticationSession); - assertTrue(exception.getMessage().contains("supported for authentication")); - } - @ParameterizedTest @EnumSource void initAuthenticationSession_hashAlgorithm_ok(HashAlgorithm expectedHashAlgorithm) { diff --git a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java index 55bbf052..c39fc8d5 100644 --- a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java @@ -174,7 +174,7 @@ void initSignatureSession_useDefaultHashAlgorithmForSignableHash_ok() { NotificationSignatureSessionRequest capturedRequest = requestCaptor.getValue(); assertEquals(HashAlgorithm.SHA_512.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); - assertEquals(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); } @ParameterizedTest @@ -192,7 +192,7 @@ void initSignatureSession_overrideDefaultHashAlgorithmForSignableHash_ok(HashAlg verify(connector).initNotificationSignature(requestCaptor.capture(), any(SemanticsIdentifier.class)); NotificationSignatureSessionRequest capturedRequest = requestCaptor.getValue(); - assertEquals(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); assertEquals(Base64.getEncoder().encodeToString("Test hash".getBytes()), capturedRequest.signatureProtocolParameters().digest()); assertEquals(hashAlgorithm.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); assertEquals(SignatureProtocol.RAW_DIGEST_SIGNATURE.name(), capturedRequest.signatureProtocol()); @@ -211,7 +211,7 @@ void initSignatureSession_useDefaultHashAlgorithmForSignableData_ok() { NotificationSignatureSessionRequest capturedRequest = requestCaptor.getValue(); assertEquals(HashAlgorithm.SHA_512.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); - assertEquals(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); } @ParameterizedTest @@ -227,7 +227,7 @@ void initSignatureSession_overrideDefaultHashAlgorithmForSignableData_ok(HashAlg verify(connector).initNotificationSignature(requestCaptor.capture(), any(SemanticsIdentifier.class)); NotificationSignatureSessionRequest capturedRequest = requestCaptor.getValue(); - assertEquals(SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); assertEquals(Base64.getEncoder().encodeToString(signableData.calculateHash()), capturedRequest.signatureProtocolParameters().digest()); assertEquals(hashAlgorithm.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); assertEquals(SignatureProtocol.RAW_DIGEST_SIGNATURE.name(), capturedRequest.signatureProtocol()); diff --git a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java index 03530da5..f12c95b8 100644 --- a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java @@ -111,8 +111,8 @@ void validate_nqSigning_ok() { } @ParameterizedTest - @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_legacyRsaSignature_ok_withoutSignatureAlgorithmParameters(SignatureAlgorithm signatureAlgorithm) { + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_legacyRsaSignature_ok_withoutSignatureAlgorithmParameters(SigningSignatureAlgorithm signatureAlgorithm) { SessionStatus sessionStatus = toQualifiedSignatureSessionStatus("RAW_DIGEST_SIGNATURE", signatureAlgorithm.getAlgorithmName()); sessionStatus.setSignatureProtocol("RAW_DIGEST_SIGNATURE"); sessionStatus.getSignature().setSignatureAlgorithmParameters(null); @@ -558,7 +558,7 @@ private static SessionStatus toNqignatureSessionStatus() { sessionCertificate.setValue(CertificateUtil.getEncodedCertificateData(NQ_SIGNING_CERTIFICATE)); var params = toSessionSignatureAlgorithmParams(); - var sessionSignature = toSessionSignature(NQ_SIGNATURE_VALUE, SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), params, FlowType.QR); + var sessionSignature = toSessionSignature(NQ_SIGNATURE_VALUE, SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), params, FlowType.QR); var sessionStatus = new SessionStatus(); sessionStatus.setState("COMPLETE"); diff --git a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java index 9f62c32b..3e0f86ed 100644 --- a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java @@ -100,8 +100,8 @@ void validateSignatureValue_constructedPayloadDoesNotMatchTheSignature_throwExce } @ParameterizedTest - @EnumSource(value = SignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_legacyRsa_invalidSignature_throwException(SignatureAlgorithm algorithm) throws CertificateException { + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_legacyRsa_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) throws CertificateException { var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( "invalidSignature".getBytes(StandardCharsets.UTF_8), @@ -118,7 +118,7 @@ void validate_legacyRsa_nonLegacyAlgorithmName_throwException() throws Certifica SIGNATURE_VALUE, PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), - SignatureAlgorithm.RSASSA_PSS.getAlgorithmName())); + SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName())); assertTrue(ex.getMessage().contains("not a legacy RSA")); } diff --git a/src/test/java/ee/sk/smartid/SmartIdClientTest.java b/src/test/java/ee/sk/smartid/SmartIdClientTest.java index a44def42..4c8f6a88 100644 --- a/src/test/java/ee/sk/smartid/SmartIdClientTest.java +++ b/src/test/java/ee/sk/smartid/SmartIdClientTest.java @@ -455,7 +455,7 @@ void createLinkedNotificationSignature_onlyRequiredFields_ok() { LinkedSignatureSessionResponse response = smartIdClient.createLinkedNotificationSignature() .withDocumentNumber(DOCUMENT_NUMBER) .withSignableData(new SignableData("Test data".getBytes())) - .withSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS) + .withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS) .withLinkedSessionID("10000000-0000-000-000-000000000000") .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Sign?"))) .initSignatureSession(); @@ -473,7 +473,7 @@ void createLinkedNotificationSignature_allFields_ok() { .withDocumentNumber(DOCUMENT_NUMBER) .withCertificateLevel(CertificateLevel.QUALIFIED) .withSignableData(new SignableData("Test data".getBytes())) - .withSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS) + .withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS) .withLinkedSessionID("10000000-0000-000-000-000000000000") .withNonce("cmFuZG9tTm9uY2U=") .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Sign?"))) @@ -522,7 +522,7 @@ void createDynamicContent_authenticationForSameDeviceFlows(DeviceLinkType device DeviceLinkAuthenticationSessionRequestBuilder builder = smartIdClient.createDeviceLinkAuthentication() .withRpChallenge(Base64.toBase64String("a".repeat(32).getBytes())) - .withSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Log in?"))) .withHashAlgorithm(HashAlgorithm.SHA3_512) .withInitialCallbackUrl(INITIAL_CALLBACK_URL); @@ -552,7 +552,7 @@ void createDynamicContent_authenticationWithQRCode() { DeviceLinkAuthenticationSessionRequestBuilder builder = smartIdClient.createDeviceLinkAuthentication() .withRpChallenge(Base64.toBase64String("a".repeat(32).getBytes())) - .withSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Log in?"))) .withHashAlgorithm(HashAlgorithm.SHA3_512); DeviceLinkSessionResponse response = builder.initAuthenticationSession(); @@ -583,7 +583,7 @@ void createDynamicContent_authenticationWithQRCodeImage() { DeviceLinkAuthenticationSessionRequestBuilder builder = smartIdClient.createDeviceLinkAuthentication() .withRpChallenge(Base64.toBase64String("a".repeat(32).getBytes())) - .withSignatureAlgorithm(SignatureAlgorithm.RSASSA_PSS) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Log in?"))) .withHashAlgorithm(HashAlgorithm.SHA3_512); DeviceLinkSessionResponse response = builder.initAuthenticationSession(); diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index c109af33..b0fad963 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -74,7 +74,7 @@ import ee.sk.smartid.RpChallengeGenerator; import ee.sk.smartid.SessionType; import ee.sk.smartid.SignableData; -import ee.sk.smartid.SignatureAlgorithm; +import ee.sk.smartid.SigningSignatureAlgorithm; import ee.sk.smartid.SignatureCertificatePurposeValidator; import ee.sk.smartid.SignatureCertificatePurposeValidatorFactory; import ee.sk.smartid.SignatureCertificatePurposeValidatorFactoryImpl; @@ -697,8 +697,8 @@ void certificateChoice_withSemanticIdentifier() { } @ParameterizedTest - @EnumSource(SignatureAlgorithm.class) - void signature_withSemanticsIdentifier(SignatureAlgorithm signatureAlgorithm) { + @EnumSource(SigningSignatureAlgorithm.class) + void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgorithm) { var semanticIdentifier = new SemanticsIdentifier( // 3 character identity type // (PAS-passport, IDC-national identity card or PNO - (national) personal number) @@ -773,8 +773,8 @@ void signature_withSemanticsIdentifier(SignatureAlgorithm signatureAlgorithm) { } @ParameterizedTest - @EnumSource(SignatureAlgorithm.class) - void signature_withDocumentNumber(SignatureAlgorithm signatureAlgorithm) { + @EnumSource(SigningSignatureAlgorithm.class) + void signature_withDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) { String documentNumber = "PNOEE-50001029996-DEMO-Q"; CertificateLevel certificateLevel = CertificateLevel.QSCD; diff --git a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java index 2f30d371..5acb8a91 100644 --- a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java @@ -41,7 +41,8 @@ import ee.sk.smartid.DigestCalculator; import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.RpChallengeGenerator; -import ee.sk.smartid.SignatureAlgorithm; +import ee.sk.smartid.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.SigningSignatureAlgorithm; import ee.sk.smartid.SignatureProtocol; import ee.sk.smartid.SmartIdDemoIntegrationTest; import ee.sk.smartid.VerificationCodeType; @@ -129,7 +130,7 @@ void initDeviceLinkAuthentication_withSemanticsIdentifier() { private static DeviceLinkAuthenticationSessionRequest toDeviceLinkAuthenticationSessionRequest() { var signatureParameters = new AcspV2SignatureProtocolParameters( RpChallengeGenerator.generate().toBase64EncodedValue(), - SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + AuthenticationSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())); return new DeviceLinkAuthenticationSessionRequest(RELYING_PARTY_UUID, @@ -175,7 +176,7 @@ class Signature { @Test void initDeviceLinkSignature_withSemanticIdentifier() { var signatureProtocolParameters = new RawDigestSignatureProtocolParameters(Base64.toBase64String(DigestCalculator.calculateDigest("test".getBytes(), HashAlgorithm.SHA3_512)), - SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())); var request = new DeviceLinkSignatureSessionRequest(RELYING_PARTY_UUID, RELYING_PARTY_NAME, @@ -201,7 +202,7 @@ void initDeviceLinkSignature_withSemanticIdentifier() { void initDeviceLinkSignature_withDocumentNumber() { var signatureProtocolParameters = new RawDigestSignatureProtocolParameters( Base64.toBase64String(DigestCalculator.calculateDigest("test".getBytes(), HashAlgorithm.SHA_512)), - SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())); var request = new DeviceLinkSignatureSessionRequest(RELYING_PARTY_UUID, RELYING_PARTY_NAME, @@ -255,7 +256,7 @@ void initNotificationAuthentication_withDocumentNumber() { private static NotificationAuthenticationSessionRequest toAuthenticationRequest() { var signatureParameters = new AcspV2SignatureProtocolParameters( RpChallengeGenerator.generate().toBase64EncodedValue(), - SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + AuthenticationSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA_512.getAlgorithmName())); return new NotificationAuthenticationSessionRequest(RELYING_PARTY_UUID, @@ -312,7 +313,7 @@ void initNotificationCertificateChoice_withDocumentNumber() { private static NotificationSignatureSessionRequest toSignatureSessionRequest() { var signatureProtocolParameters = new RawDigestSignatureProtocolParameters( Base64.toBase64String(DigestCalculator.calculateDigest("test".getBytes(), HashAlgorithm.SHA_512)), - SignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), new SignatureAlgorithmParameters(HashAlgorithm.SHA3_512.getAlgorithmName())); return new NotificationSignatureSessionRequest(RELYING_PARTY_UUID, RELYING_PARTY_NAME, From 520516c3940ab9d6345939d1c89e611409c3e616 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Wed, 4 Mar 2026 16:25:18 +0200 Subject: [PATCH 09/36] Try to make tests stable by using SessionStatusPoller.fetchFinalSessionStatus instead of SessionStatusPoller.getSessionStatus --- .../java/ee/sk/smartid/integration/ReadmeIntegrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index b0fad963..2d48ee4c 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -467,7 +467,7 @@ void signature_withSemanticIdentifier() { SessionStatusPoller poller = smartIdClient.getSessionStatusPoller(); // Querying the sessions status - SessionStatus certificateSessionStatus = poller.getSessionStatus(certificateChoiceSessionId); + SessionStatus certificateSessionStatus = poller.fetchFinalSessionStatus(certificateChoiceSessionId); TrustedCACertStore trustedCACertStore = new FileTrustedCAStoreBuilder().build(); CertificateValidator certificateValidator = new CertificateValidatorImpl(trustedCACertStore); CertificateChoiceResponseValidator certificateChoiceResponseValidator = new CertificateChoiceResponseValidator(certificateValidator); @@ -720,7 +720,7 @@ void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgori SessionStatusPoller poller = smartIdClient.getSessionStatusPoller(); // Querying the sessions status - SessionStatus certificateSessionStatus = poller.getSessionStatus(certificateChoiceSessionId); + SessionStatus certificateSessionStatus = poller.fetchFinalSessionStatus(certificateChoiceSessionId); TrustedCACertStore trustedCACertStore = new FileTrustedCAStoreBuilder().build(); CertificateValidator certificateValidator = new CertificateValidatorImpl(trustedCACertStore); From 9c60a842ceb19afc0fd0daac7ad96645bedd50e6 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Wed, 4 Mar 2026 16:26:26 +0200 Subject: [PATCH 10/36] Update version to 3.2-SNAPSHOT as next version should be 3.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e13153b..a18b77ee 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ ee.sk.smartid smart-id-java-client jar - 3.0-SNAPSHOT + 3.2-SNAPSHOT Smart-ID Java client Smart-ID Java client is a Java library that can be used for easy integration of the Smart-ID solution to information systems or e-services From 28da3fa741b51249214675b23a6c7a6af1e34192 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Wed, 4 Mar 2026 17:13:03 +0200 Subject: [PATCH 11/36] Run tests by GitHub Actions when PR target branch is legacy-signature-algorithms --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f0598fd2..716a69db 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -4,7 +4,7 @@ on: push: branches: [ "master", "v3.1" ] pull_request: - branches: [ "master" ] + branches: [ "master", "legacy-signature-algorithms" ] permissions: contents: read From 4ddf917335c975e8db54e53870d8816c8dee6646 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Thu, 5 Mar 2026 09:43:34 +0200 Subject: [PATCH 12/36] Polish unit tests --- ...NotificationAuthenticationSessionRequestBuilderTest.java | 2 -- .../java/ee/sk/smartid/SignatureValueValidatorImplTest.java | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java index abbddca1..5bd75642 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java @@ -29,7 +29,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -59,7 +58,6 @@ import ee.sk.smartid.common.notification.interactions.NotificationInteraction; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; import ee.sk.smartid.exception.permanent.SmartIdClientException; -import ee.sk.smartid.exception.permanent.SmartIdRequestSetupException; import ee.sk.smartid.rest.SmartIdConnector; import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionRequest; import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionResponse; diff --git a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java index 3e0f86ed..374ff01e 100644 --- a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java @@ -101,18 +101,18 @@ void validateSignatureValue_constructedPayloadDoesNotMatchTheSignature_throwExce @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_legacyRsa_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) throws CertificateException { + void validate_legacyRsa_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) { var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( "invalidSignature".getBytes(StandardCharsets.UTF_8), PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), algorithm.getAlgorithmName())); - assertTrue(ex.getMessage().contains("Signature value validation failed") || ex.getMessage().contains("does not match")); + assertEquals("Signature value validation failed", ex.getMessage()); } @Test - void validate_legacyRsa_nonLegacyAlgorithmName_throwException() throws CertificateException { + void validate_legacyRsa_nonLegacyAlgorithmName_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( SIGNATURE_VALUE, From ad6e07ade3984ba647dbf16cbe2fcaa62d2deb75 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Fri, 6 Mar 2026 13:47:02 +0200 Subject: [PATCH 13/36] Remove redundant part from comment --- .../smartid/rest/dao/RawDigestSignatureProtocolParameters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java b/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java index c780ebb2..d6f7305b 100644 --- a/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java +++ b/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java @@ -35,7 +35,7 @@ * * @param digest Required. The digest to be signed, Base64 encoded. * @param signatureAlgorithm Required. The signature algorithm (e.g. rsassa-pss, sha512WithRSAEncryption). - * @param signatureAlgorithmParameters Required for RSASSA-PSS. Omitted for RSASSA-PKCS#1 v1.5 algorithms; must be null in that case so it is not serialized. + * @param signatureAlgorithmParameters Required for RSASSA-PSS. Omitted for RSASSA-PKCS#1 v1.5 algorithms. */ public record RawDigestSignatureProtocolParameters(String digest, String signatureAlgorithm, From fc62316562c30f42566a3d2a6154fb0d61c0d7a0 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Fri, 6 Mar 2026 15:14:20 +0200 Subject: [PATCH 14/36] Add legacy algorithm tests into SmartIdRestIntegrationTest --- .../SmartIdRestIntegrationTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java index 5acb8a91..91839732 100644 --- a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java @@ -37,6 +37,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import ee.sk.smartid.DigestCalculator; import ee.sk.smartid.HashAlgorithm; @@ -310,6 +312,48 @@ void initNotificationCertificateChoice_withDocumentNumber() { assertEquals(VerificationCodeType.NUMERIC4.getValue(), sessionResponse.vc().type()); } + @ParameterizedTest + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void initNotificationSignature_withPkcs15Algorithm_andSemanticIdentifier(SigningSignatureAlgorithm signatureAlgorithm) { + var request = toSignatureSessionRequestWithPkcs15(signatureAlgorithm); + + NotificationSignatureSessionResponse sessionResponse = smartIdConnector.initNotificationSignature(request, SEMANTICS_IDENTIFIER); + + assertTrue(UUID_PATTERN.matcher(sessionResponse.sessionID()).matches()); + assertTrue(VERIFICATION_CODE_PATTERN.matcher(sessionResponse.vc().value()).matches()); + assertEquals(VerificationCodeType.NUMERIC4.getValue(), sessionResponse.vc().type()); + } + + @ParameterizedTest + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void initNotificationSignature_withPkcs15Algorithm_andDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) { + var request = toSignatureSessionRequestWithPkcs15(signatureAlgorithm); + + NotificationSignatureSessionResponse sessionResponse = smartIdConnector.initNotificationSignature(request, DOCUMENT_NUMBER); + + assertTrue(UUID_PATTERN.matcher(sessionResponse.sessionID()).matches()); + assertTrue(VERIFICATION_CODE_PATTERN.matcher(sessionResponse.vc().value()).matches()); + assertEquals(VerificationCodeType.NUMERIC4.getValue(), sessionResponse.vc().type()); + } + + private static NotificationSignatureSessionRequest toSignatureSessionRequestWithPkcs15(SigningSignatureAlgorithm signatureAlgorithm) { + byte[] digest = DigestCalculator.calculateDigest("test".getBytes(), signatureAlgorithm.getHashAlgorithmForLegacy()); + var signatureProtocolParameters = new RawDigestSignatureProtocolParameters( + Base64.toBase64String(digest), + signatureAlgorithm.getAlgorithmName(), + null); + return new NotificationSignatureSessionRequest(RELYING_PARTY_UUID, + RELYING_PARTY_NAME, + "QUALIFIED", + SignatureProtocol.RAW_DIGEST_SIGNATURE.name(), + signatureProtocolParameters, + null, + null, + InteractionUtil.encodeToBase64(List.of(new Interaction(DeviceLinkInteractionType.DISPLAY_TEXT_AND_PIN.getCode(), "Sign it!", null))), + null + ); + } + private static NotificationSignatureSessionRequest toSignatureSessionRequest() { var signatureProtocolParameters = new RawDigestSignatureProtocolParameters( Base64.toBase64String(DigestCalculator.calculateDigest("test".getBytes(), HashAlgorithm.SHA_512)), From 3257b1876e5734b489a46aae762947c97710f437 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Fri, 6 Mar 2026 17:20:34 +0200 Subject: [PATCH 15/36] Add signature algorithm tests to signature session request builders --- ...inkSignatureSessionRequestBuilderTest.java | 23 ++++++++++++++- ...ionSignatureSessionRequestBuilderTest.java | 28 ++++++++++++++++++- ...ionSignatureSessionRequestBuilderTest.java | 24 +++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java index b9484d89..5c01d134 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -277,6 +277,27 @@ void initSignatureSession_withDefaultAlgorithmWhenNoSignatureAlgorithmSet() { assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); } + @ParameterizedTest + @EnumSource(SigningSignatureAlgorithm.class) + void initSignatureSession_withSignatureAlgorithm_ok(SigningSignatureAlgorithm signatureAlgorithm) { + when(connector.initDeviceLinkSignature(any(DeviceLinkSignatureSessionRequest.class), any(SemanticsIdentifier.class))).thenReturn(mockSignatureSessionResponse()); + + toDeviceLinkSignatureSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)) + .initSignatureSession(); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(DeviceLinkSignatureSessionRequest.class); + verify(connector).initDeviceLinkSignature(requestCaptor.capture(), any(SemanticsIdentifier.class)); + DeviceLinkSignatureSessionRequest capturedRequest = requestCaptor.getValue(); + + assertEquals(signatureAlgorithm.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + if (signatureAlgorithm.isLegacyRsa()) { + assertNull(capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters()); + } else { + assertNotNull(capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters()); + assertEquals(HashAlgorithm.SHA_512.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); + } + } + @Test void getSignatureSessionRequest_ok() { when(connector.initDeviceLinkSignature(any(DeviceLinkSignatureSessionRequest.class), any(SemanticsIdentifier.class))).thenReturn(mockSignatureSessionResponse()); diff --git a/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java index 2e817836..2743e5cf 100644 --- a/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,8 @@ */ import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -126,6 +128,30 @@ void initSignatureSession_withCapabilities_ok(String[] capabilities, Set assertEquals(expectedRequestCapabilities, request.capabilities()); } + + @ParameterizedTest + @EnumSource(SigningSignatureAlgorithm.class) + void initSignatureSession_withSignatureAlgorithm_ok(SigningSignatureAlgorithm signatureAlgorithm) { + LinkedNotificationSignatureSessionRequestBuilder builder = toLinkedNotificationSignatureSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)); + when(connector.initLinkedNotificationSignature(any(LinkedSignatureSessionRequest.class), eq(DOCUMENT_NUMBER))) + .thenReturn(new LinkedSignatureSessionResponse("20000000-0000-0000-0000-000000000000")); + + LinkedSignatureSessionResponse response = builder.initSignatureSession(); + + assertEquals("20000000-0000-0000-0000-000000000000", response.sessionID()); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(LinkedSignatureSessionRequest.class); + verify(connector).initLinkedNotificationSignature(requestCaptor.capture(), eq(DOCUMENT_NUMBER)); + LinkedSignatureSessionRequest request = requestCaptor.getValue(); + assertEquals(signatureAlgorithm.getAlgorithmName(), request.signatureProtocolParameters().signatureAlgorithm()); + if (signatureAlgorithm.isLegacyRsa()) { + assertNull(request.signatureProtocolParameters().signatureAlgorithmParameters()); + } else { + assertNotNull(request.signatureProtocolParameters().signatureAlgorithmParameters()); + assertEquals(HashAlgorithm.SHA_512.getAlgorithmName(), request.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); + } + } + @Nested class ValidateRequestParameters { diff --git a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java index c39fc8d5..4feece38 100644 --- a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -249,6 +250,27 @@ void initSignatureSession_withCapabilities_ok(String[] capabilities, Set assertEquals(SignatureProtocol.RAW_DIGEST_SIGNATURE.name(), capturedRequest.signatureProtocol()); } + @ParameterizedTest + @EnumSource(SigningSignatureAlgorithm.class) + void initSignatureSession_withSignatureAlgorithm_ok(SigningSignatureAlgorithm signatureAlgorithm) { + when(connector.initNotificationSignature(any(NotificationSignatureSessionRequest.class), any(SemanticsIdentifier.class))).thenReturn(mockNotificationSignatureSessionResponse()); + + toNotificationSignatureSessionRequestBuilder(b -> b.withSignatureAlgorithm(signatureAlgorithm)) + .initSignatureSession(); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(NotificationSignatureSessionRequest.class); + verify(connector).initNotificationSignature(requestCaptor.capture(), any(SemanticsIdentifier.class)); + NotificationSignatureSessionRequest capturedRequest = requestCaptor.getValue(); + + assertEquals(signatureAlgorithm.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); + if (signatureAlgorithm.isLegacyRsa()) { + assertNull(capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters()); + } else { + assertNotNull(capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters()); + assertEquals(HashAlgorithm.SHA_512.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); + } + } + @Nested class ErrorCases { From b277670a248eb3987c83ec052636f8b2034d0603 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Fri, 6 Mar 2026 17:34:02 +0200 Subject: [PATCH 16/36] Update year in changed files license header --- .../java/ee/sk/smartid/AuthenticationResponseMapperImpl.java | 2 +- .../smartid/DeviceLinkAuthenticationSessionRequestBuilder.java | 2 +- .../ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java | 2 +- .../LinkedNotificationSignatureSessionRequestBuilder.java | 2 +- .../NotificationAuthenticationSessionRequestBuilder.java | 2 +- .../sk/smartid/NotificationSignatureSessionRequestBuilder.java | 2 +- src/main/java/ee/sk/smartid/RsaSsaPssParameters.java | 2 +- src/main/java/ee/sk/smartid/SignatureResponse.java | 2 +- src/main/java/ee/sk/smartid/SignatureResponseValidator.java | 2 +- src/main/java/ee/sk/smartid/SignatureValueValidator.java | 2 +- src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java | 2 +- .../smartid/rest/dao/RawDigestSignatureProtocolParameters.java | 2 +- .../smartid/DeviceLinkAuthenticationResponseValidatorTest.java | 2 +- .../DeviceLinkAuthenticationSessionRequestBuilderTest.java | 2 +- .../NotificationAuthenticationResponseValidatorTest.java | 2 +- .../NotificationAuthenticationSessionRequestBuilderTest.java | 2 +- src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java | 2 +- src/test/java/ee/sk/smartid/SmartIdClientTest.java | 2 +- .../java/ee/sk/smartid/integration/ReadmeIntegrationTest.java | 2 +- .../ee/sk/smartid/integration/SmartIdRestIntegrationTest.java | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java index b4ee0740..ae52b0ce 100644 --- a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java +++ b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java index 35afaa84..6f27c6e4 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java index 4df3adbb..7048be2f 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java index ee1afc04..df5584a4 100644 --- a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java index c8832d82..9eb7c022 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2024 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java index 4cc1f62a..6c95591d 100644 --- a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java b/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java index feaaa8ba..60fb5e81 100644 --- a/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java +++ b/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/SignatureResponse.java b/src/main/java/ee/sk/smartid/SignatureResponse.java index 62770df9..58e0cf2f 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponse.java +++ b/src/main/java/ee/sk/smartid/SignatureResponse.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java index 4464c4d3..ed5da888 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidator.java b/src/main/java/ee/sk/smartid/SignatureValueValidator.java index c83006a8..1a2cab0f 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidator.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java index e9eefaa1..e780519b 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java b/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java index d6f7305b..57fb78d1 100644 --- a/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java +++ b/src/main/java/ee/sk/smartid/rest/dao/RawDigestSignatureProtocolParameters.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java index 2c60534c..6fdacf1f 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java index 4eae47bd..25bead73 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java index 1b066692..a7bfc600 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java index 5bd75642..8c869bb0 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java index f12c95b8..d8e5494a 100644 --- a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/SmartIdClientTest.java b/src/test/java/ee/sk/smartid/SmartIdClientTest.java index 4c8f6a88..f0109bba 100644 --- a/src/test/java/ee/sk/smartid/SmartIdClientTest.java +++ b/src/test/java/ee/sk/smartid/SmartIdClientTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 2d48ee4c..9b6180e5 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java index 91839732..7e96d5b4 100644 --- a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From 107c83a7be35815611462dfb5a518d18727b3559 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Fri, 6 Mar 2026 18:02:09 +0200 Subject: [PATCH 17/36] Handle SignatureValueValidatorImplTest --- .../smartid/SignatureValueValidatorImpl.java | 32 +++---- .../SignatureValueValidatorImplTest.java | 84 ++++++++++++++----- 2 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java index e780519b..555e173e 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java @@ -53,9 +53,12 @@ public void validate(byte[] signatureValue, byte[] payload, X509Certificate certificate, RsaSsaPssParameters rsaSsaPssParameters) { - validateInputs(signatureValue, payload, certificate, rsaSsaPssParameters); + validateCommonInput(signatureValue, payload, certificate); + if (rsaSsaPssParameters == null) { + throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not provided"); + } try { - Signature result = getSignature(rsaSsaPssParameters); + Signature result = getRsaSsaPssSignature(rsaSsaPssParameters); result.initVerify(certificate.getPublicKey()); result.update(payload); if (!result.verify(signatureValue)) { @@ -71,20 +74,13 @@ public void validate(byte[] signatureValue, byte[] payload, X509Certificate certificate, String signatureAlgorithmName) { - if (signatureValue == null) { - throw new SmartIdClientException("Parameter 'signatureValue' is not provided"); - } - if (payload == null) { - throw new SmartIdClientException("Parameter 'payload' is not provided"); - } - if (certificate == null) { - throw new SmartIdClientException("Parameter 'certificate' is not provided"); - } + validateCommonInput(signatureValue, payload, certificate); if (signatureAlgorithmName == null || signatureAlgorithmName.isBlank()) { throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); } if (!SigningSignatureAlgorithm.isLegacyRsa(signatureAlgorithmName)) { - throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signatureAlgorithmName + "' is not a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm; use validate(..., RsaSsaPssParameters) for RSASSA-PSS"); + throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signatureAlgorithmName + + "' is not a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm; use validate(..., RsaSsaPssParameters) for RSASSA-PSS"); } try { SigningSignatureAlgorithm algorithm = SigningSignatureAlgorithm.fromString(signatureAlgorithmName); @@ -99,7 +95,7 @@ public void validate(byte[] signatureValue, } } - private Signature getSignature(RsaSsaPssParameters rsaSsaPssParameters) { + private Signature getRsaSsaPssSignature(RsaSsaPssParameters rsaSsaPssParameters) { try { var params = new PSSParameterSpec(rsaSsaPssParameters.getDigestHashAlgorithm().getAlgorithmName(), rsaSsaPssParameters.getMaskGenAlgorithm().getMgfName(), @@ -117,10 +113,9 @@ private Signature getSignature(RsaSsaPssParameters rsaSsaPssParameters) { } } - private static void validateInputs(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - RsaSsaPssParameters rsaSsaPssParameters) { + private static void validateCommonInput(byte[] signatureValue, + byte[] payload, + X509Certificate certificate) { if (signatureValue == null) { throw new SmartIdClientException("Parameter 'signatureValue' is not provided"); } @@ -130,8 +125,5 @@ private static void validateInputs(byte[] signatureValue, if (certificate == null) { throw new SmartIdClientException("Parameter 'certificate' is not provided"); } - if (rsaSsaPssParameters == null) { - throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not provided"); - } } } diff --git a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java index 374ff01e..58c1e431 100644 --- a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,6 +35,7 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Base64; +import java.util.Map; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; @@ -51,10 +52,17 @@ class SignatureValueValidatorImplTest { - // TODO - 22.08.25: replace these values when test accounts are available - private static final String CERT = "MIIHSjCCBtCgAwIBAgIQBQHi3vqqZg+tDaGzQeB2GzAKBggqhkjOPQQDAzBxMSwwKgYDVQQDDCNURVNUIG9mIFNLIElEIFNvbHV0aW9ucyBFSUQtUSAyMDI0RTEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxGzAZBgNVBAoMElNLIElEIFNvbHV0aW9ucyBBUzELMAkGA1UEBhMCRUUwHhcNMjUwNzI5MDgxMTAzWhcNMjgwNzI4MDgxMTAyWjBfMQswCQYDVQQGEwJFRTEUMBIGA1UEAwwLTVVTRVIsVVJNQVMxDjAMBgNVBAQMBU1VU0VSMQ4wDAYDVQQqDAVVUk1BUzEaMBgGA1UEBRMRUE5PRUUtMzkwMDMwMTI3OTgwggMiMA0GCSqGSIb3DQEBAQUAA4IDDwAwggMKAoIDAQCf8qQkO51SM/Gdw63LObpk4kwutMSqW345PU4HC+HqQ2H03fTludjY7iBCgEWmXQjoTt6vQgDGPfBlydjZiu2GUSCL/f2DTv76BuWzR/Jw6q4+R86GRhlMJFqfqE2gqCIddVbUx+qYZ37qCddqgIoRYejdrUeWopp2xzya5gt41FM9By95e3pS/1tug7aAlPoT3Tg18+13qqru1SDGxYW+0NVojesYX3Pzz8Exz2dWcFWwMqoU3SMlAULHDC9OPMtuZBSZA2tvyuD+CHHsU13LI46iDRU2j9BVr9EBuO/uvL3U5eIkX0gpy5bdo/TWmXDijTb5udXO9cz+GMaCQTx4yuBTnC31pHw/qrEp00FRZy7yiG0expv7w4c0YiziMFK8GfhnPmNAVEyjTWImmckK9SiIZH0F/oU1VZvtX3aXsmoTzEwpzAy3KPiKxJ0ZSSsVHV+G1nZvx/1mRxKcT+rOzNcx7iY9uAzin9ajPLYTukWsGVOTgQ2GxpYrEhuf8PvQlZ62BVIvfS5swhlwXzMU8oEAsHCpUVDNCLtckkKBgoy9pYZyKbXUtUP1TTEL3ZC9/4h3Udmao6JNWp5niyHDWVpF6r56O/ORZGx1GlT1P+G9rK6bBteptvNWillGPMA5E1fdwSci7/eH8amSED0CAy0rlq+0CdMdnpasqyG5oDmYJncWhhEozQ2fI7SkvNgSiMxDnJXhi8/Zvh4j+29eC7fqG5ZsLxQ1YqaK8XsIsNJ2Lxj0BhrEgU7Zz5lILUdOILEfU1S2Wi4Ow1P23dAP/O+o6u4SDSKSM2+C5s9daq/5zJ2w2s/B8JB8Mat5MPJuzKrvSnYMIUzQjtlsuMBRIRbHmHtCjDXufF11BOCLfPUYU5GDvk6MY51+p/hZrAowQHWZYI+271UxJR9I1dCTNvo1HsiNEnLSgdOikWvmykqiDVWPe6SiRpVKBQ7MkhgvF/CrHGG0S4GBuG6E2OHEMKl73CWuqU8MrPSOQvaXY7f99ZGK9RL1OG8oxRJpJNECAwEAAaOCAo8wggKLMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUsCQXGYjjZvjNKFhle00U2JJmT2swcAYIKwYBBQUHAQEEZDBiMDMGCCsGAQUFBzAChidodHRwOi8vYy5zay5lZS9URVNUX0VJRC1RXzIwMjRFLmRlci5jcnQwKwYIKwYBBQUHMAGGH2h0dHA6Ly9haWEuZGVtby5zay5lZS9laWRxMjAyNGUwMAYDVR0RBCkwJ6QlMCMxITAfBgNVBAMMGFBOT0VFLTM5MDAzMDEyNzk4LUZGTDgtUTB5BgNVHSAEcjBwMGMGCSsGAQQBzh8RAjBWMFQGCCsGAQUFBwIBFkhodHRwczovL3d3dy5za2lkc29sdXRpb25zLmV1L3Jlc291cmNlcy9jZXJ0aWZpY2F0aW9uLXByYWN0aWNlLXN0YXRlbWVudC8wCQYHBACL7EABAjAoBgNVHQkEITAfMB0GCCsGAQUFBwkBMREYDzE5OTAwMzAxMTIwMDAwWjCBrgYIKwYBBQUHAQMEgaEwgZ4wFQYIKwYBBQUHCwIwCQYHBACL7EkBATAIBgYEAI5GAQEwCAYGBACORgEEMBMGBgQAjkYBBjAJBgcEAI5GAQYBMFwGBgQAjkYBBTBSMFAWSmh0dHBzOi8vd3d3LnNraWRzb2x1dGlvbnMuZXUvcmVzb3VyY2VzL2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJlbjA0BgNVHR8ELTArMCmgJ6AlhiNodHRwOi8vYy5zay5lZS90ZXN0X2VpZC1xXzIwMjRlLmNybDAdBgNVHQ4EFgQUq5xLZIjeh1p1kreds8ie7OgpfmwwDgYDVR0PAQH/BAQDAgZAMAoGCCqGSM49BAMDA2gAMGUCMQCdrnNqlxbO/N6FELvGd4MHeNjTIpdDSj+6Htu6W7KRFleQGe8zhK9yA2l/zSerZvwCMGgbT0nvtgyoXBhSsUhY3RWTMiee4nKn7aBKqcmrDuHC9I9o67WpttfSE4srvL+qWQ=="; - private static final byte[] PAYLOAD = Base64.getDecoder().decode(("PGRzOlNpZ25lZEluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48L2RzOkNhbm9uaWNhbGl6YXRpb25NZXRob2Q" + "+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGE1MTIiPjwvZHM6U2lnbmF0dXJlTWV0aG9kPjxkczpSZWZlcmVuY2UgSWQ9InItaWQtNzcwMDA4OTNlNWU1YmVjOGMwY2IyOThjNmFkMGY0YTQtMSIgVVJJPSJkdW1teS5wZGYiPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiPjwvZHM6RGlnZXN0TWV0aG9kPjxkczpEaWdlc3RWYWx1ZT5QZmVkTkt1OHFaTUk1NXk1UkdIQmlUV0NZRTFvTXBwQi9VdnNHSVhtcmJRPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PGRzOlJlZmVyZW5jZSBUeXBlPSJodHRwOi8vdXJpLmV0c2kub3JnLzAxOTAzI1NpZ25lZFByb3BlcnRpZXMiIFVSST0iI3hhZGVzLWlkLTc3MDAwODkzZTVlNWJlYzhjMGNiMjk4YzZhZDBmNGE0Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48L2RzOlRyYW5zZm9ybT48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiI+PC9kczpEaWdlc3RNZXRob2Q+PGRzOkRpZ2VzdFZhbHVlPjFZd014blRUYmwwZXB5S0g0OEZ0WXFDb3pNbzAxem03NWpwV1pWNDJJNlk9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+").getBytes(StandardCharsets.UTF_8)); - private static final byte[] SIGNATURE_VALUE = Base64.getDecoder().decode("UEVKOrz3Mr+qAXyOjGEt3Nnb8andzicBcEdbb4T2qVyGUslHdeJfgkXccPqBnjmEbL7xoU7eHVkO02K+XNseVY5UBHnXDlMBj1TyCnfelfiZFpAppgWmHKBXC11yE1OhtQ5/siaokPGBX1nZM2rzGlHYWxXYZrOGHCrm7gQEbClL342N6bEzeVVVPnxnxDEkzpNMFvY8UIL3C55WPPNKLBzFwSfduNcLaBiHc4ghaIiJebQc1h+Kad5OAYeu35v70k4HVePbDDp1Cb7RXfMRyUx7GNFSTZiKrG16XO8krp+d9T10SGRbZNoTzxvXBjtb8SjXM6Zvx0tiNdVnsWBrEylGzjS2DVU2+MDbek9QxlxqUU5E5H/WrelywgGTEzfZekowjofQjkYXaEAvNTK8x8Me1wIJThZwfrOy6H8MyPAdgAwl7fDwsZG6QhRCeG+9VY4CtmcII6YMZccCFCy9X3SJvXga4OcSrPi+Nwh3tfvJ5pkYvLliVKSCDpslTZk7JQYcQsJ1DVefMW6BfA+V3iX35mG/VHPo789BpzlZL6Ebs/dxNSEnyyWTDECFl2k2/B38w9jO4OuFLLg/U0AvM6ZLNlLWUjsKKg1s4U+SGlLc7r3hxaWCCwx4/NP2h8f3MTquxOCt+7WrjvCNOQ33bKcFGjYyCWpfGAfVgfMenp4oa40A1+Or+Px4Sd5yD3ZTnPSMYh2UzFZOiejGAS/koBYhn60P5PKRpEkC0nq+WQJD58soelH1EKifLoBtYNzhNOAuOfGRI5nEsW94TZ8hbC/mIEBmMnhKr9Lq/+glxbqskwOavWIF5xeWTKeSt2ErvgtNxX3hTlGxdNavwPi+/qtLikrNoirE26t1WFyPMaeH6hm0rIW42h5c0IvsXrQ4258uJzpZPe/RLbjdy62wi1S35PmowFUFImlHDKSIj4plEVXkrApZDV+/bL0VR6PNr7bsIZqgamL9OyLm6vTunP+A7Q+zpaZxuun17SC1QsthiGGBk03uf4CpNVVUpsO3".getBytes(StandardCharsets.UTF_8)); + // person PNOEE-40504040001 certificate from Smart-ID mock + private static final String CERT = "MIIHQjCCBsigAwIBAgIQZg+OZ2My84me/diBMmXJsDAKBggqhkjOPQQDAzBxMSwwKgYDVQQDDCNURVNUIG9mIFNLIElEIFNvbHV0aW9ucyBFSUQtUSAyMDI0RTEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxGzAZBgNVBAoMElNLIElEIFNvbHV0aW9ucyBBUzELMAkGA1UEBhMCRUUwHhcNMjYwMTA2MTQyNTAxWhcNMjkwMTA1MTQyNTAwWjBXMQswCQYDVQQGEwJFRTEQMA4GA1UEAwwHVEVTVCxPSzENMAsGA1UEBAwEVEVTVDELMAkGA1UEKgwCT0sxGjAYBgNVBAUTEVBOT0VFLTQwNTA0MDQwMDAxMIIDIjANBgkqhkiG9w0BAQEFAAOCAw8AMIIDCgKCAwEAlkcfflcUuP/wZRqsPlJTzSOffEo6BkOsYZEEfUKT8M6EBTLoF6DsG6kbBYzAytlp7HeY6gO7PU6BuvzsotvTRf9bqKZQUVxyQssTOKE3eraS/HWpVyC0PVstieNds3jUI/b1MHSd4jWt02yl7nxOVDOGD7er0Eq/o0uBsLGJCvD7dytiyKUUbyPuPxa3FyogHm9F6+8N2lCGPn5ST1cBCE2QQZh+Wjjaf1OJPc3JhxPPPnJD3yMdAUC+FWE7i/fk/8k62EcJ4zWrPUGw4frCUf20tPsQ1Dd0telOWk9CwC1b9oXfSTDVjQlTBfjo11qaSBbqk/GK9UoHCkiq7PUaXw3vkUrPOt9tq/wDNlhi+YTumN6bjxc6HTmW/aM2gzxvswS0yX7IenKD8DPEgcpJyWWYUzE64FychCfdniZ+QZeiD+g1gdWbDn3p9fQ8S8eVu5TApIZQolWZal4q15ZgNWxb6f1c05AAmquH5K4+7I4IDhlYLBfTEGVoSDM5gdYWORiFvGQb0Vv9awy0C8m18wN0z4l0qQsKCj/pR9WjoBCvAr2KGfY/S2p63tMpVFPOWFwOOmiWQQkpZ0TRCaZSuOYBiFhKUnbSG83hfqeN9B9WNXou5HfJ+e/GAmYOxxXXTFTWtBHc85yT5rnmq6Pjcz4aFnU7ZS7CaZtnCPkP4uctXicjJBNp+IDdB3hp4/Pqpaj+UeqbZadkcgEN7bWBogq3GblxZq6dBecX+XHWQx/3hBBIpmOnXZZHxlZCnhznGw6DBcFJ05gYf4p6nGyyFED7HPhd43cbl2OZuP26u/np8EdnkJ0l4lbL2wtG+Xf9ySkKM1Lm7ZLdA510ADsdZJzqzE35adlcNcWyTeK9Aku9XuFPa4N1go46xkBnrK1LwE3XCDOr3ncbm3PCsic11U7AoljKI1EL4jvEa3P5y2UQlVBy+V5nL6QSmo88XeB30wKVcSLZfopjUUmIxrTxdMHAqhC117qQfr2KEJPJnTnJjkuWpiW2gRuBSdVE20oNAgMBAAGjggKPMIICizAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFLAkFxmI42b4zShYZXtNFNiSZk9rMHAGCCsGAQUFBwEBBGQwYjAzBggrBgEFBQcwAoYnaHR0cDovL2Muc2suZWUvVEVTVF9FSUQtUV8yMDI0RS5kZXIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vYWlhLmRlbW8uc2suZWUvZWlkcTIwMjRlMDAGA1UdEQQpMCekJTAjMSEwHwYDVQQDDBhQTk9FRS00MDUwNDA0MDAwMS1ERU0wLVEweQYDVR0gBHIwcDBjBgkrBgEEAc4fEQIwVjBUBggrBgEFBQcCARZIaHR0cHM6Ly93d3cuc2tpZHNvbHV0aW9ucy5ldS9yZXNvdXJjZXMvY2VydGlmaWNhdGlvbi1wcmFjdGljZS1zdGF0ZW1lbnQvMAkGBwQAi+xAAQIwKAYDVR0JBCEwHzAdBggrBgEFBQcJATERGA8xOTA1MDQwNDEyMDAwMFowga4GCCsGAQUFBwEDBIGhMIGeMBUGCCsGAQUFBwsCMAkGBwQAi+xJAQEwCAYGBACORgEBMAgGBgQAjkYBBDATBgYEAI5GAQYwCQYHBACORgEGATBcBgYEAI5GAQUwUjBQFkpodHRwczovL3d3dy5za2lkc29sdXRpb25zLmV1L3Jlc291cmNlcy9jb25kaXRpb25zLWZvci11c2Utb2YtY2VydGlmaWNhdGVzLxMCZW4wNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2Muc2suZWUvdGVzdF9laWQtcV8yMDI0ZS5jcmwwHQYDVR0OBBYEFJGVYDWDS/+f3wOUdFXmjEAIKsSiMA4GA1UdDwEB/wQEAwIGQDAKBggqhkjOPQQDAwNoADBlAjA/30qqYa6kKYDAzWeph4bYJfY2Sve/spq3znrNMdU3mD4VPhHpeLt8q0M/cWHNG0QCMQCoBStc4F10hjXQSpTCva8SqkYWVm6pclSLy5+K40CaYWM4yrZhcM7FgcUM2aOqfEs="; + // original data as string is "dataToSign" + private static final byte[] PAYLOAD = Base64.getDecoder().decode("ZGF0YVRvU2lnbg=="); + // signatures from Smart-ID mock + private static final Map SIGNATURE_VALUE_MAP = Map.of( + SigningSignatureAlgorithm.RSASSA_PSS, Base64.getDecoder().decode("iBgRsurf7Hc0uEUYAnRR3IfQ8JXNvYM/Ft9TvWtQvnatwzLLDDukhpPCRPPP5CtNrPvKVLSLSJ9GRMz1w6lHqrlooWJm74sQaD0o6QAl5UrdaJe5ez9zn/jBPh0p+E5Z1TC9bYFTibyRHGX1W6LFTP+/wDS0eGLURnc82Q0CxQjjqzlpOQXBFqPYLmCFn8ps2AAzzKPIKLimebkuOYzYWGFT25qaiWN8ZHif8RysEYex07eTmsaLl/ZeO5DURZtJaaIFFQURcD7XIgg1iy4jns3Q3T+E5lfdPMwocn3CZgZNyx0gsoiJF0A3LRjN9lb5MS3SCN697i8EFZxsOXg/IJRWjwp2afeRgJunwFK/STkeVVDwdOAgSJ0DY4/BjasFWRuBSdrkTPPjzAFvJa00DRofS1GMaayjcVgw4H7QAvAXoden/QTLUMIy8nJSfVTfAV42dbfBLrlkenL2fWfyR6gr1s8Lkh9beFM+5AYjfwNlsuy1iY6riqVtcFDl8glpE0bxo706W143RPRxfo2VxJkk+hZtcSaunMkFCjFxxzHUOgh2Jmtz7fmWmWtknnTcc6uBhB2w00y4lLrQ80c80TMO8K6uivZU2WQ7PDxvJ0uOJXDXJItd36Aa7wwIV/XPefkDrCBRyUdpKKbgjSO3S0cbRhnvVzSDy8t95wFheXKg8uJ8bbU/f2ZpFpCN+dk+HTgSNffh9dpCV4FgjDhMYE1Oz7Ennf/gLWW5FePg7K9Xd1d9T5DfBb/YGVvxOOoleQr0DTbarCwGaUnyTSatgcSMrtSrQlq0nh1p3RKrJF8qluqNqdDvhFLMEEUHt66Zkg/JEiT/k3+PYMk43E2TsIaqTOrs+AiTvZRuJlwhXKAUSApG6U76UdsWF3Ui7pGBRlwHDbJtq4qsAhUiEqSqfFnsGeIMxvUT9pv7aasStv13G7RzehGtxoOqkae8ajy7prmFgRl0Om8xwcHWXRGlMFTppeagsjG5ilKYkj7RpWo2NOXCwsOx5KXQxu3xcv1Y"), + SigningSignatureAlgorithm.SHA256_WITH_RSA_ENCRYPTION, Base64.getDecoder().decode("cRZanVdkMHJAuBYKpmmwT4TowxqibpwpFR//XJo3Cx71XN55XlMeTrrhjrNs/IYr2y5plRFD9n8OkrvkQIK/dSzAhnnwVirMFmJv/NjxXkb1HisKCS1XMEcNtpW17k5N6PU8yOD6jLZqcMmGCIjO/PQQZfIwKXJCOC1YC0oAmaBDvVMpT+j4jaxoGhdzpjvf5vaueam94K9DmMRkJaoCxo93WGa04dZgYOz62GnFp991bOQv95TasH48fUkVCju5zSuzD2Cf8jaxd3Zc8V8JfsUEmAiumsyTv2wNyjrTZ3TVh3W7oOYXLI7GLb6XQFVQG7TBwnPnD4wf5SvLuRgCUtXVUjYLHt9tQz20AO7tEAxnX3FcmS4r759aiqFjX/xreCmIQ96zTnsF3pMsHgPhnnPCiXHRRWQHPU1vil19LYTciYrjRouuO2IKV9Etx/5Ire3YUXQM81yDfeWfPFEj4s8DRtywYNW9Ditv2MECUJTP07h4DbKpNpJFHfh6etI43YM755949DkFwmN6ZyIvZ32jgclV0ZpPsAvB0VquH8Odc8OpHYOXMBvHXr/Kd2TywTNA88HFgesBhWSbKcKt756cvFbUX/+Eji/DxjJ6DTmS8jl9q+dPvcIAzNcDEcmxTGcwS+qjD6HzoMv0I3+pZ4v3ZzwwDeahfhAqxMr3sqceCqzqS1fG+KTpeXJw+vADnVcCh+oOKPfLf9iQStOheX2c+cBqjEGWuoCVrMY+3+MaQ0NHEjG5UA7M+m9Oj80Jr6ODSSFLi4KM6NZ1yT/vOh4b0klIET0LSVm3HXACvmVc+l7IC2toVNoAsaZ83zQ2O5R2dtwNc/6p8TCOrlfvxnx3MM8R8HDET1csyqnnOAiZi0JToMJkr3F/0N8gFgAb/BJs7onETvCLuv+8SCaTTJD5XdcUno6BrjRho9vcbOnjpKWE6KbsOZu/rYwWkeqeMEzgHryD0MkYLUhqfNHuIKw2zJgP5T0apsKASoJjSLzTd0TFFNkT34MNx7YMEOHF"), + SigningSignatureAlgorithm.SHA384_WITH_RSA_ENCRYPTION, Base64.getDecoder().decode("XHDnLzHGds19lU4zdwWILXGOoFEE9h1OCZ3McSJE0osEWkR7v05FCsLTylSx/UdExxgqdychsBP6vlRyrkx5aZM64xletCgMbd+AQn3+nGxkCFSWzo/szv/8fB0OpJ5o1Ks5BpgvONrfRVhjvVCYxH9d1tjNvJYNMsxVYvpkz4Njox0IefOHwJWj9VoL13S/g5h0MBUNPjKlec3evCbZ4n0eesQySY6dQSWL8WhMdnXp9UIU0pEROEhO2Y+iHnjXa1T7FKcdzh/uhHaAipJnufBJ2Q0xpjlV3GPpYMhvk/qL2Rdh71bJKlPDaKCdZEgOglK/++qHmuxMOfMRimGT57QGVcVMcQXf/iBzoSIkucZu4rwPhQdVJpVSpBydO8rVcz1orSi3WCYHcQIMmdn3MSSK3nIxxpx/DLBChVNoVyGrCKLEDktb5ZCRS71RZZBrIW2reMi9l/jQGdZ5YAdQ1hx29LhANtxFPL+8imbBHqbOCkGHGLf+5d9ibmLWPBfnwdoBuN9pb5UH3gdf6/YWp3XObOoY2919ZwtWqBYqY0rum5a6cGuVia+52y8z+GJql/IE49sLispI9B0gm1tWY1mH7YP+6mzxweNBY+O4L49X23YHdNq9uTgYkkmUFu4CQqFDpuzqMh8KabgnZxop4IwXyZSKlAFFiM7GQgLKI9MRM69e/xjMMBjBjzILgusDdnStRs3VcRWW5zpbeUwqnEiV+stZweDSuLJsy4BfAEeYOzVVSu1O94zIKZJJRl+sW9W0lbfdQd0jjKzOXebTLegud+RzU4Agoh4zLa0mGS4pMiNJfye4Lt8po7JCLsqR4RU46UFnyt/R7eTU8Zk01nN8F1O+qyo2FIyU2drOxybhE2YsOdgBiAox04IlR1XmHFenaytFn+6Gn951gZPah+1OrCWiO+z63jzdAfNMkWk5vhaqr81jYKYAYc8d5/TTEd4kkNtK5aaLoiwh7ZAG5nu4Y7dciGt2YJurINdG9la3Z9cJA8DJAkjyrNK9Uz9p"), + SigningSignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION, Base64.getDecoder().decode("IgbB+cIDO2jTFuzWWowjN2PZlGm1PFLLSDv2K6eEfnSt1DPHDeKl8nQMituwuNOqh6HpG6TZhmHvOxebxx9FCSNyradw4rYNX/tSDEuIc4dBZQGLHJqDbI+1Yz1mHOEZE1GpJb8LbHF34/1z98s05isTQ8kwiFkzipSL1Qm3G/o6O018yEsCD4YxsDL8YUBdgUzF1T+NhMOppDy5rVge+gblGY2WfFV+Q0i3dPuqllqpa7k31QEBzAzeOPzUQqC78J9Rax9AaR1vKrYS5RR9U9hxCCQUROK6Ead/1igYnDfwd8xXe+RMSel46KQkBrH9tWUw5DpQuzDbyuCw/2HvK68/jZwJjAeAPoiRgE2DLuQJkzTGMDE6JREe/nJ+LpOSEJxX+eyKp4Ia1+J317j/cK+CApdbAK1molV1Cb4N264tT+KZmG24ERDfMDfXKzLVJUF+AIfJbQdbxxUZ51D+NQX03gj6yjVoIQ0rAdFbRarqtW1AAPAqNfcx9hvYTfD52fzPk3IHd2WMcronmTLU21RCnu0V072DXsKBOnQyBmL3UE4udVQBZxOsip3u2y/qGN8fVFzL5J0+xHbDIixHWo7b5QCR+ZZSUZmoVA/SMqumRGVMcUTaRwxPOih32+eXOElZRoGJo80Km8EHJPpo1Fvsw13I9NyHKRudIFDVJz+iJTZRHrVaWAxUdf5qJZI1FknQXX3jUmrcCMFdjAlGpVz2LxCPz7rHJh6oBg4+eWdP4TdnHuG6Hzh4QaAKEvHnmWHNHSKNYYTCzkOpRMVgQd2w9ymkVHgu8K0oBwKcMu43Pb9u/lxWRUgwt3qf6wa336vrE70A17xJZa2fWCncnCIl9ilXKH1FxHrWvhK7rH1YgfLgYQHXcO5PGDEemMiPmzKnGJDhP11BaNEhnI9ogWCr/nahwDL7nBpgYP9SMN3EY6pxZzP9II6fGWA9VNtGJg4fif6pPVUutLV5El+cf04rWChBMis+5cSgKyKNAPBHJFHiASDOEaPG4fpgaiCT") + ); private SignatureValueValidator signatureValueValidator; @@ -64,39 +72,61 @@ void setUp() { } @Test - void validate() throws CertificateException { + void validate_rsaSsaPss_ok() throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); RsaSsaPssParameters rsaSsaPssParameters = toRsaSsaPssParameters(); - assertDoesNotThrow(() -> signatureValueValidator.validate(SIGNATURE_VALUE, PAYLOAD, certificate, rsaSsaPssParameters)); + assertDoesNotThrow(() -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), + PAYLOAD, + certificate, + rsaSsaPssParameters)); + } + + @ParameterizedTest + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_legacyRsa_ok(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + + assertDoesNotThrow(() -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(signatureAlgorithm), + PAYLOAD, + certificate, + signatureAlgorithm.getAlgorithmName())); } @ParameterizedTest @ArgumentsSource(EmptyInputArgumentProvider.class) - void validate_InputParametersNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, RsaSsaPssParameters rsaSsaPssParameters) { + void validate_inputParametersNotProvided_forRsaSsaPss_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, RsaSsaPssParameters rsaSsaPssParameters) { assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validate(signatureValue, payload, certificate, rsaSsaPssParameters)); } + @ParameterizedTest + @ArgumentsSource(EmptyInputArgumentProvider.class) + void validate_inputParametersNotProvided_forLegacyRsa_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, String signingSignatureAlgorithm) { + assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validate(signatureValue, payload, certificate, signingSignatureAlgorithm)); + } + @Test - void validateSignatureValue_IsInvalid_throwException() { + void validate_legacyRsa_nonLegacyAlgorithmName_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( - "invalidValue".getBytes(StandardCharsets.UTF_8), + SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), - toRsaSsaPssParameters())); - assertEquals("Signature value validation failed", ex.getMessage()); + SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName())); + assertTrue(ex.getMessage().contains("not a legacy RSA")); } @Test - void validateSignatureValue_constructedPayloadDoesNotMatchTheSignature_throwException() { + void validate_rsaSsaPss_invalidSignature_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( - SIGNATURE_VALUE, - "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), + "invalidValue".getBytes(StandardCharsets.UTF_8), + PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), toRsaSsaPssParameters())); - assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); + assertEquals("Signature value validation failed", ex.getMessage()); } @ParameterizedTest @@ -112,14 +142,26 @@ void validate_legacyRsa_invalidSignature_throwException(SigningSignatureAlgorith } @Test - void validate_legacyRsa_nonLegacyAlgorithmName_throwException() { + void validate_rsaSsaPss_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( - SIGNATURE_VALUE, - PAYLOAD, + SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), + "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), CertificateUtil.toX509CertificateFromEncodedString(CERT), - SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName())); - assertTrue(ex.getMessage().contains("not a legacy RSA")); + toRsaSsaPssParameters())); + assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); + } + + @ParameterizedTest + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_legacyRsa_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException(SigningSignatureAlgorithm signatureAlgorithm) { + var ex = assertThrows(UnprocessableSmartIdResponseException.class, + () -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(signatureAlgorithm), + "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), + CertificateUtil.toX509CertificateFromEncodedString(CERT), + signatureAlgorithm.getAlgorithmName())); + assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); } private static RsaSsaPssParameters toRsaSsaPssParameters() { From bfe8e036683ae9181dd3d9a2381749916a523e9b Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Fri, 6 Mar 2026 21:12:17 +0200 Subject: [PATCH 18/36] Add signature value validation into notification based signing flow tests --- .../integration/ReadmeIntegrationTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 9b6180e5..c6195e04 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -764,6 +764,21 @@ void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgori SignatureResponseValidator validator = new SignatureResponseValidator(certificateValidator); SignatureResponse signatureResponse = validator.validate(signatureSessionStatus, certificateLevel); + SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); + if (signatureAlgorithm.isLegacyRsa()) { + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), + signatureResponse.getCertificate(), + signatureAlgorithm.getAlgorithmName()); + } else { + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), + signatureResponse.getCertificate(), + signatureResponse.getRsaSsaPssParameters()); + } + assertEquals("OK", signatureResponse.getEndResult()); assertEquals("PNOEE-40504040001-DEM0-Q", signatureResponse.getDocumentNumber()); assertEquals(CertificateLevel.QUALIFIED, signatureResponse.getCertificateLevel()); @@ -830,6 +845,21 @@ void signature_withDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) SignatureResponseValidator validator = new SignatureResponseValidator(certificateValidator); SignatureResponse signatureResponse = validator.validate(signatureSessionStatus, certificateLevel); + SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); + if (signatureAlgorithm.isLegacyRsa()) { + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), + signatureResponse.getCertificate(), + signatureAlgorithm.getAlgorithmName()); + } else { + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), + signatureResponse.getCertificate(), + signatureResponse.getRsaSsaPssParameters()); + } + assertEquals("OK", signatureResponse.getEndResult()); assertEquals(documentNumber, signatureResponse.getDocumentNumber()); assertEquals(CertificateLevel.QUALIFIED, signatureResponse.getCertificateLevel()); From e0503e1f77fdb8e098908e2ad8af55ffbf168008 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Fri, 6 Mar 2026 21:44:25 +0200 Subject: [PATCH 19/36] Add v3.2 changelog --- CHANGELOG.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31e9571b..7c76a64a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [3.1-?] - TBD +## [3.2] - TBD + +### Added + +- Legacy signing algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) for RSASSA-PKCS#1 v1.5. + - Compatible with DigiDoc4j library which does not support RSASSA-PSS. + - Use `SigningSignatureAlgorithm` enum and `withSignatureAlgorithm()` on signature session builders. + - Use `SigningSignatureAlgorithm.getHashAlgorithmForLegacy()` when creating `SignableData` for legacy algorithms. + - Legacy algorithms do not use `signatureAlgorithmParameters` in requests or responses. +- `AuthenticationSignatureAlgorithm` and `SigningSignatureAlgorithm` enums for type-safe signature algorithm selection. + - Default `AuthenticationSignatureAlgorithm` is `RSASSA_PSS`; default `SigningSignatureAlgorithm` is `RSASSA_PSS`. +- `withSignatureAlgorithm()` and `withHashAlgorithm()` on authentication and signature session builders. +- `SignatureValueValidator.validate(..., String signatureAlgorithmName)` overload for validating legacy RSA signatures. + +### Changed + +- Split `SignatureAlgorithm` into `AuthenticationSignatureAlgorithm` (authentication) and `SigningSignatureAlgorithm` (signing). + +## [3.1] - 2025-10-15 ### Structural changes From 7e284242bf55a457822f9a64fc722dc15717bbe9 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 9 Mar 2026 09:48:45 +0200 Subject: [PATCH 20/36] Small simplification --- .../smartid/NotificationSignatureSessionRequestBuilderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java index 4feece38..86f63ecb 100644 --- a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java @@ -229,7 +229,7 @@ void initSignatureSession_overrideDefaultHashAlgorithmForSignableData_ok(HashAlg NotificationSignatureSessionRequest capturedRequest = requestCaptor.getValue(); assertEquals(SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithm()); - assertEquals(Base64.getEncoder().encodeToString(signableData.calculateHash()), capturedRequest.signatureProtocolParameters().digest()); + assertEquals(signableData.getDigestInBase64(), capturedRequest.signatureProtocolParameters().digest()); assertEquals(hashAlgorithm.getAlgorithmName(), capturedRequest.signatureProtocolParameters().signatureAlgorithmParameters().hashAlgorithm()); assertEquals(SignatureProtocol.RAW_DIGEST_SIGNATURE.name(), capturedRequest.signatureProtocol()); } From 73b3755d45512cf18676fbcfd1e89bdb32d6a2ae Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 9 Mar 2026 11:44:55 +0200 Subject: [PATCH 21/36] Refactor SignatureValueValidator, instead of overloaded validate methods use validateRsaSsaPss, validateLegacyRsa and unified 5-parameter validate --- CHANGELOG.md | 16 ++-- ...ceLinkAuthenticationResponseValidator.java | 2 +- ...cationAuthenticationResponseValidator.java | 2 +- .../sk/smartid/SignatureValueValidator.java | 58 ++++++++---- .../smartid/SignatureValueValidatorImpl.java | 45 ++++++++-- .../SignatureValueValidatorImplTest.java | 89 +++++++++++++++---- .../integration/ReadmeIntegrationTest.java | 51 +++++------ 7 files changed, 179 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c76a64a..e198d9db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,21 +5,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [3.2] - TBD -### Added +### Changes -- Legacy signing algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) for RSASSA-PKCS#1 v1.5. +- Added legacy signing algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) for RSASSA-PKCS#1 v1.5. - Compatible with DigiDoc4j library which does not support RSASSA-PSS. - Use `SigningSignatureAlgorithm` enum and `withSignatureAlgorithm()` on signature session builders. - Use `SigningSignatureAlgorithm.getHashAlgorithmForLegacy()` when creating `SignableData` for legacy algorithms. - Legacy algorithms do not use `signatureAlgorithmParameters` in requests or responses. -- `AuthenticationSignatureAlgorithm` and `SigningSignatureAlgorithm` enums for type-safe signature algorithm selection. - - Default `AuthenticationSignatureAlgorithm` is `RSASSA_PSS`; default `SigningSignatureAlgorithm` is `RSASSA_PSS`. -- `withSignatureAlgorithm()` and `withHashAlgorithm()` on authentication and signature session builders. -- `SignatureValueValidator.validate(..., String signatureAlgorithmName)` overload for validating legacy RSA signatures. - -### Changed - - Split `SignatureAlgorithm` into `AuthenticationSignatureAlgorithm` (authentication) and `SigningSignatureAlgorithm` (signing). + - Only allowed `AuthenticationSignatureAlgorithm` is `RSASSA_PSS`; default `SigningSignatureAlgorithm` is `RSASSA_PSS`. +- Refactored `SignatureValueValidator`: + - Introduced `validateRsaSsaPss(...)` and `validateLegacyRsa(...)` helper methods. + - Added unified 5-parameter `validate(signatureValue, payload, certificate, signatureAlgorithmName, rsaSsaPssParameters)`. + - Enforced that `rsaSsaPssParameters` **must** be present for `RSASSA_PSS` and **must be null** for legacy RSA algorithms. ## [3.1] - 2025-10-15 diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java index 55496621..d674e3a0 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java @@ -156,7 +156,7 @@ private void validateSignature(AuthenticationResponse authenticationResponse, String schemaName, String brokeredRpName) { byte[] payload = constructPayload(authenticationResponse, authenticationSessionRequest, schemaName, brokeredRpName); - signatureValueValidator.validate(authenticationResponse.getSignatureValue(), + signatureValueValidator.validateRsaSsaPss(authenticationResponse.getSignatureValue(), payload, authenticationResponse.getCertificate(), authenticationResponse.getRsaSsaPssSignatureParameters()); diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java index 0efb494e..338de4a6 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java @@ -149,7 +149,7 @@ private void validateSignature(AuthenticationResponse authenticationResponse, String schemaName, String brokeredRpName) { byte[] payload = constructPayload(authenticationResponse, authenticationSessionRequest, schemaName, brokeredRpName); - signatureValueValidator.validate(authenticationResponse.getSignatureValue(), + signatureValueValidator.validateRsaSsaPss(authenticationResponse.getSignatureValue(), payload, authenticationResponse.getCertificate(), authenticationResponse.getRsaSsaPssSignatureParameters()); diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidator.java b/src/main/java/ee/sk/smartid/SignatureValueValidator.java index 1a2cab0f..eade17e0 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidator.java @@ -36,31 +36,53 @@ public interface SignatureValueValidator { /** - * Validates the signature value (RSASSA-PSS) against the calculated signature value. + * Validates the signature value for RSASSA-PSS (authentication and signing). * - * @param signatureValue the signature value to validate - * @param payload the original data that was signed - * @param certificate X509 certificate used for signature validation - * @param rsaSsaPssParameters signature parameters used for creating signature value - * @throws UnprocessableSmartIdResponseException when there are any issue with validating the signature value + * @param signatureValue the signature value to validate + * @param payload the original data that was signed (typically the hash that was sent to Smart-ID) + * @param certificate X.509 certificate used for signature validation + * @param rsaSsaPssParameters signature parameters used for creating the signature value + * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value */ - void validate(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - RsaSsaPssParameters rsaSsaPssParameters); + void validateRsaSsaPss(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + RsaSsaPssParameters rsaSsaPssParameters); /** - * Validates the signature value (RSASSA-PKCS#1 v1.5) against the digest/payload. - * Use this when the signature session used a legacy RSA algorithm (e.g. sha256WithRSAEncryption). + * Validates the signature value for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). + * Use this when the signature session used a legacy RSA algorithm (e.g. {@code sha256WithRSAEncryption}). * - * @param signatureValue the signature value to validate - * @param payload the digest or data that was signed (typically the hash that was sent to Smart-ID) - * @param certificate X509 certificate used for signature validation - * @param signatureAlgorithmName Smart-ID API algorithm name (e.g. sha512WithRSAEncryption) - * @throws UnprocessableSmartIdResponseException when there are any issue with validating the signature value + * @param signatureValue the signature value to validate + * @param payload the digest or data that was signed (typically the hash that was sent to Smart-ID) + * @param certificate X.509 certificate used for signature validation + * @param signatureAlgorithmName Smart-ID API algorithm name (e.g. {@code sha512WithRSAEncryption}) + * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value + */ + void validateLegacyRsa(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + String signatureAlgorithmName); + + /** + * Validates the signature value for both RSASSA-PSS and legacy RSASSA-PKCS#1 v1.5 algorithms. + *
    + *
  • For RSASSA-PSS ({@link SigningSignatureAlgorithm#RSASSA_PSS}) the {@code rsaSsaPssParameters} + * parameter must be provided.
  • + *
  • For legacy RSA algorithms (e.g. {@code sha256WithRSAEncryption}) the {@code rsaSsaPssParameters} + * parameter must be {@code null}.
  • + *
+ * + * @param signatureValue the signature value to validate + * @param payload the data used when calculating the signature (hash or raw bytes, depending on the algorithm) + * @param certificate X.509 certificate used for signature validation + * @param signatureAlgorithmName Smart-ID API algorithm name + * @param rsaSsaPssParameters RSASSA-PSS parameters; required only for RSASSA-PSS and must be {@code null} for legacy RSA + * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value */ void validate(byte[] signatureValue, byte[] payload, X509Certificate certificate, - String signatureAlgorithmName); + String signatureAlgorithmName, + RsaSsaPssParameters rsaSsaPssParameters); } diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java index 555e173e..5d5bfcff 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java @@ -49,10 +49,10 @@ public final class SignatureValueValidatorImpl implements SignatureValueValidato private final Logger logger = LoggerFactory.getLogger(SignatureValueValidatorImpl.class); @Override - public void validate(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - RsaSsaPssParameters rsaSsaPssParameters) { + public void validateRsaSsaPss(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + RsaSsaPssParameters rsaSsaPssParameters) { validateCommonInput(signatureValue, payload, certificate); if (rsaSsaPssParameters == null) { throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not provided"); @@ -70,10 +70,10 @@ public void validate(byte[] signatureValue, } @Override - public void validate(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - String signatureAlgorithmName) { + public void validateLegacyRsa(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + String signatureAlgorithmName) { validateCommonInput(signatureValue, payload, certificate); if (signatureAlgorithmName == null || signatureAlgorithmName.isBlank()) { throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); @@ -95,6 +95,35 @@ public void validate(byte[] signatureValue, } } + @Override + public void validate(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + String signatureAlgorithmName, + RsaSsaPssParameters rsaSsaPssParameters) { + if (signatureAlgorithmName == null || signatureAlgorithmName.isBlank()) { + throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); + } + SigningSignatureAlgorithm algorithm; + try { + algorithm = SigningSignatureAlgorithm.fromString(signatureAlgorithmName); + } catch (IllegalArgumentException ex) { + throw new UnprocessableSmartIdResponseException("Unsupported signature algorithm: " + signatureAlgorithmName, ex); + } + + if (algorithm.isLegacyRsa()) { + if (rsaSsaPssParameters != null) { + throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not allowed for legacy RSA algorithms"); + } + validateLegacyRsa(signatureValue, payload, certificate, signatureAlgorithmName); + } else { + if (rsaSsaPssParameters == null) { + throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' must be provided for RSASSA-PSS algorithm"); + } + validateRsaSsaPss(signatureValue, payload, certificate, rsaSsaPssParameters); + } + } + private Signature getRsaSsaPssSignature(RsaSsaPssParameters rsaSsaPssParameters) { try { var params = new PSSParameterSpec(rsaSsaPssParameters.getDigestHashAlgorithm().getAlgorithmName(), diff --git a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java index 58c1e431..900d34c8 100644 --- a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java @@ -72,11 +72,11 @@ void setUp() { } @Test - void validate_rsaSsaPss_ok() throws CertificateException { + void validateRsaSsaPss_ok() throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); RsaSsaPssParameters rsaSsaPssParameters = toRsaSsaPssParameters(); - assertDoesNotThrow(() -> signatureValueValidator.validate( + assertDoesNotThrow(() -> signatureValueValidator.validateRsaSsaPss( SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), PAYLOAD, certificate, @@ -85,10 +85,10 @@ void validate_rsaSsaPss_ok() throws CertificateException { @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_legacyRsa_ok(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { + void validateLegacyRsa_ok(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - assertDoesNotThrow(() -> signatureValueValidator.validate( + assertDoesNotThrow(() -> signatureValueValidator.validateLegacyRsa( SIGNATURE_VALUE_MAP.get(signatureAlgorithm), PAYLOAD, certificate, @@ -97,20 +97,20 @@ void validate_legacyRsa_ok(SigningSignatureAlgorithm signatureAlgorithm) throws @ParameterizedTest @ArgumentsSource(EmptyInputArgumentProvider.class) - void validate_inputParametersNotProvided_forRsaSsaPss_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, RsaSsaPssParameters rsaSsaPssParameters) { - assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validate(signatureValue, payload, certificate, rsaSsaPssParameters)); + void validateRsaSsaPss_inputParametersNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, RsaSsaPssParameters rsaSsaPssParameters) { + assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validateRsaSsaPss(signatureValue, payload, certificate, rsaSsaPssParameters)); } @ParameterizedTest @ArgumentsSource(EmptyInputArgumentProvider.class) - void validate_inputParametersNotProvided_forLegacyRsa_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, String signingSignatureAlgorithm) { - assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validate(signatureValue, payload, certificate, signingSignatureAlgorithm)); + void validateLegacyRsa_inputParametersNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, String signingSignatureAlgorithm) { + assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validateLegacyRsa(signatureValue, payload, certificate, signingSignatureAlgorithm)); } @Test - void validate_legacyRsa_nonLegacyAlgorithmName_throwException() { + void validateLegacyRsa_nonLegacyAlgorithmName_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validate( + () -> signatureValueValidator.validateLegacyRsa( SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), @@ -119,9 +119,9 @@ void validate_legacyRsa_nonLegacyAlgorithmName_throwException() { } @Test - void validate_rsaSsaPss_invalidSignature_throwException() { + void validateRsaSsaPss_invalidSignature_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validate( + () -> signatureValueValidator.validateRsaSsaPss( "invalidValue".getBytes(StandardCharsets.UTF_8), PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), @@ -131,9 +131,9 @@ void validate_rsaSsaPss_invalidSignature_throwException() { @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_legacyRsa_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) { + void validateLegacyRsa_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validate( + () -> signatureValueValidator.validateLegacyRsa( "invalidSignature".getBytes(StandardCharsets.UTF_8), PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), @@ -142,9 +142,9 @@ void validate_legacyRsa_invalidSignature_throwException(SigningSignatureAlgorith } @Test - void validate_rsaSsaPss_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException() { + void validateRsaSsaPss_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validate( + () -> signatureValueValidator.validateRsaSsaPss( SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), CertificateUtil.toX509CertificateFromEncodedString(CERT), @@ -154,9 +154,9 @@ void validate_rsaSsaPss_signatureValue_constructedPayloadDoesNotMatchTheSignatur @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_legacyRsa_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException(SigningSignatureAlgorithm signatureAlgorithm) { + void validateLegacyRsa_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException(SigningSignatureAlgorithm signatureAlgorithm) { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validate( + () -> signatureValueValidator.validateLegacyRsa( SIGNATURE_VALUE_MAP.get(signatureAlgorithm), "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), CertificateUtil.toX509CertificateFromEncodedString(CERT), @@ -164,6 +164,59 @@ void validate_legacyRsa_signatureValue_constructedPayloadDoesNotMatchTheSignatur assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); } + @Test + void validate_dispatchesToRsaSsaPss_whenAlgorithmIsRsassaPss() throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + RsaSsaPssParameters rsaSsaPssParameters = toRsaSsaPssParameters(); + + assertDoesNotThrow(() -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), + PAYLOAD, + certificate, + SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + rsaSsaPssParameters)); + } + + @ParameterizedTest + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_dispatchesToLegacyRsa_whenAlgorithmIsLegacy(SigningSignatureAlgorithm algorithm) throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + + assertDoesNotThrow(() -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(algorithm), + PAYLOAD, + certificate, + algorithm.getAlgorithmName(), + null)); + } + + @ParameterizedTest + @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) + void validate_legacyRsa_withRsaSsaPssParametersProvided_throwException(SigningSignatureAlgorithm algorithm) throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + + assertThrows(SmartIdClientException.class, + () -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(algorithm), + PAYLOAD, + certificate, + algorithm.getAlgorithmName(), + toRsaSsaPssParameters())); + } + + @Test + void validate_rsassaPss_withoutRsaSsaPssParameters_throwException() throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + + assertThrows(SmartIdClientException.class, + () -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), + PAYLOAD, + certificate, + SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + null)); + } + private static RsaSsaPssParameters toRsaSsaPssParameters() { RsaSsaPssParameters rsaSsaPssParameters = new RsaSsaPssParameters(); rsaSsaPssParameters.setDigestHashAlgorithm(HashAlgorithm.SHA_512); diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index c6195e04..e67b783a 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -435,7 +435,12 @@ void signature_withDocumentNumberAndQRCode() { SignatureResponse signatureResponse = signatureResponseValidator.validate(signatureSessionStatus, CertificateLevel.QUALIFIED); // Validate signature value SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); - signatureValueValidator.validate(signatureResponse.getSignatureValue(), signableData.calculateHash(), certResponse.certificate(), signatureResponse.getRsaSsaPssParameters()); + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), + certResponse.certificate(), + signatureResponse.getSignatureAlgorithm().getAlgorithmName(), + signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); assertEquals("PNOLT-40504040001-MOCK-Q", signatureResponse.getDocumentNumber()); @@ -537,9 +542,11 @@ void signature_withSemanticIdentifier() { SignatureResponse signatureResponse = signatureResponseValidator.validate(signatureSessionStatus, CertificateLevel.QUALIFIED); // Validate signature value SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); - signatureValueValidator.validate(signatureResponse.getSignatureValue(), - signableData.calculateHash(), + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), certificateChoiceResponse.getCertificate(), + signatureResponse.getSignatureAlgorithm().getAlgorithmName(), signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); @@ -765,19 +772,12 @@ void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgori SignatureResponse signatureResponse = validator.validate(signatureSessionStatus, certificateLevel); SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); - if (signatureAlgorithm.isLegacyRsa()) { - signatureValueValidator.validate( - signatureResponse.getSignatureValue(), - signableData.dataToSign(), - signatureResponse.getCertificate(), - signatureAlgorithm.getAlgorithmName()); - } else { - signatureValueValidator.validate( - signatureResponse.getSignatureValue(), - signableData.dataToSign(), - signatureResponse.getCertificate(), - signatureResponse.getRsaSsaPssParameters()); - } + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), + signatureResponse.getCertificate(), + signatureAlgorithm.getAlgorithmName(), + signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); assertEquals("PNOEE-40504040001-DEM0-Q", signatureResponse.getDocumentNumber()); @@ -846,19 +846,12 @@ void signature_withDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) SignatureResponse signatureResponse = validator.validate(signatureSessionStatus, certificateLevel); SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); - if (signatureAlgorithm.isLegacyRsa()) { - signatureValueValidator.validate( - signatureResponse.getSignatureValue(), - signableData.dataToSign(), - signatureResponse.getCertificate(), - signatureAlgorithm.getAlgorithmName()); - } else { - signatureValueValidator.validate( - signatureResponse.getSignatureValue(), - signableData.dataToSign(), - signatureResponse.getCertificate(), - signatureResponse.getRsaSsaPssParameters()); - } + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), + signatureResponse.getCertificate(), + signatureAlgorithm.getAlgorithmName(), + signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); assertEquals(documentNumber, signatureResponse.getDocumentNumber()); From 6ff80a05c1a343f5201868bbda5ed3975f272e1d Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 9 Mar 2026 12:04:26 +0200 Subject: [PATCH 22/36] Update year in license headers --- .../sk/smartid/DeviceLinkAuthenticationResponseValidator.java | 2 +- .../sk/smartid/NotificationAuthenticationResponseValidator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java index d674e3a0..96bbd716 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java index 338de4a6..6648ed01 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From cf52dfd95ed454f5e7532ebe229f732f578d8168 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 9 Mar 2026 13:37:15 +0200 Subject: [PATCH 23/36] Migration guide from v3.1 to v3.2 --- MIGRATION_GUIDE.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 67bf42f2..29d31f22 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -4,6 +4,33 @@ Library v3.1 supports only Smart-ID v3 API. All the previous v2 related code has been removed and all the code necessary for Smart-ID API v3 is under package smartid. Some classes could also be used in v3 and for those classes the package did not change. +# Migrating from library v3.1 to v3.2 + +For signing flows are restored legacy RSASSA-PKCS#1 v1.5 algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) which are compatible with DigiDoc4j's signing support. +For that reason: +* `SignatureAlgorithm` class is split into `AuthenticationSignatureAlgorithm` and `SigningSignatureAlgorithm`. +* `SignatureValueValidator` has now 3 methods `validate`, `validateRsaSsaPss` and `validateLegacyRsa` for different use cases instead previous 1 method `validate` + +Changes needed in authentication flows: +* change `SignatureAlgorithm` to `AuthenticationSignatureAlgorithm` +* change used `SignatureValueValidator` method from `validate` to `validateRsaSsaPss` (only name changed, parameters unchanged) + +Changes needed in signing flows: +* change `SignatureAlgorithm` to `SigningSignatureAlgorithm` +* suggestion for `SignatureValueValidator` usage: + * when using only signature algorithm RSASSA_PSS then use `SignatureValueValidator.validateRsaSsaPss` + * when using only legacy signature algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) then use `SignatureValueValidator.validateLegacyRsa` + * when both RSASSA_PSS and legacy RSA algorithms are used then use `SignatureValueValidator.validate` + +When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: +``` +return switch (signatureAlgorithm) { + case SHA256_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA256; + case SHA384_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA384; + case SHA512_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA512; +} +``` + # Migrating from Smart-ID v2 to Smart-ID v3 API ## Migrating authentication From 193f8e8b4f57ae6ebc58e8f82012fab9832e8bf0 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 9 Mar 2026 15:07:00 +0200 Subject: [PATCH 24/36] Move signature related core classes into signature package --- CHANGELOG.md | 13 +++++++++++++ MIGRATION_GUIDE.md | 16 +++++++++++++++- .../ee/sk/smartid/AuthenticationResponse.java | 3 ++- .../AuthenticationResponseMapperImpl.java | 5 +++++ ...eviceLinkAuthenticationResponseValidator.java | 4 ++++ ...eLinkAuthenticationSessionRequestBuilder.java | 2 ++ ...DeviceLinkSignatureSessionRequestBuilder.java | 4 ++++ ...tificationSignatureSessionRequestBuilder.java | 4 ++++ ...ificationAuthenticationResponseValidator.java | 2 ++ ...ationAuthenticationSessionRequestBuilder.java | 2 ++ ...tificationSignatureSessionRequestBuilder.java | 4 ++++ .../java/ee/sk/smartid/SignatureResponse.java | 2 ++ .../sk/smartid/SignatureResponseValidator.java | 5 +++++ .../sk/smartid/VerificationCodeCalculator.java | 4 +++- .../AuthenticationSignatureAlgorithm.java | 2 +- .../{ => signature}/DigestCalculator.java | 4 ++-- .../sk/smartid/{ => signature}/DigestInput.java | 4 ++-- .../smartid/{ => signature}/HashAlgorithm.java | 4 ++-- .../{ => signature}/MaskGenAlgorithm.java | 4 ++-- .../{ => signature}/RsaSsaPssParameters.java | 2 +- .../sk/smartid/{ => signature}/SignableData.java | 4 ++-- .../sk/smartid/{ => signature}/SignableHash.java | 4 ++-- .../{ => signature}/SignatureValueValidator.java | 2 +- .../SignatureValueValidatorImpl.java | 2 +- .../SigningSignatureAlgorithm.java | 2 +- .../sk/smartid/{ => signature}/TrailerField.java | 4 ++-- .../java/ee/sk/smartid/util/CallbackUrlUtil.java | 6 +++--- .../java/ee/sk/smartid/util/InteractionUtil.java | 6 +++--- .../AuthenticationResponseMapperImplTest.java | 3 ++- ...eLinkAuthenticationResponseValidatorTest.java | 4 ++++ ...kAuthenticationSessionRequestBuilderTest.java | 2 ++ ...ceLinkSignatureSessionRequestBuilderTest.java | 5 +++++ ...cationSignatureSessionRequestBuilderTest.java | 5 +++++ ...ationAuthenticationResponseValidatorTest.java | 4 ++++ ...nAuthenticationSessionRequestBuilderTest.java | 2 ++ ...cationSignatureSessionRequestBuilderTest.java | 5 +++++ .../smartid/SignatureResponseValidatorTest.java | 1 + .../java/ee/sk/smartid/SmartIdClientTest.java | 5 +++++ .../smartid/VerificationCodeCalculatorTest.java | 4 +++- .../integration/ReadmeIntegrationTest.java | 10 +++++----- .../integration/SmartIdRestIntegrationTest.java | 8 ++++---- .../smartid/rest/SmartIdRestConnectorTest.java | 2 +- .../{ => signature}/DigestCalculatorTest.java | 4 ++-- .../{ => signature}/SignableDataTest.java | 4 ++-- .../{ => signature}/SignableHashTest.java | 4 ++-- .../SignatureValueValidatorImplTest.java | 3 ++- 46 files changed, 148 insertions(+), 47 deletions(-) rename src/main/java/ee/sk/smartid/{ => signature}/AuthenticationSignatureAlgorithm.java (98%) rename src/main/java/ee/sk/smartid/{ => signature}/DigestCalculator.java (96%) rename src/main/java/ee/sk/smartid/{ => signature}/DigestInput.java (96%) rename src/main/java/ee/sk/smartid/{ => signature}/HashAlgorithm.java (97%) rename src/main/java/ee/sk/smartid/{ => signature}/MaskGenAlgorithm.java (97%) rename src/main/java/ee/sk/smartid/{ => signature}/RsaSsaPssParameters.java (99%) rename src/main/java/ee/sk/smartid/{ => signature}/SignableData.java (97%) rename src/main/java/ee/sk/smartid/{ => signature}/SignableHash.java (97%) rename src/main/java/ee/sk/smartid/{ => signature}/SignatureValueValidator.java (99%) rename src/main/java/ee/sk/smartid/{ => signature}/SignatureValueValidatorImpl.java (99%) rename src/main/java/ee/sk/smartid/{ => signature}/SigningSignatureAlgorithm.java (99%) rename src/main/java/ee/sk/smartid/{ => signature}/TrailerField.java (97%) rename src/test/java/ee/sk/smartid/{ => signature}/DigestCalculatorTest.java (97%) rename src/test/java/ee/sk/smartid/{ => signature}/SignableDataTest.java (97%) rename src/test/java/ee/sk/smartid/{ => signature}/SignableHashTest.java (96%) rename src/test/java/ee/sk/smartid/{ => signature}/SignatureValueValidatorImplTest.java (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e198d9db..6afbfc18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Introduced `validateRsaSsaPss(...)` and `validateLegacyRsa(...)` helper methods. - Added unified 5-parameter `validate(signatureValue, payload, certificate, signatureAlgorithmName, rsaSsaPssParameters)`. - Enforced that `rsaSsaPssParameters` **must** be present for `RSASSA_PSS` and **must be null** for legacy RSA algorithms. +- The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature`: + - `AuthenticationSignatureAlgorithm` + - `DigestCalculator` + - `DigestInput` + - `HashAlgorithm` + - `MaskGenAlgorithm` + - `RsaSsaPssParameters` + - `SignableData` + - `SignableHash` + - `SignatureValueValidator` + - `SignatureValueValidatorImpl` + - `SigningSignatureAlgorithm` + - `TrailerField` ## [3.1] - 2025-10-15 diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 29d31f22..a696ff2a 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -22,7 +22,7 @@ Changes needed in signing flows: * when using only legacy signature algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) then use `SignatureValueValidator.validateLegacyRsa` * when both RSASSA_PSS and legacy RSA algorithms are used then use `SignatureValueValidator.validate` -When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: +When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.signature.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: ``` return switch (signatureAlgorithm) { case SHA256_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA256; @@ -31,6 +31,20 @@ return switch (signatureAlgorithm) { } ``` +The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature` so when used then imports need to be adjusted: +* `AuthenticationSignatureAlgorithm` +* `DigestCalculator` +* `DigestInput` +* `HashAlgorithm` +* `MaskGenAlgorithm` +* `RsaSsaPssParameters` +* `SignableData` +* `SignableHash` +* `SignatureValueValidator` +* `SignatureValueValidatorImpl` +* `SigningSignatureAlgorithm` +* `TrailerField` + # Migrating from Smart-ID v2 to Smart-ID v3 API ## Migrating authentication diff --git a/src/main/java/ee/sk/smartid/AuthenticationResponse.java b/src/main/java/ee/sk/smartid/AuthenticationResponse.java index 25bdabba..103f7dde 100644 --- a/src/main/java/ee/sk/smartid/AuthenticationResponse.java +++ b/src/main/java/ee/sk/smartid/AuthenticationResponse.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,6 +31,7 @@ import java.util.Base64; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; +import ee.sk.smartid.signature.RsaSsaPssParameters; /** * The authentication response after a successful authentication session status response was received. diff --git a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java index ae52b0ce..81da82f0 100644 --- a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java +++ b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java @@ -39,6 +39,11 @@ import ee.sk.smartid.rest.dao.SessionResult; import ee.sk.smartid.rest.dao.SessionSignature; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.MaskGenAlgorithm; +import ee.sk.smartid.signature.RsaSsaPssParameters; +import ee.sk.smartid.signature.TrailerField; import ee.sk.smartid.util.StringUtil; /** diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java index 96bbd716..f2c71044 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java @@ -37,6 +37,10 @@ import ee.sk.smartid.exception.useraccount.CertificateLevelMismatchException; import ee.sk.smartid.rest.dao.DeviceLinkAuthenticationSessionRequest; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.SignatureValueValidator; +import ee.sk.smartid.signature.SignatureValueValidatorImpl; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java index 6f27c6e4..d84a6690 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java @@ -42,6 +42,8 @@ import ee.sk.smartid.rest.dao.RequestProperties; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.SetUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java index 7048be2f..b6a99ea7 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilder.java @@ -41,6 +41,10 @@ import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.DeviceLinkSignatureSessionRequest; +import ee.sk.smartid.signature.DigestInput; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignableHash; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.SetUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java index df5584a4..c260e306 100644 --- a/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilder.java @@ -39,6 +39,10 @@ import ee.sk.smartid.rest.dao.RawDigestSignatureProtocolParameters; import ee.sk.smartid.rest.dao.RequestProperties; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; +import ee.sk.smartid.signature.DigestInput; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignableHash; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; import ee.sk.smartid.util.SetUtil; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java index 6648ed01..cd4b429c 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java @@ -36,6 +36,8 @@ import ee.sk.smartid.exception.useraccount.CertificateLevelMismatchException; import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionRequest; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.SignatureValueValidator; +import ee.sk.smartid.signature.SignatureValueValidatorImpl; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java index 9eb7c022..f24befcc 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java @@ -42,6 +42,8 @@ import ee.sk.smartid.rest.dao.RequestProperties; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.SetUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java index 6c95591d..35d85831 100644 --- a/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilder.java @@ -30,6 +30,10 @@ import java.util.Set; import java.util.regex.Pattern; +import ee.sk.smartid.signature.DigestInput; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignableHash; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/ee/sk/smartid/SignatureResponse.java b/src/main/java/ee/sk/smartid/SignatureResponse.java index 58e0cf2f..f125e974 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponse.java +++ b/src/main/java/ee/sk/smartid/SignatureResponse.java @@ -31,6 +31,8 @@ import java.util.Base64; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; +import ee.sk.smartid.signature.RsaSsaPssParameters; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; /** * Response of a completed and validated signature session. diff --git a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java index ed5da888..6416cdcb 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java @@ -49,6 +49,11 @@ import ee.sk.smartid.rest.dao.SessionSignature; import ee.sk.smartid.rest.dao.SessionSignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.MaskGenAlgorithm; +import ee.sk.smartid.signature.RsaSsaPssParameters; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; +import ee.sk.smartid.signature.TrailerField; import ee.sk.smartid.util.StringUtil; /** diff --git a/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java b/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java index 661658f5..36961eab 100644 --- a/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java +++ b/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,8 @@ import java.nio.ByteBuffer; import ee.sk.smartid.exception.permanent.SmartIdClientException; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; /** * Utility class for calculating verification code from a hash. diff --git a/src/main/java/ee/sk/smartid/AuthenticationSignatureAlgorithm.java b/src/main/java/ee/sk/smartid/signature/AuthenticationSignatureAlgorithm.java similarity index 98% rename from src/main/java/ee/sk/smartid/AuthenticationSignatureAlgorithm.java rename to src/main/java/ee/sk/smartid/signature/AuthenticationSignatureAlgorithm.java index 633febd7..390e7700 100644 --- a/src/main/java/ee/sk/smartid/AuthenticationSignatureAlgorithm.java +++ b/src/main/java/ee/sk/smartid/signature/AuthenticationSignatureAlgorithm.java @@ -1,4 +1,4 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L diff --git a/src/main/java/ee/sk/smartid/DigestCalculator.java b/src/main/java/ee/sk/smartid/signature/DigestCalculator.java similarity index 96% rename from src/main/java/ee/sk/smartid/DigestCalculator.java rename to src/main/java/ee/sk/smartid/signature/DigestCalculator.java index a8ace55e..a7e08efa 100644 --- a/src/main/java/ee/sk/smartid/DigestCalculator.java +++ b/src/main/java/ee/sk/smartid/signature/DigestCalculator.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/DigestInput.java b/src/main/java/ee/sk/smartid/signature/DigestInput.java similarity index 96% rename from src/main/java/ee/sk/smartid/DigestInput.java rename to src/main/java/ee/sk/smartid/signature/DigestInput.java index bf74c745..5b2b67ea 100644 --- a/src/main/java/ee/sk/smartid/DigestInput.java +++ b/src/main/java/ee/sk/smartid/signature/DigestInput.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/HashAlgorithm.java b/src/main/java/ee/sk/smartid/signature/HashAlgorithm.java similarity index 97% rename from src/main/java/ee/sk/smartid/HashAlgorithm.java rename to src/main/java/ee/sk/smartid/signature/HashAlgorithm.java index f7e74a9d..8fbf68a6 100644 --- a/src/main/java/ee/sk/smartid/HashAlgorithm.java +++ b/src/main/java/ee/sk/smartid/signature/HashAlgorithm.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/MaskGenAlgorithm.java b/src/main/java/ee/sk/smartid/signature/MaskGenAlgorithm.java similarity index 97% rename from src/main/java/ee/sk/smartid/MaskGenAlgorithm.java rename to src/main/java/ee/sk/smartid/signature/MaskGenAlgorithm.java index dad59496..bf24b457 100644 --- a/src/main/java/ee/sk/smartid/MaskGenAlgorithm.java +++ b/src/main/java/ee/sk/smartid/signature/MaskGenAlgorithm.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java b/src/main/java/ee/sk/smartid/signature/RsaSsaPssParameters.java similarity index 99% rename from src/main/java/ee/sk/smartid/RsaSsaPssParameters.java rename to src/main/java/ee/sk/smartid/signature/RsaSsaPssParameters.java index 60fb5e81..2a118f81 100644 --- a/src/main/java/ee/sk/smartid/RsaSsaPssParameters.java +++ b/src/main/java/ee/sk/smartid/signature/RsaSsaPssParameters.java @@ -1,4 +1,4 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L diff --git a/src/main/java/ee/sk/smartid/SignableData.java b/src/main/java/ee/sk/smartid/signature/SignableData.java similarity index 97% rename from src/main/java/ee/sk/smartid/SignableData.java rename to src/main/java/ee/sk/smartid/signature/SignableData.java index 29a7495a..903a876c 100644 --- a/src/main/java/ee/sk/smartid/SignableData.java +++ b/src/main/java/ee/sk/smartid/signature/SignableData.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/SignableHash.java b/src/main/java/ee/sk/smartid/signature/SignableHash.java similarity index 97% rename from src/main/java/ee/sk/smartid/SignableHash.java rename to src/main/java/ee/sk/smartid/signature/SignableHash.java index 9d18117a..4ce78bf7 100644 --- a/src/main/java/ee/sk/smartid/SignableHash.java +++ b/src/main/java/ee/sk/smartid/signature/SignableHash.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidator.java b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java similarity index 99% rename from src/main/java/ee/sk/smartid/SignatureValueValidator.java rename to src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java index eade17e0..b61471eb 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidator.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java @@ -1,4 +1,4 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L diff --git a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java similarity index 99% rename from src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java rename to src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java index 5d5bfcff..8bbe3a1c 100644 --- a/src/main/java/ee/sk/smartid/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java @@ -1,4 +1,4 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L diff --git a/src/main/java/ee/sk/smartid/SigningSignatureAlgorithm.java b/src/main/java/ee/sk/smartid/signature/SigningSignatureAlgorithm.java similarity index 99% rename from src/main/java/ee/sk/smartid/SigningSignatureAlgorithm.java rename to src/main/java/ee/sk/smartid/signature/SigningSignatureAlgorithm.java index f3e66dbf..80a15902 100644 --- a/src/main/java/ee/sk/smartid/SigningSignatureAlgorithm.java +++ b/src/main/java/ee/sk/smartid/signature/SigningSignatureAlgorithm.java @@ -1,4 +1,4 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L diff --git a/src/main/java/ee/sk/smartid/TrailerField.java b/src/main/java/ee/sk/smartid/signature/TrailerField.java similarity index 97% rename from src/main/java/ee/sk/smartid/TrailerField.java rename to src/main/java/ee/sk/smartid/signature/TrailerField.java index b8482bb8..55b6f22b 100644 --- a/src/main/java/ee/sk/smartid/TrailerField.java +++ b/src/main/java/ee/sk/smartid/signature/TrailerField.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java b/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java index b12e1fc2..150f00fa 100644 --- a/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java +++ b/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,12 +28,12 @@ import java.util.Base64; -import ee.sk.smartid.DigestCalculator; -import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.common.devicelink.CallbackUrl; import ee.sk.smartid.common.devicelink.UrlSafeTokenGenerator; import ee.sk.smartid.exception.SessionSecretMismatchException; import ee.sk.smartid.exception.permanent.SmartIdClientException; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; import jakarta.ws.rs.core.UriBuilder; /** diff --git a/src/main/java/ee/sk/smartid/util/InteractionUtil.java b/src/main/java/ee/sk/smartid/util/InteractionUtil.java index 2ba91cbd..c30ad1bc 100644 --- a/src/main/java/ee/sk/smartid/util/InteractionUtil.java +++ b/src/main/java/ee/sk/smartid/util/InteractionUtil.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,11 +33,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import ee.sk.smartid.DigestCalculator; -import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.common.SmartIdInteraction; import ee.sk.smartid.exception.permanent.SmartIdClientException; import ee.sk.smartid.rest.dao.Interaction; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; /** * Utility for interactions related actions diff --git a/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java b/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java index a671136a..82b81846 100644 --- a/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java +++ b/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -49,6 +49,7 @@ import ee.sk.smartid.rest.dao.SessionSignature; import ee.sk.smartid.rest.dao.SessionSignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.HashAlgorithm; class AuthenticationResponseMapperImplTest { diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java index 6fdacf1f..c729215d 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java @@ -57,6 +57,10 @@ import ee.sk.smartid.rest.dao.SessionSignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.MaskGenAlgorithm; +import ee.sk.smartid.signature.TrailerField; import ee.sk.smartid.util.InteractionUtil; class DeviceLinkAuthenticationResponseValidatorTest { diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java index 25bead73..1da96d78 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java @@ -70,6 +70,8 @@ import ee.sk.smartid.rest.dao.DeviceLinkSessionResponse; import ee.sk.smartid.rest.dao.Interaction; import ee.sk.smartid.rest.dao.SemanticsIdentifier; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; class DeviceLinkAuthenticationSessionRequestBuilderTest { diff --git a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java index 5c01d134..fe35e58b 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java @@ -66,6 +66,11 @@ import ee.sk.smartid.rest.dao.DeviceLinkSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.DeviceLinkSignatureSessionRequest; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignableHash; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; class DeviceLinkSignatureSessionRequestBuilderTest { diff --git a/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java index 2743e5cf..7d45689a 100644 --- a/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java @@ -56,6 +56,11 @@ import ee.sk.smartid.rest.SmartIdConnector; import ee.sk.smartid.rest.dao.LinkedSignatureSessionRequest; import ee.sk.smartid.rest.dao.LinkedSignatureSessionResponse; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignableHash; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; class LinkedNotificationSignatureSessionRequestBuilderTest { diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java index a7bfc600..31e94605 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java @@ -51,6 +51,10 @@ import ee.sk.smartid.rest.dao.SessionSignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.MaskGenAlgorithm; +import ee.sk.smartid.signature.TrailerField; class NotificationAuthenticationResponseValidatorTest { diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java index 8c869bb0..cbabf221 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java @@ -62,6 +62,8 @@ import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionRequest; import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; class NotificationAuthenticationSessionRequestBuilderTest { diff --git a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java index 86f63ecb..19064f1e 100644 --- a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java @@ -66,6 +66,11 @@ import ee.sk.smartid.rest.dao.NotificationSignatureSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.VerificationCode; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignableHash; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; class NotificationSignatureSessionRequestBuilderTest { diff --git a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java index d8e5494a..3adc92e3 100644 --- a/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/SignatureResponseValidatorTest.java @@ -51,6 +51,7 @@ import ee.sk.smartid.rest.dao.SessionSignature; import ee.sk.smartid.rest.dao.SessionSignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; class SignatureResponseValidatorTest { diff --git a/src/test/java/ee/sk/smartid/SmartIdClientTest.java b/src/test/java/ee/sk/smartid/SmartIdClientTest.java index f0109bba..98abdf7f 100644 --- a/src/test/java/ee/sk/smartid/SmartIdClientTest.java +++ b/src/test/java/ee/sk/smartid/SmartIdClientTest.java @@ -58,6 +58,11 @@ import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.VerificationCode; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignableHash; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; class SmartIdClientTest { diff --git a/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java b/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java index 847e2fa7..b9f65245 100644 --- a/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java +++ b/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -42,6 +42,8 @@ import org.junit.jupiter.params.provider.NullAndEmptySource; import ee.sk.smartid.exception.permanent.SmartIdClientException; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; public class VerificationCodeCalculatorTest { diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index e67b783a..00dc4391 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -66,22 +66,17 @@ import ee.sk.smartid.DeviceLinkSignatureSessionRequestBuilder; import ee.sk.smartid.DeviceLinkType; import ee.sk.smartid.FileTrustedCAStoreBuilder; -import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.NotificationAuthenticationResponseValidator; import ee.sk.smartid.NotificationAuthenticationSessionRequestBuilder; import ee.sk.smartid.QrCodeGenerator; import ee.sk.smartid.RpChallenge; import ee.sk.smartid.RpChallengeGenerator; import ee.sk.smartid.SessionType; -import ee.sk.smartid.SignableData; -import ee.sk.smartid.SigningSignatureAlgorithm; import ee.sk.smartid.SignatureCertificatePurposeValidator; import ee.sk.smartid.SignatureCertificatePurposeValidatorFactory; import ee.sk.smartid.SignatureCertificatePurposeValidatorFactoryImpl; import ee.sk.smartid.SignatureResponse; import ee.sk.smartid.SignatureResponseValidator; -import ee.sk.smartid.SignatureValueValidator; -import ee.sk.smartid.SignatureValueValidatorImpl; import ee.sk.smartid.SmartIdClient; import ee.sk.smartid.SmartIdDemoIntegrationTest; import ee.sk.smartid.TrustedCACertStore; @@ -100,6 +95,11 @@ import ee.sk.smartid.rest.dao.NotificationSignatureSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; +import ee.sk.smartid.signature.SignatureValueValidator; +import ee.sk.smartid.signature.SignatureValueValidatorImpl; import ee.sk.smartid.util.CallbackUrlUtil; @SmartIdDemoIntegrationTest diff --git a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java index 7e96d5b4..e4f99959 100644 --- a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java @@ -40,11 +40,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import ee.sk.smartid.DigestCalculator; -import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.RpChallengeGenerator; -import ee.sk.smartid.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.SigningSignatureAlgorithm; import ee.sk.smartid.SignatureProtocol; import ee.sk.smartid.SmartIdDemoIntegrationTest; import ee.sk.smartid.VerificationCodeType; @@ -67,6 +63,10 @@ import ee.sk.smartid.rest.dao.RequestProperties; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; +import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; +import ee.sk.smartid.signature.DigestCalculator; +import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.SigningSignatureAlgorithm; import ee.sk.smartid.util.InteractionUtil; @SmartIdDemoIntegrationTest diff --git a/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java b/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java index 0082e0ee..2e1e1a7b 100644 --- a/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java +++ b/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java @@ -57,7 +57,6 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import ee.sk.smartid.CertificateLevel; -import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.SignatureProtocol; import ee.sk.smartid.SmartIdRestServiceStubs; import ee.sk.smartid.common.devicelink.interactions.DeviceLinkInteractionType; @@ -94,6 +93,7 @@ import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.VerificationCode; +import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.util.InteractionUtil; class SmartIdRestConnectorTest { diff --git a/src/test/java/ee/sk/smartid/DigestCalculatorTest.java b/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java similarity index 97% rename from src/test/java/ee/sk/smartid/DigestCalculatorTest.java rename to src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java index aa6ad3f0..e4f23fed 100644 --- a/src/test/java/ee/sk/smartid/DigestCalculatorTest.java +++ b/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/SignableDataTest.java b/src/test/java/ee/sk/smartid/signature/SignableDataTest.java similarity index 97% rename from src/test/java/ee/sk/smartid/SignableDataTest.java rename to src/test/java/ee/sk/smartid/signature/SignableDataTest.java index 82d57a2d..a41250ac 100644 --- a/src/test/java/ee/sk/smartid/SignableDataTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignableDataTest.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/SignableHashTest.java b/src/test/java/ee/sk/smartid/signature/SignableHashTest.java similarity index 96% rename from src/test/java/ee/sk/smartid/SignableHashTest.java rename to src/test/java/ee/sk/smartid/signature/SignableHashTest.java index 7d6bdf7c..6cff5965 100644 --- a/src/test/java/ee/sk/smartid/SignableHashTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignableHashTest.java @@ -1,10 +1,10 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2025 SK ID Solutions AS + * Copyright (C) 2018 - 2026 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java similarity index 99% rename from src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java rename to src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java index 900d34c8..2e0d7519 100644 --- a/src/test/java/ee/sk/smartid/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java @@ -1,4 +1,4 @@ -package ee.sk.smartid; +package ee.sk.smartid.signature; /*- * #%L @@ -47,6 +47,7 @@ import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.EnumSource; +import ee.sk.smartid.CertificateUtil; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; import ee.sk.smartid.exception.permanent.SmartIdClientException; From 4e4ead0f9046a925740f1f7191902350df109e7c Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 9 Mar 2026 16:24:19 +0200 Subject: [PATCH 25/36] In signature validation methods use strongly typed parameter SigningSignatureAlgorithm instead of String --- .../signature/SignatureValueValidator.java | 22 ++++++++-------- .../SignatureValueValidatorImpl.java | 26 +++++++------------ .../integration/ReadmeIntegrationTest.java | 8 +++--- .../SignatureValueValidatorImplTest.java | 18 ++++++------- 4 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java index b61471eb..98c659a6 100644 --- a/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java @@ -53,16 +53,16 @@ void validateRsaSsaPss(byte[] signatureValue, * Validates the signature value for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). * Use this when the signature session used a legacy RSA algorithm (e.g. {@code sha256WithRSAEncryption}). * - * @param signatureValue the signature value to validate - * @param payload the digest or data that was signed (typically the hash that was sent to Smart-ID) - * @param certificate X.509 certificate used for signature validation - * @param signatureAlgorithmName Smart-ID API algorithm name (e.g. {@code sha512WithRSAEncryption}) + * @param signatureValue the signature value to validate + * @param payload the digest or data that was signed (typically the hash that was sent to Smart-ID) + * @param certificate X.509 certificate used for signature validation + * @param signatureAlgorithm Smart-ID API algorithm name (e.g. {@code SHA512_WITH_RSA_ENCRYPTION}) * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value */ void validateLegacyRsa(byte[] signatureValue, byte[] payload, X509Certificate certificate, - String signatureAlgorithmName); + SigningSignatureAlgorithm signatureAlgorithm); /** * Validates the signature value for both RSASSA-PSS and legacy RSASSA-PKCS#1 v1.5 algorithms. @@ -73,16 +73,16 @@ void validateLegacyRsa(byte[] signatureValue, * parameter must be {@code null}. * * - * @param signatureValue the signature value to validate - * @param payload the data used when calculating the signature (hash or raw bytes, depending on the algorithm) - * @param certificate X.509 certificate used for signature validation - * @param signatureAlgorithmName Smart-ID API algorithm name - * @param rsaSsaPssParameters RSASSA-PSS parameters; required only for RSASSA-PSS and must be {@code null} for legacy RSA + * @param signatureValue the signature value to validate + * @param payload the data used when calculating the signature (hash or raw bytes, depending on the algorithm) + * @param certificate X.509 certificate used for signature validation + * @param signatureAlgorithm Smart-ID API signature algorithm + * @param rsaSsaPssParameters RSASSA-PSS parameters; required only for RSASSA-PSS and must be {@code null} for legacy RSA * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value */ void validate(byte[] signatureValue, byte[] payload, X509Certificate certificate, - String signatureAlgorithmName, + SigningSignatureAlgorithm signatureAlgorithm, RsaSsaPssParameters rsaSsaPssParameters); } diff --git a/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java index 8bbe3a1c..7e29147f 100644 --- a/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java @@ -73,18 +73,17 @@ public void validateRsaSsaPss(byte[] signatureValue, public void validateLegacyRsa(byte[] signatureValue, byte[] payload, X509Certificate certificate, - String signatureAlgorithmName) { + SigningSignatureAlgorithm signatureAlgorithm) { validateCommonInput(signatureValue, payload, certificate); - if (signatureAlgorithmName == null || signatureAlgorithmName.isBlank()) { + if (signatureAlgorithm == null) { throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); } - if (!SigningSignatureAlgorithm.isLegacyRsa(signatureAlgorithmName)) { - throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signatureAlgorithmName + + if (!signatureAlgorithm.isLegacyRsa()) { + throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signatureAlgorithm + "' is not a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm; use validate(..., RsaSsaPssParameters) for RSASSA-PSS"); } try { - SigningSignatureAlgorithm algorithm = SigningSignatureAlgorithm.fromString(signatureAlgorithmName); - Signature signature = Signature.getInstance(algorithm.getJceAlgorithmName()); + Signature signature = Signature.getInstance(signatureAlgorithm.getJceAlgorithmName()); signature.initVerify(certificate.getPublicKey()); signature.update(payload); if (!signature.verify(signatureValue)) { @@ -99,23 +98,16 @@ public void validateLegacyRsa(byte[] signatureValue, public void validate(byte[] signatureValue, byte[] payload, X509Certificate certificate, - String signatureAlgorithmName, + SigningSignatureAlgorithm signatureAlgorithm, RsaSsaPssParameters rsaSsaPssParameters) { - if (signatureAlgorithmName == null || signatureAlgorithmName.isBlank()) { + if (signatureAlgorithm == null) { throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); } - SigningSignatureAlgorithm algorithm; - try { - algorithm = SigningSignatureAlgorithm.fromString(signatureAlgorithmName); - } catch (IllegalArgumentException ex) { - throw new UnprocessableSmartIdResponseException("Unsupported signature algorithm: " + signatureAlgorithmName, ex); - } - - if (algorithm.isLegacyRsa()) { + if (signatureAlgorithm.isLegacyRsa()) { if (rsaSsaPssParameters != null) { throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not allowed for legacy RSA algorithms"); } - validateLegacyRsa(signatureValue, payload, certificate, signatureAlgorithmName); + validateLegacyRsa(signatureValue, payload, certificate, signatureAlgorithm); } else { if (rsaSsaPssParameters == null) { throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' must be provided for RSASSA-PSS algorithm"); diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 00dc4391..20b9abef 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -439,7 +439,7 @@ void signature_withDocumentNumberAndQRCode() { signatureResponse.getSignatureValue(), signableData.dataToSign(), certResponse.certificate(), - signatureResponse.getSignatureAlgorithm().getAlgorithmName(), + signatureResponse.getSignatureAlgorithm(), signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); @@ -546,7 +546,7 @@ void signature_withSemanticIdentifier() { signatureResponse.getSignatureValue(), signableData.dataToSign(), certificateChoiceResponse.getCertificate(), - signatureResponse.getSignatureAlgorithm().getAlgorithmName(), + signatureResponse.getSignatureAlgorithm(), signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); @@ -776,7 +776,7 @@ void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgori signatureResponse.getSignatureValue(), signableData.dataToSign(), signatureResponse.getCertificate(), - signatureAlgorithm.getAlgorithmName(), + signatureAlgorithm, signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); @@ -850,7 +850,7 @@ void signature_withDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) signatureResponse.getSignatureValue(), signableData.dataToSign(), signatureResponse.getCertificate(), - signatureAlgorithm.getAlgorithmName(), + signatureAlgorithm, signatureResponse.getRsaSsaPssParameters()); assertEquals("OK", signatureResponse.getEndResult()); diff --git a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java index 2e0d7519..6fd5bfa9 100644 --- a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java @@ -93,7 +93,7 @@ void validateLegacyRsa_ok(SigningSignatureAlgorithm signatureAlgorithm) throws C SIGNATURE_VALUE_MAP.get(signatureAlgorithm), PAYLOAD, certificate, - signatureAlgorithm.getAlgorithmName())); + signatureAlgorithm)); } @ParameterizedTest @@ -104,7 +104,7 @@ void validateRsaSsaPss_inputParametersNotProvided_throwException(byte[] signatur @ParameterizedTest @ArgumentsSource(EmptyInputArgumentProvider.class) - void validateLegacyRsa_inputParametersNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, String signingSignatureAlgorithm) { + void validateLegacyRsa_inputParametersNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, SigningSignatureAlgorithm signingSignatureAlgorithm) { assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validateLegacyRsa(signatureValue, payload, certificate, signingSignatureAlgorithm)); } @@ -115,7 +115,7 @@ void validateLegacyRsa_nonLegacyAlgorithmName_throwException() { SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), - SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName())); + SigningSignatureAlgorithm.RSASSA_PSS)); assertTrue(ex.getMessage().contains("not a legacy RSA")); } @@ -138,7 +138,7 @@ void validateLegacyRsa_invalidSignature_throwException(SigningSignatureAlgorithm "invalidSignature".getBytes(StandardCharsets.UTF_8), PAYLOAD, CertificateUtil.toX509CertificateFromEncodedString(CERT), - algorithm.getAlgorithmName())); + algorithm)); assertEquals("Signature value validation failed", ex.getMessage()); } @@ -161,7 +161,7 @@ void validateLegacyRsa_signatureValue_constructedPayloadDoesNotMatchTheSignature SIGNATURE_VALUE_MAP.get(signatureAlgorithm), "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), CertificateUtil.toX509CertificateFromEncodedString(CERT), - signatureAlgorithm.getAlgorithmName())); + signatureAlgorithm)); assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); } @@ -174,7 +174,7 @@ void validate_dispatchesToRsaSsaPss_whenAlgorithmIsRsassaPss() throws Certificat SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), PAYLOAD, certificate, - SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + SigningSignatureAlgorithm.RSASSA_PSS, rsaSsaPssParameters)); } @@ -187,7 +187,7 @@ void validate_dispatchesToLegacyRsa_whenAlgorithmIsLegacy(SigningSignatureAlgori SIGNATURE_VALUE_MAP.get(algorithm), PAYLOAD, certificate, - algorithm.getAlgorithmName(), + algorithm, null)); } @@ -201,7 +201,7 @@ void validate_legacyRsa_withRsaSsaPssParametersProvided_throwException(SigningSi SIGNATURE_VALUE_MAP.get(algorithm), PAYLOAD, certificate, - algorithm.getAlgorithmName(), + algorithm, toRsaSsaPssParameters())); } @@ -214,7 +214,7 @@ void validate_rsassaPss_withoutRsaSsaPssParameters_throwException() throws Certi SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), PAYLOAD, certificate, - SigningSignatureAlgorithm.RSASSA_PSS.getAlgorithmName(), + SigningSignatureAlgorithm.RSASSA_PSS, null)); } From 6201f711a832728ed55628bcd14fe18479863ab5 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 9 Mar 2026 18:43:36 +0200 Subject: [PATCH 26/36] Simplify SignatureValueValidator by keeping 1 method instead of 3 and adding interface SignatureFactory and it's implementations RsaSsaPssSignatureFactory and Pkcs15SignatureFactory --- CHANGELOG.md | 8 +- MIGRATION_GUIDE.md | 23 +-- ...ceLinkAuthenticationResponseValidator.java | 5 +- ...cationAuthenticationResponseValidator.java | 5 +- .../signature/Pkcs15SignatureFactory.java | 68 +++++++++ .../signature/RsaSsaPssSignatureFactory.java | 81 ++++++++++ .../smartid/signature/SignatureFactory.java | 33 ++++ .../signature/SignatureValueValidator.java | 54 ++----- .../SignatureValueValidatorImpl.java | 106 ++----------- .../integration/ReadmeIntegrationTest.java | 27 +++- .../signature/Pkcs15SignatureFactoryTest.java | 52 +++++++ .../RsaSsaPssSignatureFactoryTest.java | 43 ++++++ .../SignatureValueValidatorImplTest.java | 141 ++++++------------ 13 files changed, 386 insertions(+), 260 deletions(-) create mode 100644 src/main/java/ee/sk/smartid/signature/Pkcs15SignatureFactory.java create mode 100644 src/main/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactory.java create mode 100644 src/main/java/ee/sk/smartid/signature/SignatureFactory.java create mode 100644 src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java create mode 100644 src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afbfc18..f013db49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Legacy algorithms do not use `signatureAlgorithmParameters` in requests or responses. - Split `SignatureAlgorithm` into `AuthenticationSignatureAlgorithm` (authentication) and `SigningSignatureAlgorithm` (signing). - Only allowed `AuthenticationSignatureAlgorithm` is `RSASSA_PSS`; default `SigningSignatureAlgorithm` is `RSASSA_PSS`. -- Refactored `SignatureValueValidator`: - - Introduced `validateRsaSsaPss(...)` and `validateLegacyRsa(...)` helper methods. - - Added unified 5-parameter `validate(signatureValue, payload, certificate, signatureAlgorithmName, rsaSsaPssParameters)`. - - Enforced that `rsaSsaPssParameters` **must** be present for `RSASSA_PSS` and **must be null** for legacy RSA algorithms. +- Added `SignatureFactory` interface for creating `java.security.Signature` instance for verifying signature and added its implementations: + - `RsaSsaPssSignatureFactory` + - `Pkcs15SignatureFactory` +- Changed `SignatureValueValidator.validate` last parameter from `RsaSsaPssParameters` to `SignatureFactory`: - The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature`: - `AuthenticationSignatureAlgorithm` - `DigestCalculator` diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index a696ff2a..e0c36186 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -8,19 +8,24 @@ Some classes could also be used in v3 and for those classes the package did not For signing flows are restored legacy RSASSA-PKCS#1 v1.5 algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) which are compatible with DigiDoc4j's signing support. For that reason: -* `SignatureAlgorithm` class is split into `AuthenticationSignatureAlgorithm` and `SigningSignatureAlgorithm`. -* `SignatureValueValidator` has now 3 methods `validate`, `validateRsaSsaPss` and `validateLegacyRsa` for different use cases instead previous 1 method `validate` +- `SignatureAlgorithm` class is split into `AuthenticationSignatureAlgorithm` and `SigningSignatureAlgorithm`. +- `SignatureValueValidator.validate` last parameter changed from `RsaSsaPssParameters` to `SignatureFactory` Changes needed in authentication flows: -* change `SignatureAlgorithm` to `AuthenticationSignatureAlgorithm` -* change used `SignatureValueValidator` method from `validate` to `validateRsaSsaPss` (only name changed, parameters unchanged) +- change `SignatureAlgorithm` to `AuthenticationSignatureAlgorithm` +- change `SignatureValueValidator.validate` last parameter from `RsaSsaPssParameters` to `new RsaSsaPssSignatureFactory(RsaSsaPssParameters)` Changes needed in signing flows: -* change `SignatureAlgorithm` to `SigningSignatureAlgorithm` -* suggestion for `SignatureValueValidator` usage: - * when using only signature algorithm RSASSA_PSS then use `SignatureValueValidator.validateRsaSsaPss` - * when using only legacy signature algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) then use `SignatureValueValidator.validateLegacyRsa` - * when both RSASSA_PSS and legacy RSA algorithms are used then use `SignatureValueValidator.validate` +- change `SignatureAlgorithm` to `SigningSignatureAlgorithm` +- suggestion for `SignatureValueValidator.validate` last parameter changes: + - when using only signature algorithm RSASSA_PSS then use `new RsaSsaPssSignatureFactory(RsaSsaPssParameters)` + - when using only legacy signature algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) then use `new Pkcs15SignatureFactory(SigningSignatureAlgorithm)` + - when both RSASSA_PSS and legacy RSA algorithms are used then possible solution is: + ``` + SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() + ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); + ``` When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.signature.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: ``` diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java index f2c71044..6e3e22b0 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java @@ -39,6 +39,7 @@ import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.signature.DigestCalculator; import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.RsaSsaPssSignatureFactory; import ee.sk.smartid.signature.SignatureValueValidator; import ee.sk.smartid.signature.SignatureValueValidatorImpl; import ee.sk.smartid.util.InteractionUtil; @@ -160,10 +161,10 @@ private void validateSignature(AuthenticationResponse authenticationResponse, String schemaName, String brokeredRpName) { byte[] payload = constructPayload(authenticationResponse, authenticationSessionRequest, schemaName, brokeredRpName); - signatureValueValidator.validateRsaSsaPss(authenticationResponse.getSignatureValue(), + signatureValueValidator.validate(authenticationResponse.getSignatureValue(), payload, authenticationResponse.getCertificate(), - authenticationResponse.getRsaSsaPssSignatureParameters()); + new RsaSsaPssSignatureFactory(authenticationResponse.getRsaSsaPssSignatureParameters())); } private byte[] constructPayload(AuthenticationResponse authenticationResponse, diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java index cd4b429c..ef58b7e2 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationResponseValidator.java @@ -36,6 +36,7 @@ import ee.sk.smartid.exception.useraccount.CertificateLevelMismatchException; import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionRequest; import ee.sk.smartid.rest.dao.SessionStatus; +import ee.sk.smartid.signature.RsaSsaPssSignatureFactory; import ee.sk.smartid.signature.SignatureValueValidator; import ee.sk.smartid.signature.SignatureValueValidatorImpl; import ee.sk.smartid.util.InteractionUtil; @@ -151,10 +152,10 @@ private void validateSignature(AuthenticationResponse authenticationResponse, String schemaName, String brokeredRpName) { byte[] payload = constructPayload(authenticationResponse, authenticationSessionRequest, schemaName, brokeredRpName); - signatureValueValidator.validateRsaSsaPss(authenticationResponse.getSignatureValue(), + signatureValueValidator.validate(authenticationResponse.getSignatureValue(), payload, authenticationResponse.getCertificate(), - authenticationResponse.getRsaSsaPssSignatureParameters()); + new RsaSsaPssSignatureFactory(authenticationResponse.getRsaSsaPssSignatureParameters())); } private byte[] constructPayload(AuthenticationResponse authenticationResponse, diff --git a/src/main/java/ee/sk/smartid/signature/Pkcs15SignatureFactory.java b/src/main/java/ee/sk/smartid/signature/Pkcs15SignatureFactory.java new file mode 100644 index 00000000..7e0a04cc --- /dev/null +++ b/src/main/java/ee/sk/smartid/signature/Pkcs15SignatureFactory.java @@ -0,0 +1,68 @@ +package ee.sk.smartid.signature; + +/*- + * #%L + * Smart ID sample Java client + * %% + * Copyright (C) 2018 - 2026 SK ID Solutions AS + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.security.NoSuchAlgorithmException; +import java.security.Signature; + +import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; +import ee.sk.smartid.exception.permanent.SmartIdClientException; + +/** + * {@link SignatureFactory} implementation for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). + */ +public final class Pkcs15SignatureFactory implements SignatureFactory { + + private final SigningSignatureAlgorithm signingSignatureAlgorithm; + + /** + * Creates a factory for legacy RSA (RSASSA-PKCS#1 v1.5) signature verification. + * + * @param signingSignatureAlgorithm the signature algorithm; must not be null and must be a legacy RSA algorithm + * @throws SmartIdClientException if {@code signingSignatureAlgorithm} is null + * @throws UnprocessableSmartIdResponseException if the algorithm is not a legacy RSA algorithm + */ + public Pkcs15SignatureFactory(SigningSignatureAlgorithm signingSignatureAlgorithm) { + if (signingSignatureAlgorithm == null) { + throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); + } + if (!signingSignatureAlgorithm.isLegacyRsa()) { + throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signingSignatureAlgorithm + + "' is not a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm; use validate(..., RsaSsaPssParameters) for RSASSA-PSS"); + } + this.signingSignatureAlgorithm = signingSignatureAlgorithm; + } + + @Override + public Signature getSignature() { + try { + return Signature.getInstance(signingSignatureAlgorithm.getJceAlgorithmName()); + } catch (NoSuchAlgorithmException ex) { + throw new UnprocessableSmartIdResponseException("Signature value validation failed", ex); + } + } +} diff --git a/src/main/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactory.java b/src/main/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactory.java new file mode 100644 index 00000000..6dcc8511 --- /dev/null +++ b/src/main/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactory.java @@ -0,0 +1,81 @@ +package ee.sk.smartid.signature; + +/*- + * #%L + * Smart ID sample Java client + * %% + * Copyright (C) 2018 - 2026 SK ID Solutions AS + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; +import ee.sk.smartid.exception.permanent.SmartIdClientException; + +/** + * {@link SignatureFactory} implementation for RSASSA-PSS (authentication and signing). + */ +public final class RsaSsaPssSignatureFactory implements SignatureFactory { + + private static final Logger logger = LoggerFactory.getLogger(RsaSsaPssSignatureFactory.class); + + private final RsaSsaPssParameters rsaSsaPssParameters; + + /** + * Creates a factory for RSASSA-PSS signature verification. + * + * @param rsaSsaPssParameters signature parameters; must not be null + * @throws SmartIdClientException if {@code rsaSsaPssParameters} is null + */ + public RsaSsaPssSignatureFactory(RsaSsaPssParameters rsaSsaPssParameters) { + if (rsaSsaPssParameters == null) { + throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not provided"); + } + this.rsaSsaPssParameters = rsaSsaPssParameters; + } + + @Override + public Signature getSignature() { + try { + var params = new PSSParameterSpec(rsaSsaPssParameters.getDigestHashAlgorithm().getAlgorithmName(), + rsaSsaPssParameters.getMaskGenAlgorithm().getMgfName(), + new MGF1ParameterSpec(rsaSsaPssParameters.getMaskHashAlgorithm().getAlgorithmName()), + rsaSsaPssParameters.getSaltLength(), + rsaSsaPssParameters.getTrailerField().getPssSpecValue()); + var signature = Signature.getInstance(rsaSsaPssParameters.getSignatureAlgorithmName()); + signature.setParameter(params); + return signature; + } catch (NoSuchAlgorithmException ex) { + logger.error("Invalid signature algorithm name was provided: {}", rsaSsaPssParameters.getSignatureAlgorithmName()); + throw new UnprocessableSmartIdResponseException("Invalid signature algorithm was provided", ex); + } catch (InvalidAlgorithmParameterException ex) { + throw new UnprocessableSmartIdResponseException("Invalid signature algorithm parameters were provided", ex); + } + } +} diff --git a/src/main/java/ee/sk/smartid/signature/SignatureFactory.java b/src/main/java/ee/sk/smartid/signature/SignatureFactory.java new file mode 100644 index 00000000..6d7af934 --- /dev/null +++ b/src/main/java/ee/sk/smartid/signature/SignatureFactory.java @@ -0,0 +1,33 @@ +package ee.sk.smartid.signature; + +/*- + * #%L + * Smart ID sample Java client + * %% + * Copyright (C) 2018 - 2026 SK ID Solutions AS + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.security.Signature; + +public interface SignatureFactory { + Signature getSignature(); +} diff --git a/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java index 98c659a6..c42cfcad 100644 --- a/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java @@ -32,57 +32,25 @@ /** * Interface for signature value validator. + *

+ * Use a concrete {@link SignatureFactory} implementation to specify the signature algorithm and parameters: + * {@link RsaSsaPssSignatureFactory} for RSASSA-PSS (authentication and signing), or + * {@link Pkcs15SignatureFactory} for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). + * The factory encapsulates algorithm choice and parameter validation. */ public interface SignatureValueValidator { /** - * Validates the signature value for RSASSA-PSS (authentication and signing). + * Validates the signature value using the provided signature factory. * - * @param signatureValue the signature value to validate - * @param payload the original data that was signed (typically the hash that was sent to Smart-ID) - * @param certificate X.509 certificate used for signature validation - * @param rsaSsaPssParameters signature parameters used for creating the signature value - * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value - */ - void validateRsaSsaPss(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - RsaSsaPssParameters rsaSsaPssParameters); - - /** - * Validates the signature value for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). - * Use this when the signature session used a legacy RSA algorithm (e.g. {@code sha256WithRSAEncryption}). - * - * @param signatureValue the signature value to validate - * @param payload the digest or data that was signed (typically the hash that was sent to Smart-ID) - * @param certificate X.509 certificate used for signature validation - * @param signatureAlgorithm Smart-ID API algorithm name (e.g. {@code SHA512_WITH_RSA_ENCRYPTION}) - * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value - */ - void validateLegacyRsa(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - SigningSignatureAlgorithm signatureAlgorithm); - - /** - * Validates the signature value for both RSASSA-PSS and legacy RSASSA-PKCS#1 v1.5 algorithms. - *

    - *
  • For RSASSA-PSS ({@link SigningSignatureAlgorithm#RSASSA_PSS}) the {@code rsaSsaPssParameters} - * parameter must be provided.
  • - *
  • For legacy RSA algorithms (e.g. {@code sha256WithRSAEncryption}) the {@code rsaSsaPssParameters} - * parameter must be {@code null}.
  • - *
- * - * @param signatureValue the signature value to validate - * @param payload the data used when calculating the signature (hash or raw bytes, depending on the algorithm) - * @param certificate X.509 certificate used for signature validation - * @param signatureAlgorithm Smart-ID API signature algorithm - * @param rsaSsaPssParameters RSASSA-PSS parameters; required only for RSASSA-PSS and must be {@code null} for legacy RSA + * @param signatureValue the signature value to validate + * @param payload the original data that was signed (typically the hash that was sent to Smart-ID) + * @param certificate X.509 certificate used for signature validation + * @param signatureFactory factory that creates the {@link java.security.Signature} instance for verification * @throws UnprocessableSmartIdResponseException when there is any issue with validating the signature value */ void validate(byte[] signatureValue, byte[] payload, X509Certificate certificate, - SigningSignatureAlgorithm signatureAlgorithm, - RsaSsaPssParameters rsaSsaPssParameters); + SignatureFactory signatureFactory); } diff --git a/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java b/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java index 7e29147f..eb34af7c 100644 --- a/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureValueValidatorImpl.java @@ -27,15 +27,8 @@ */ import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; import java.security.Signature; import java.security.cert.X509Certificate; -import java.security.spec.MGF1ParameterSpec; -import java.security.spec.PSSParameterSpec; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; import ee.sk.smartid.exception.permanent.SmartIdClientException; @@ -46,44 +39,25 @@ */ public final class SignatureValueValidatorImpl implements SignatureValueValidator { - private final Logger logger = LoggerFactory.getLogger(SignatureValueValidatorImpl.class); - @Override - public void validateRsaSsaPss(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - RsaSsaPssParameters rsaSsaPssParameters) { - validateCommonInput(signatureValue, payload, certificate); - if (rsaSsaPssParameters == null) { - throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not provided"); + public void validate(byte[] signatureValue, + byte[] payload, + X509Certificate certificate, + SignatureFactory signatureFactory) { + if (signatureValue == null) { + throw new SmartIdClientException("Parameter 'signatureValue' is not provided"); } - try { - Signature result = getRsaSsaPssSignature(rsaSsaPssParameters); - result.initVerify(certificate.getPublicKey()); - result.update(payload); - if (!result.verify(signatureValue)) { - throw new UnprocessableSmartIdResponseException("Provided signature value does not match the calculated signature value"); - } - } catch (GeneralSecurityException ex) { - throw new UnprocessableSmartIdResponseException("Signature value validation failed", ex); + if (payload == null) { + throw new SmartIdClientException("Parameter 'payload' is not provided"); } - } - - @Override - public void validateLegacyRsa(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - SigningSignatureAlgorithm signatureAlgorithm) { - validateCommonInput(signatureValue, payload, certificate); - if (signatureAlgorithm == null) { - throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); + if (certificate == null) { + throw new SmartIdClientException("Parameter 'certificate' is not provided"); } - if (!signatureAlgorithm.isLegacyRsa()) { - throw new UnprocessableSmartIdResponseException("Signature algorithm '" + signatureAlgorithm + - "' is not a legacy RSA (RSASSA-PKCS#1 v1.5) algorithm; use validate(..., RsaSsaPssParameters) for RSASSA-PSS"); + if (signatureFactory == null) { + throw new SmartIdClientException("Parameter 'signatureFactory' is not provided"); } try { - Signature signature = Signature.getInstance(signatureAlgorithm.getJceAlgorithmName()); + Signature signature = signatureFactory.getSignature(); signature.initVerify(certificate.getPublicKey()); signature.update(payload); if (!signature.verify(signatureValue)) { @@ -93,58 +67,4 @@ public void validateLegacyRsa(byte[] signatureValue, throw new UnprocessableSmartIdResponseException("Signature value validation failed", ex); } } - - @Override - public void validate(byte[] signatureValue, - byte[] payload, - X509Certificate certificate, - SigningSignatureAlgorithm signatureAlgorithm, - RsaSsaPssParameters rsaSsaPssParameters) { - if (signatureAlgorithm == null) { - throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); - } - if (signatureAlgorithm.isLegacyRsa()) { - if (rsaSsaPssParameters != null) { - throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' is not allowed for legacy RSA algorithms"); - } - validateLegacyRsa(signatureValue, payload, certificate, signatureAlgorithm); - } else { - if (rsaSsaPssParameters == null) { - throw new SmartIdClientException("Parameter 'rsaSsaPssParameters' must be provided for RSASSA-PSS algorithm"); - } - validateRsaSsaPss(signatureValue, payload, certificate, rsaSsaPssParameters); - } - } - - private Signature getRsaSsaPssSignature(RsaSsaPssParameters rsaSsaPssParameters) { - try { - var params = new PSSParameterSpec(rsaSsaPssParameters.getDigestHashAlgorithm().getAlgorithmName(), - rsaSsaPssParameters.getMaskGenAlgorithm().getMgfName(), - new MGF1ParameterSpec(rsaSsaPssParameters.getMaskHashAlgorithm().getAlgorithmName()), - rsaSsaPssParameters.getSaltLength(), - rsaSsaPssParameters.getTrailerField().getPssSpecValue()); - var signature = Signature.getInstance(rsaSsaPssParameters.getSignatureAlgorithmName()); - signature.setParameter(params); - return signature; - } catch (NoSuchAlgorithmException ex) { - logger.error("Invalid signature algorithm name was provided: {}", rsaSsaPssParameters.getSignatureAlgorithmName()); - throw new UnprocessableSmartIdResponseException("Invalid signature algorithm was provided", ex); - } catch (InvalidAlgorithmParameterException ex) { - throw new UnprocessableSmartIdResponseException("Invalid signature algorithm parameters were provided", ex); - } - } - - private static void validateCommonInput(byte[] signatureValue, - byte[] payload, - X509Certificate certificate) { - if (signatureValue == null) { - throw new SmartIdClientException("Parameter 'signatureValue' is not provided"); - } - if (payload == null) { - throw new SmartIdClientException("Parameter 'payload' is not provided"); - } - if (certificate == null) { - throw new SmartIdClientException("Parameter 'certificate' is not provided"); - } - } } diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 20b9abef..7b7c444b 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -96,7 +96,10 @@ import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.signature.HashAlgorithm; +import ee.sk.smartid.signature.Pkcs15SignatureFactory; +import ee.sk.smartid.signature.RsaSsaPssSignatureFactory; import ee.sk.smartid.signature.SignableData; +import ee.sk.smartid.signature.SignatureFactory; import ee.sk.smartid.signature.SigningSignatureAlgorithm; import ee.sk.smartid.signature.SignatureValueValidator; import ee.sk.smartid.signature.SignatureValueValidatorImpl; @@ -435,12 +438,14 @@ void signature_withDocumentNumberAndQRCode() { SignatureResponse signatureResponse = signatureResponseValidator.validate(signatureSessionStatus, CertificateLevel.QUALIFIED); // Validate signature value SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); + SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() + ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), signableData.dataToSign(), certResponse.certificate(), - signatureResponse.getSignatureAlgorithm(), - signatureResponse.getRsaSsaPssParameters()); + signatureFactory); assertEquals("OK", signatureResponse.getEndResult()); assertEquals("PNOLT-40504040001-MOCK-Q", signatureResponse.getDocumentNumber()); @@ -542,12 +547,14 @@ void signature_withSemanticIdentifier() { SignatureResponse signatureResponse = signatureResponseValidator.validate(signatureSessionStatus, CertificateLevel.QUALIFIED); // Validate signature value SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); + SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() + ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), signableData.dataToSign(), certificateChoiceResponse.getCertificate(), - signatureResponse.getSignatureAlgorithm(), - signatureResponse.getRsaSsaPssParameters()); + signatureFactory); assertEquals("OK", signatureResponse.getEndResult()); assertEquals("PNOLT-40504040001-MOCK-Q", signatureResponse.getDocumentNumber()); @@ -772,12 +779,14 @@ void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgori SignatureResponse signatureResponse = validator.validate(signatureSessionStatus, certificateLevel); SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); + SignatureFactory signatureFactory = signatureAlgorithm.isLegacyRsa() + ? new Pkcs15SignatureFactory(signatureAlgorithm) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), signableData.dataToSign(), signatureResponse.getCertificate(), - signatureAlgorithm, - signatureResponse.getRsaSsaPssParameters()); + signatureFactory); assertEquals("OK", signatureResponse.getEndResult()); assertEquals("PNOEE-40504040001-DEM0-Q", signatureResponse.getDocumentNumber()); @@ -846,12 +855,14 @@ void signature_withDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) SignatureResponse signatureResponse = validator.validate(signatureSessionStatus, certificateLevel); SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); + SignatureFactory signatureFactory = signatureAlgorithm.isLegacyRsa() + ? new Pkcs15SignatureFactory(signatureAlgorithm) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), signableData.dataToSign(), signatureResponse.getCertificate(), - signatureAlgorithm, - signatureResponse.getRsaSsaPssParameters()); + signatureFactory); assertEquals("OK", signatureResponse.getEndResult()); assertEquals(documentNumber, signatureResponse.getDocumentNumber()); diff --git a/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java b/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java new file mode 100644 index 00000000..ac7be93c --- /dev/null +++ b/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java @@ -0,0 +1,52 @@ +package ee.sk.smartid.signature; + +/*- + * #%L + * Smart ID sample Java client + * %% + * Copyright (C) 2018 - 2026 SK ID Solutions AS + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; +import ee.sk.smartid.exception.permanent.SmartIdClientException; + +class Pkcs15SignatureFactoryTest { + + @Test + void constructor_nullAlgorithm_throwException() { + var ex = assertThrows(SmartIdClientException.class, () -> new Pkcs15SignatureFactory(null)); + assertEquals("Parameter 'signatureAlgorithmName' is not provided", ex.getMessage()); + } + + @Test + void constructor_nonLegacyAlgorithm_throwException() { + var ex = assertThrows(UnprocessableSmartIdResponseException.class, + () -> new Pkcs15SignatureFactory(SigningSignatureAlgorithm.RSASSA_PSS)); + assertTrue(ex.getMessage().contains("not a legacy RSA")); + } +} diff --git a/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java b/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java new file mode 100644 index 00000000..5ca97a6b --- /dev/null +++ b/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java @@ -0,0 +1,43 @@ +package ee.sk.smartid.signature; + +/*- + * #%L + * Smart ID sample Java client + * %% + * Copyright (C) 2018 - 2026 SK ID Solutions AS + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import ee.sk.smartid.exception.permanent.SmartIdClientException; + +class RsaSsaPssSignatureFactoryTest { + + @Test + void constructor_nullParameters_throwException() { + var ex = assertThrows(SmartIdClientException.class, () -> new RsaSsaPssSignatureFactory(null)); + assertEquals("Parameter 'rsaSsaPssParameters' is not provided", ex.getMessage()); + } +} diff --git a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java index 6fd5bfa9..6758bbc6 100644 --- a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java @@ -29,7 +29,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; @@ -73,149 +72,93 @@ void setUp() { } @Test - void validateRsaSsaPss_ok() throws CertificateException { + void validate_withRsaSsaPssSignatureFactory_ok() throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - RsaSsaPssParameters rsaSsaPssParameters = toRsaSsaPssParameters(); + SignatureFactory factory = new RsaSsaPssSignatureFactory(toRsaSsaPssParameters()); - assertDoesNotThrow(() -> signatureValueValidator.validateRsaSsaPss( + assertDoesNotThrow(() -> signatureValueValidator.validate( SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), PAYLOAD, certificate, - rsaSsaPssParameters)); + factory)); } @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validateLegacyRsa_ok(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { + void validate_withPkcs15SignatureFactory_ok(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + SignatureFactory factory = new Pkcs15SignatureFactory(signatureAlgorithm); - assertDoesNotThrow(() -> signatureValueValidator.validateLegacyRsa( + assertDoesNotThrow(() -> signatureValueValidator.validate( SIGNATURE_VALUE_MAP.get(signatureAlgorithm), PAYLOAD, certificate, - signatureAlgorithm)); - } - - @ParameterizedTest - @ArgumentsSource(EmptyInputArgumentProvider.class) - void validateRsaSsaPss_inputParametersNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, RsaSsaPssParameters rsaSsaPssParameters) { - assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validateRsaSsaPss(signatureValue, payload, certificate, rsaSsaPssParameters)); + factory)); } @ParameterizedTest @ArgumentsSource(EmptyInputArgumentProvider.class) - void validateLegacyRsa_inputParametersNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, SigningSignatureAlgorithm signingSignatureAlgorithm) { - assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validateLegacyRsa(signatureValue, payload, certificate, signingSignatureAlgorithm)); + void validate_inputNotProvided_throwException(byte[] signatureValue, byte[] payload, X509Certificate certificate, SignatureFactory signatureFactory, String errorParameter) { + var ex = assertThrows(SmartIdClientException.class, () -> signatureValueValidator.validate(signatureValue, payload, certificate, signatureFactory)); + assertEquals("Parameter '" + errorParameter + "' is not provided", ex.getMessage()); } @Test - void validateLegacyRsa_nonLegacyAlgorithmName_throwException() { - var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validateLegacyRsa( - SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), - PAYLOAD, - CertificateUtil.toX509CertificateFromEncodedString(CERT), - SigningSignatureAlgorithm.RSASSA_PSS)); - assertTrue(ex.getMessage().contains("not a legacy RSA")); - } + void validate_withRsaSsaPssSignatureFactory_invalidSignature_throwException() throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + SignatureFactory factory = new RsaSsaPssSignatureFactory(toRsaSsaPssParameters()); - @Test - void validateRsaSsaPss_invalidSignature_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validateRsaSsaPss( + () -> signatureValueValidator.validate( "invalidValue".getBytes(StandardCharsets.UTF_8), PAYLOAD, - CertificateUtil.toX509CertificateFromEncodedString(CERT), - toRsaSsaPssParameters())); + certificate, + factory)); assertEquals("Signature value validation failed", ex.getMessage()); } @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validateLegacyRsa_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) { + void validate_withPkcs15SignatureFactory_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + SignatureFactory factory = new Pkcs15SignatureFactory(algorithm); + var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validateLegacyRsa( + () -> signatureValueValidator.validate( "invalidSignature".getBytes(StandardCharsets.UTF_8), PAYLOAD, - CertificateUtil.toX509CertificateFromEncodedString(CERT), - algorithm)); + certificate, + factory)); assertEquals("Signature value validation failed", ex.getMessage()); } @Test - void validateRsaSsaPss_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException() { - var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validateRsaSsaPss( - SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), - "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), - CertificateUtil.toX509CertificateFromEncodedString(CERT), - toRsaSsaPssParameters())); - assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); - } + void validate_withRsaSsaPssSignatureFactory_payloadDoesNotMatch_throwException() throws CertificateException { + X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + SignatureFactory factory = new RsaSsaPssSignatureFactory(toRsaSsaPssParameters()); - @ParameterizedTest - @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validateLegacyRsa_signatureValue_constructedPayloadDoesNotMatchTheSignature_throwException(SigningSignatureAlgorithm signatureAlgorithm) { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> signatureValueValidator.validateLegacyRsa( - SIGNATURE_VALUE_MAP.get(signatureAlgorithm), + () -> signatureValueValidator.validate( + SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), - CertificateUtil.toX509CertificateFromEncodedString(CERT), - signatureAlgorithm)); + certificate, + factory)); assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); } - @Test - void validate_dispatchesToRsaSsaPss_whenAlgorithmIsRsassaPss() throws CertificateException { - X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - RsaSsaPssParameters rsaSsaPssParameters = toRsaSsaPssParameters(); - - assertDoesNotThrow(() -> signatureValueValidator.validate( - SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), - PAYLOAD, - certificate, - SigningSignatureAlgorithm.RSASSA_PSS, - rsaSsaPssParameters)); - } - @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_dispatchesToLegacyRsa_whenAlgorithmIsLegacy(SigningSignatureAlgorithm algorithm) throws CertificateException { + void validate_withPkcs15SignatureFactory_payloadDoesNotMatch_throwException(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); + SignatureFactory factory = new Pkcs15SignatureFactory(signatureAlgorithm); - assertDoesNotThrow(() -> signatureValueValidator.validate( - SIGNATURE_VALUE_MAP.get(algorithm), - PAYLOAD, - certificate, - algorithm, - null)); - } - - @ParameterizedTest - @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_legacyRsa_withRsaSsaPssParametersProvided_throwException(SigningSignatureAlgorithm algorithm) throws CertificateException { - X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - - assertThrows(SmartIdClientException.class, - () -> signatureValueValidator.validate( - SIGNATURE_VALUE_MAP.get(algorithm), - PAYLOAD, - certificate, - algorithm, - toRsaSsaPssParameters())); - } - - @Test - void validate_rsassaPss_withoutRsaSsaPssParameters_throwException() throws CertificateException { - X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - - assertThrows(SmartIdClientException.class, + var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( - SIGNATURE_VALUE_MAP.get(SigningSignatureAlgorithm.RSASSA_PSS), - PAYLOAD, + SIGNATURE_VALUE_MAP.get(signatureAlgorithm), + "payloadThatDoesNotMatch".getBytes(StandardCharsets.UTF_8), certificate, - SigningSignatureAlgorithm.RSASSA_PSS, - null)); + factory)); + assertEquals("Provided signature value does not match the calculated signature value", ex.getMessage()); } private static RsaSsaPssParameters toRsaSsaPssParameters() { @@ -232,10 +175,10 @@ private static class EmptyInputArgumentProvider implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) throws CertificateException { return Stream.of( - Arguments.of(null, null, null, null), - Arguments.of(new byte[0], null, null, null), - Arguments.of(new byte[0], new byte[0], null, null), - Arguments.of(new byte[0], new byte[0], CertificateUtil.toX509CertificateFromEncodedString(CERT), null) + Arguments.of(null, null, null, null, "signatureValue"), + Arguments.of(new byte[0], null, null, null, "payload"), + Arguments.of(new byte[0], new byte[0], null, null, "certificate"), + Arguments.of(new byte[0], new byte[0], CertificateUtil.toX509CertificateFromEncodedString(CERT), null, "signatureFactory") ); } } From 6db9650cb8fa7ab11522ee3c1a72f33c8dacf1b3 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 10 Mar 2026 17:42:03 +0200 Subject: [PATCH 27/36] Add DigiDoc4J sample code fragments with legacy RSA to migration guide --- MIGRATION_GUIDE.md | 92 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index e0c36186..2af19363 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -27,15 +27,6 @@ Changes needed in signing flows: : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); ``` -When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.signature.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: -``` -return switch (signatureAlgorithm) { - case SHA256_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA256; - case SHA384_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA384; - case SHA512_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA512; -} -``` - The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature` so when used then imports need to be adjusted: * `AuthenticationSignatureAlgorithm` * `DigestCalculator` @@ -50,6 +41,89 @@ The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature * `SigningSignatureAlgorithm` * `TrailerField` +When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.signature.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: +``` +return switch (signatureAlgorithm) { + case SHA256_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA256; + case SHA384_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA384; + case SHA512_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA512; +} +``` + +## device-link based signing example with legacy RSA algorithm and DigiDoc4J container + +Full device-link process without DigiDoc container is described in README.md + +Here we describe: +- first step in the process: how to get `SignableData` object when `DigiDoc4J` container is used +- last step in the process: how to add signature to `DigiDoc4J` container and how to validate signature + +### Getting SignableData + +Prerequisite: `SmartIdClient smartIdClient` configured and available to use. + +``` + var signatureCertificateLevel = CertificateLevel.QUALIFIED; + var documentNumber = "PNOEE-40504040001-DEM0-Q"; + var dataBytes = "Signable data".getBytes(); + var dataFileName = "test.txt"; + var dataFileMimeType = "text/plain"; + var signatureAlgorithm = SigningSignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION; + org.digidoc4j.DigestAlgorithm digestAlgorithm = DigestAlgorithm.SHA512; + + var certificateByDocumentNumberResult = smartIdClient + .createCertificateByDocumentNumber() + .withDocumentNumber(documentNumber) + .withCertificateLevel(signatureCertificateLevel) + .getCertificateByDocumentNumber(); + var certificate = certificateByDocumentNumberResult.certificate(); + + org.digidoc4j.Configuration configuration = new Configuration(Configuration.Mode.PROD); + org.digidoc4j.DataFile dataFile = new DataFile(dataBytes, dataFileName, dataFileMimeType); + org.digidoc4j.Container container = ContainerBuilder.aContainer() + .withConfiguration(configuration) + .withDataFile(dataFile) + .build(); + org.digidoc4j.DataToSign dataToSign = SignatureBuilder.aSignature(container) + .withSigningCertificate(certificate) + .withSignatureDigestAlgorithm(digestAlgorithm) + .withSignatureProfile(SignatureProfile.LT) + .buildDataToSign(); + byte[] dataToSignBytes = dataToSign.getDataToSign(); + SignableData signableData = new SignableData(dataToSignBytes, signatureAlgorithm.getHashAlgorithmForLegacy()); +``` + +### Validating signature + +Prerequisite: +- `container` and `dataToSign` are the same as in previous code fragment +- `SignatureResponse signatureResponse` is read from RP API with successful response + +``` + byte[] signatureValue = signatureResponse.getSignatureValue(); + + SignatureValueValidator validator = new SignatureValueValidatorImpl(); + SigningSignatureAlgorithm signatureAlgorithm = signatureResponse.getSignatureAlgorithm(); + SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() + ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); + validator.validate( + signatureValue, + dataToSign.getDataToSign(), + signatureResponse.getCertificate(), + signatureFactory); + + org.digidoc4j.Signature digiDoc4jSignature = dataToSign.finalize(signatureValue); + container.addSignature(digiDoc4jSignature); + + org.digidoc4j.ValidationResult validationResult = digiDoc4jSignature.validateSignature(); + + // possible data to use and DigiDoc4J container to save + boolean signatureValid = validationResult.isValid(); + Date timeStampCreationTime = digiDoc4jSignature.getTimeStampCreationTime(); + container.saveAsFile("targetPath"); +``` + # Migrating from Smart-ID v2 to Smart-ID v3 API ## Migrating authentication From bfadc153f58789d0e1c10bc3779742b30af87219 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 10 Mar 2026 22:04:06 +0200 Subject: [PATCH 28/36] Add javadoc to SignatureFactory --- .../sk/smartid/signature/SignatureFactory.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/ee/sk/smartid/signature/SignatureFactory.java b/src/main/java/ee/sk/smartid/signature/SignatureFactory.java index 6d7af934..ec849563 100644 --- a/src/main/java/ee/sk/smartid/signature/SignatureFactory.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureFactory.java @@ -28,6 +28,23 @@ import java.security.Signature; +/** + * Factory for creating preconfigured {@link Signature} instances used when + * validating Smart-ID signatures. + *

+ * Implementations encapsulate the details of the concrete signature algorithm + * (for example RSASSA-PSS or legacy RSASSA-PKCS#1 v1.5) so that callers can + * obtain a correctly initialised {@link Signature} object without dealing with + * low-level JCA configuration. + */ public interface SignatureFactory { + + /** + * Creates a new {@link Signature} instance configured for the underlying + * Smart-ID signature algorithm. + * + * @return a configured {@link Signature} implementation ready for public key + * initialization + */ Signature getSignature(); } From 6f621dce71a9d3bbbc810d71441943ad11a61393 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 10 Mar 2026 22:05:05 +0200 Subject: [PATCH 29/36] Cover SignatureFactory implementations with success flow unit tests --- .../signature/Pkcs15SignatureFactoryTest.java | 11 ++++++++++ .../RsaSsaPssSignatureFactoryTest.java | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java b/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java index ac7be93c..c4434acd 100644 --- a/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java +++ b/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java @@ -27,6 +27,7 @@ */ import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -49,4 +50,14 @@ void constructor_nonLegacyAlgorithm_throwException() { () -> new Pkcs15SignatureFactory(SigningSignatureAlgorithm.RSASSA_PSS)); assertTrue(ex.getMessage().contains("not a legacy RSA")); } + + @Test + void getSignature_legacyAlgorithm_returnsSignatureInstance() { + var factory = new Pkcs15SignatureFactory(SigningSignatureAlgorithm.SHA256_WITH_RSA_ENCRYPTION); + + var signature = factory.getSignature(); + + assertNotNull(signature); + assertEquals(SigningSignatureAlgorithm.SHA256_WITH_RSA_ENCRYPTION.getJceAlgorithmName(), signature.getAlgorithm()); + } } diff --git a/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java b/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java index 5ca97a6b..ae93cf3f 100644 --- a/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java +++ b/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java @@ -27,8 +27,11 @@ */ import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.security.Signature; + import org.junit.jupiter.api.Test; import ee.sk.smartid.exception.permanent.SmartIdClientException; @@ -40,4 +43,21 @@ void constructor_nullParameters_throwException() { var ex = assertThrows(SmartIdClientException.class, () -> new RsaSsaPssSignatureFactory(null)); assertEquals("Parameter 'rsaSsaPssParameters' is not provided", ex.getMessage()); } + + @Test + void getSignature_validParameters_returnsConfiguredSignature() { + var rsaSsaPssParameters = new RsaSsaPssParameters(); + rsaSsaPssParameters.setDigestHashAlgorithm(HashAlgorithm.SHA_256); + rsaSsaPssParameters.setMaskGenAlgorithm(MaskGenAlgorithm.ID_MGF1); + rsaSsaPssParameters.setMaskHashAlgorithm(HashAlgorithm.SHA_256); + rsaSsaPssParameters.setSaltLength(32); + rsaSsaPssParameters.setTrailerField(TrailerField.BC); + + SignatureFactory factory = new RsaSsaPssSignatureFactory(rsaSsaPssParameters); + + Signature signature = factory.getSignature(); + + assertNotNull(signature); + assertEquals(rsaSsaPssParameters.getSignatureAlgorithmName(), signature.getAlgorithm()); + } } From 9053b721456d18680644eca2536d02d4ae0acff5 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 10 Mar 2026 22:13:06 +0200 Subject: [PATCH 30/36] Improve RSASSA-PKCS#1 v1.5 Signature factory name --- CHANGELOG.md | 2 +- MIGRATION_GUIDE.md | 6 +++--- ...Factory.java => RsaSsaPkcs1SignatureFactory.java} | 4 ++-- .../smartid/signature/SignatureValueValidator.java | 2 +- .../smartid/integration/ReadmeIntegrationTest.java | 10 +++++----- .../integration/SmartIdRestIntegrationTest.java | 10 +++++----- ...est.java => RsaSsaPkcs1SignatureFactoryTest.java} | 8 ++++---- .../signature/SignatureValueValidatorImplTest.java | 12 ++++++------ 8 files changed, 27 insertions(+), 27 deletions(-) rename src/main/java/ee/sk/smartid/signature/{Pkcs15SignatureFactory.java => RsaSsaPkcs1SignatureFactory.java} (94%) rename src/test/java/ee/sk/smartid/signature/{Pkcs15SignatureFactoryTest.java => RsaSsaPkcs1SignatureFactoryTest.java} (89%) diff --git a/CHANGELOG.md b/CHANGELOG.md index f013db49..2d576785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Only allowed `AuthenticationSignatureAlgorithm` is `RSASSA_PSS`; default `SigningSignatureAlgorithm` is `RSASSA_PSS`. - Added `SignatureFactory` interface for creating `java.security.Signature` instance for verifying signature and added its implementations: - `RsaSsaPssSignatureFactory` - - `Pkcs15SignatureFactory` + - `RsaSsaPkcs1SignatureFactory` - Changed `SignatureValueValidator.validate` last parameter from `RsaSsaPssParameters` to `SignatureFactory`: - The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature`: - `AuthenticationSignatureAlgorithm` diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 2af19363..6033bdd3 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -19,11 +19,11 @@ Changes needed in signing flows: - change `SignatureAlgorithm` to `SigningSignatureAlgorithm` - suggestion for `SignatureValueValidator.validate` last parameter changes: - when using only signature algorithm RSASSA_PSS then use `new RsaSsaPssSignatureFactory(RsaSsaPssParameters)` - - when using only legacy signature algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) then use `new Pkcs15SignatureFactory(SigningSignatureAlgorithm)` + - when using only legacy signature algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) then use `new RsaSsaPkcs1SignatureFactory(SigningSignatureAlgorithm)` - when both RSASSA_PSS and legacy RSA algorithms are used then possible solution is: ``` SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() - ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); ``` @@ -105,7 +105,7 @@ Prerequisite: SignatureValueValidator validator = new SignatureValueValidatorImpl(); SigningSignatureAlgorithm signatureAlgorithm = signatureResponse.getSignatureAlgorithm(); SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() - ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); validator.validate( signatureValue, diff --git a/src/main/java/ee/sk/smartid/signature/Pkcs15SignatureFactory.java b/src/main/java/ee/sk/smartid/signature/RsaSsaPkcs1SignatureFactory.java similarity index 94% rename from src/main/java/ee/sk/smartid/signature/Pkcs15SignatureFactory.java rename to src/main/java/ee/sk/smartid/signature/RsaSsaPkcs1SignatureFactory.java index 7e0a04cc..e7926c3e 100644 --- a/src/main/java/ee/sk/smartid/signature/Pkcs15SignatureFactory.java +++ b/src/main/java/ee/sk/smartid/signature/RsaSsaPkcs1SignatureFactory.java @@ -35,7 +35,7 @@ /** * {@link SignatureFactory} implementation for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). */ -public final class Pkcs15SignatureFactory implements SignatureFactory { +public final class RsaSsaPkcs1SignatureFactory implements SignatureFactory { private final SigningSignatureAlgorithm signingSignatureAlgorithm; @@ -46,7 +46,7 @@ public final class Pkcs15SignatureFactory implements SignatureFactory { * @throws SmartIdClientException if {@code signingSignatureAlgorithm} is null * @throws UnprocessableSmartIdResponseException if the algorithm is not a legacy RSA algorithm */ - public Pkcs15SignatureFactory(SigningSignatureAlgorithm signingSignatureAlgorithm) { + public RsaSsaPkcs1SignatureFactory(SigningSignatureAlgorithm signingSignatureAlgorithm) { if (signingSignatureAlgorithm == null) { throw new SmartIdClientException("Parameter 'signatureAlgorithmName' is not provided"); } diff --git a/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java index c42cfcad..1f0099f8 100644 --- a/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java +++ b/src/main/java/ee/sk/smartid/signature/SignatureValueValidator.java @@ -35,7 +35,7 @@ *

* Use a concrete {@link SignatureFactory} implementation to specify the signature algorithm and parameters: * {@link RsaSsaPssSignatureFactory} for RSASSA-PSS (authentication and signing), or - * {@link Pkcs15SignatureFactory} for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). + * {@link RsaSsaPkcs1SignatureFactory} for legacy RSASSA-PKCS#1 v1.5 algorithms (signing only). * The factory encapsulates algorithm choice and parameter validation. */ public interface SignatureValueValidator { diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 7b7c444b..ebbd0d56 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -96,7 +96,7 @@ import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.signature.HashAlgorithm; -import ee.sk.smartid.signature.Pkcs15SignatureFactory; +import ee.sk.smartid.signature.RsaSsaPkcs1SignatureFactory; import ee.sk.smartid.signature.RsaSsaPssSignatureFactory; import ee.sk.smartid.signature.SignableData; import ee.sk.smartid.signature.SignatureFactory; @@ -439,7 +439,7 @@ void signature_withDocumentNumberAndQRCode() { // Validate signature value SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() - ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), @@ -548,7 +548,7 @@ void signature_withSemanticIdentifier() { // Validate signature value SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() - ? new Pkcs15SignatureFactory(signatureResponse.getSignatureAlgorithm()) + ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), @@ -780,7 +780,7 @@ void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgori SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); SignatureFactory signatureFactory = signatureAlgorithm.isLegacyRsa() - ? new Pkcs15SignatureFactory(signatureAlgorithm) + ? new RsaSsaPkcs1SignatureFactory(signatureAlgorithm) : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), @@ -856,7 +856,7 @@ void signature_withDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); SignatureFactory signatureFactory = signatureAlgorithm.isLegacyRsa() - ? new Pkcs15SignatureFactory(signatureAlgorithm) + ? new RsaSsaPkcs1SignatureFactory(signatureAlgorithm) : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); signatureValueValidator.validate( signatureResponse.getSignatureValue(), diff --git a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java index e4f99959..2eb81e77 100644 --- a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java @@ -314,8 +314,8 @@ void initNotificationCertificateChoice_withDocumentNumber() { @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void initNotificationSignature_withPkcs15Algorithm_andSemanticIdentifier(SigningSignatureAlgorithm signatureAlgorithm) { - var request = toSignatureSessionRequestWithPkcs15(signatureAlgorithm); + void initNotificationSignature_withRsaSsaPkcs1Algorithm_andSemanticIdentifier(SigningSignatureAlgorithm signatureAlgorithm) { + var request = toSignatureSessionRequestWithRsaSsaPkcs1(signatureAlgorithm); NotificationSignatureSessionResponse sessionResponse = smartIdConnector.initNotificationSignature(request, SEMANTICS_IDENTIFIER); @@ -326,8 +326,8 @@ void initNotificationSignature_withPkcs15Algorithm_andSemanticIdentifier(Signing @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void initNotificationSignature_withPkcs15Algorithm_andDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) { - var request = toSignatureSessionRequestWithPkcs15(signatureAlgorithm); + void initNotificationSignature_withRsaSsaPkcs1Algorithm_andDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) { + var request = toSignatureSessionRequestWithRsaSsaPkcs1(signatureAlgorithm); NotificationSignatureSessionResponse sessionResponse = smartIdConnector.initNotificationSignature(request, DOCUMENT_NUMBER); @@ -336,7 +336,7 @@ void initNotificationSignature_withPkcs15Algorithm_andDocumentNumber(SigningSign assertEquals(VerificationCodeType.NUMERIC4.getValue(), sessionResponse.vc().type()); } - private static NotificationSignatureSessionRequest toSignatureSessionRequestWithPkcs15(SigningSignatureAlgorithm signatureAlgorithm) { + private static NotificationSignatureSessionRequest toSignatureSessionRequestWithRsaSsaPkcs1(SigningSignatureAlgorithm signatureAlgorithm) { byte[] digest = DigestCalculator.calculateDigest("test".getBytes(), signatureAlgorithm.getHashAlgorithmForLegacy()); var signatureProtocolParameters = new RawDigestSignatureProtocolParameters( Base64.toBase64String(digest), diff --git a/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java b/src/test/java/ee/sk/smartid/signature/RsaSsaPkcs1SignatureFactoryTest.java similarity index 89% rename from src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java rename to src/test/java/ee/sk/smartid/signature/RsaSsaPkcs1SignatureFactoryTest.java index c4434acd..bd7eca19 100644 --- a/src/test/java/ee/sk/smartid/signature/Pkcs15SignatureFactoryTest.java +++ b/src/test/java/ee/sk/smartid/signature/RsaSsaPkcs1SignatureFactoryTest.java @@ -36,24 +36,24 @@ import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; import ee.sk.smartid.exception.permanent.SmartIdClientException; -class Pkcs15SignatureFactoryTest { +class RsaSsaPkcs1SignatureFactoryTest { @Test void constructor_nullAlgorithm_throwException() { - var ex = assertThrows(SmartIdClientException.class, () -> new Pkcs15SignatureFactory(null)); + var ex = assertThrows(SmartIdClientException.class, () -> new RsaSsaPkcs1SignatureFactory(null)); assertEquals("Parameter 'signatureAlgorithmName' is not provided", ex.getMessage()); } @Test void constructor_nonLegacyAlgorithm_throwException() { var ex = assertThrows(UnprocessableSmartIdResponseException.class, - () -> new Pkcs15SignatureFactory(SigningSignatureAlgorithm.RSASSA_PSS)); + () -> new RsaSsaPkcs1SignatureFactory(SigningSignatureAlgorithm.RSASSA_PSS)); assertTrue(ex.getMessage().contains("not a legacy RSA")); } @Test void getSignature_legacyAlgorithm_returnsSignatureInstance() { - var factory = new Pkcs15SignatureFactory(SigningSignatureAlgorithm.SHA256_WITH_RSA_ENCRYPTION); + var factory = new RsaSsaPkcs1SignatureFactory(SigningSignatureAlgorithm.SHA256_WITH_RSA_ENCRYPTION); var signature = factory.getSignature(); diff --git a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java index 6758bbc6..652854ce 100644 --- a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java @@ -85,9 +85,9 @@ void validate_withRsaSsaPssSignatureFactory_ok() throws CertificateException { @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_withPkcs15SignatureFactory_ok(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { + void validate_withRsaSsaPkcs1SignatureFactory_ok(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - SignatureFactory factory = new Pkcs15SignatureFactory(signatureAlgorithm); + SignatureFactory factory = new RsaSsaPkcs1SignatureFactory(signatureAlgorithm); assertDoesNotThrow(() -> signatureValueValidator.validate( SIGNATURE_VALUE_MAP.get(signatureAlgorithm), @@ -119,9 +119,9 @@ void validate_withRsaSsaPssSignatureFactory_invalidSignature_throwException() th @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_withPkcs15SignatureFactory_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) throws CertificateException { + void validate_withRsaSsaPkcs1SignatureFactory_invalidSignature_throwException(SigningSignatureAlgorithm algorithm) throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - SignatureFactory factory = new Pkcs15SignatureFactory(algorithm); + SignatureFactory factory = new RsaSsaPkcs1SignatureFactory(algorithm); var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( @@ -148,9 +148,9 @@ void validate_withRsaSsaPssSignatureFactory_payloadDoesNotMatch_throwException() @ParameterizedTest @EnumSource(value = SigningSignatureAlgorithm.class, names = {"SHA256_WITH_RSA_ENCRYPTION", "SHA384_WITH_RSA_ENCRYPTION", "SHA512_WITH_RSA_ENCRYPTION"}) - void validate_withPkcs15SignatureFactory_payloadDoesNotMatch_throwException(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { + void validate_withRsaSsaPkcs1SignatureFactory_payloadDoesNotMatch_throwException(SigningSignatureAlgorithm signatureAlgorithm) throws CertificateException { X509Certificate certificate = CertificateUtil.toX509CertificateFromEncodedString(CERT); - SignatureFactory factory = new Pkcs15SignatureFactory(signatureAlgorithm); + SignatureFactory factory = new RsaSsaPkcs1SignatureFactory(signatureAlgorithm); var ex = assertThrows(UnprocessableSmartIdResponseException.class, () -> signatureValueValidator.validate( From ad7ce16eb077580d086f89f69f3aadbddf51499d Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 10 Mar 2026 22:44:52 +0200 Subject: [PATCH 31/36] Restore HashAlgorithm and DigestCalculator into main package --- CHANGELOG.md | 2 -- MIGRATION_GUIDE.md | 22 +++++++++---------- .../AuthenticationResponseMapperImpl.java | 1 - ...ceLinkAuthenticationResponseValidator.java | 2 -- ...nkAuthenticationSessionRequestBuilder.java | 1 - .../{signature => }/DigestCalculator.java | 2 +- .../{signature => }/HashAlgorithm.java | 2 +- ...onAuthenticationSessionRequestBuilder.java | 1 - .../smartid/SignatureResponseValidator.java | 1 - .../smartid/VerificationCodeCalculator.java | 2 -- .../ee/sk/smartid/signature/DigestInput.java | 2 ++ .../signature/RsaSsaPssParameters.java | 2 ++ .../ee/sk/smartid/signature/SignableData.java | 2 ++ .../ee/sk/smartid/signature/SignableHash.java | 1 + .../signature/SigningSignatureAlgorithm.java | 2 ++ .../ee/sk/smartid/util/CallbackUrlUtil.java | 4 ++-- .../ee/sk/smartid/util/InteractionUtil.java | 4 ++-- .../AuthenticationResponseMapperImplTest.java | 1 - ...nkAuthenticationResponseValidatorTest.java | 1 - ...thenticationSessionRequestBuilderTest.java | 1 - ...inkSignatureSessionRequestBuilderTest.java | 2 -- ...ionSignatureSessionRequestBuilderTest.java | 2 -- ...onAuthenticationResponseValidatorTest.java | 1 - ...thenticationSessionRequestBuilderTest.java | 1 - ...ionSignatureSessionRequestBuilderTest.java | 2 -- .../java/ee/sk/smartid/SmartIdClientTest.java | 1 - .../VerificationCodeCalculatorTest.java | 2 -- .../integration/ReadmeIntegrationTest.java | 2 +- .../SmartIdRestIntegrationTest.java | 4 ++-- .../rest/SmartIdRestConnectorTest.java | 2 +- .../signature/DigestCalculatorTest.java | 2 ++ .../RsaSsaPssSignatureFactoryTest.java | 1 + .../smartid/signature/SignableDataTest.java | 2 ++ .../smartid/signature/SignableHashTest.java | 2 ++ .../SignatureValueValidatorImplTest.java | 1 + 35 files changed, 37 insertions(+), 46 deletions(-) rename src/main/java/ee/sk/smartid/{signature => }/DigestCalculator.java (98%) rename src/main/java/ee/sk/smartid/{signature => }/HashAlgorithm.java (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d576785..561b0ba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Changed `SignatureValueValidator.validate` last parameter from `RsaSsaPssParameters` to `SignatureFactory`: - The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature`: - `AuthenticationSignatureAlgorithm` - - `DigestCalculator` - `DigestInput` - - `HashAlgorithm` - `MaskGenAlgorithm` - `RsaSsaPssParameters` - `SignableData` diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 6033bdd3..d5b57aa0 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -28,18 +28,16 @@ Changes needed in signing flows: ``` The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature` so when used then imports need to be adjusted: -* `AuthenticationSignatureAlgorithm` -* `DigestCalculator` -* `DigestInput` -* `HashAlgorithm` -* `MaskGenAlgorithm` -* `RsaSsaPssParameters` -* `SignableData` -* `SignableHash` -* `SignatureValueValidator` -* `SignatureValueValidatorImpl` -* `SigningSignatureAlgorithm` -* `TrailerField` +- `AuthenticationSignatureAlgorithm` +- `DigestInput` +- `MaskGenAlgorithm` +- `RsaSsaPssParameters` +- `SignableData` +- `SignableHash` +- `SignatureValueValidator` +- `SignatureValueValidatorImpl` +- `SigningSignatureAlgorithm` +- `TrailerField` When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.signature.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: ``` diff --git a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java index 81da82f0..c5827cdd 100644 --- a/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java +++ b/src/main/java/ee/sk/smartid/AuthenticationResponseMapperImpl.java @@ -40,7 +40,6 @@ import ee.sk.smartid.rest.dao.SessionSignature; import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.MaskGenAlgorithm; import ee.sk.smartid.signature.RsaSsaPssParameters; import ee.sk.smartid.signature.TrailerField; diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java index 6e3e22b0..f0e67ed5 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidator.java @@ -37,8 +37,6 @@ import ee.sk.smartid.exception.useraccount.CertificateLevelMismatchException; import ee.sk.smartid.rest.dao.DeviceLinkAuthenticationSessionRequest; import ee.sk.smartid.rest.dao.SessionStatus; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.RsaSsaPssSignatureFactory; import ee.sk.smartid.signature.SignatureValueValidator; import ee.sk.smartid.signature.SignatureValueValidatorImpl; diff --git a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java index d84a6690..89ee9253 100644 --- a/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilder.java @@ -43,7 +43,6 @@ import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.SetUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/signature/DigestCalculator.java b/src/main/java/ee/sk/smartid/DigestCalculator.java similarity index 98% rename from src/main/java/ee/sk/smartid/signature/DigestCalculator.java rename to src/main/java/ee/sk/smartid/DigestCalculator.java index a7e08efa..5495b19d 100644 --- a/src/main/java/ee/sk/smartid/signature/DigestCalculator.java +++ b/src/main/java/ee/sk/smartid/DigestCalculator.java @@ -1,4 +1,4 @@ -package ee.sk.smartid.signature; +package ee.sk.smartid; /*- * #%L diff --git a/src/main/java/ee/sk/smartid/signature/HashAlgorithm.java b/src/main/java/ee/sk/smartid/HashAlgorithm.java similarity index 98% rename from src/main/java/ee/sk/smartid/signature/HashAlgorithm.java rename to src/main/java/ee/sk/smartid/HashAlgorithm.java index 8fbf68a6..91f8ec75 100644 --- a/src/main/java/ee/sk/smartid/signature/HashAlgorithm.java +++ b/src/main/java/ee/sk/smartid/HashAlgorithm.java @@ -1,4 +1,4 @@ -package ee.sk.smartid.signature; +package ee.sk.smartid; /*- * #%L diff --git a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java index f24befcc..64b49972 100644 --- a/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java +++ b/src/main/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilder.java @@ -43,7 +43,6 @@ import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.util.InteractionUtil; import ee.sk.smartid.util.SetUtil; import ee.sk.smartid.util.StringUtil; diff --git a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java index 6416cdcb..c6bc5658 100644 --- a/src/main/java/ee/sk/smartid/SignatureResponseValidator.java +++ b/src/main/java/ee/sk/smartid/SignatureResponseValidator.java @@ -49,7 +49,6 @@ import ee.sk.smartid.rest.dao.SessionSignature; import ee.sk.smartid.rest.dao.SessionSignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.SessionStatus; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.MaskGenAlgorithm; import ee.sk.smartid.signature.RsaSsaPssParameters; import ee.sk.smartid.signature.SigningSignatureAlgorithm; diff --git a/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java b/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java index 36961eab..07ef3268 100644 --- a/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java +++ b/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java @@ -29,8 +29,6 @@ import java.nio.ByteBuffer; import ee.sk.smartid.exception.permanent.SmartIdClientException; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; /** * Utility class for calculating verification code from a hash. diff --git a/src/main/java/ee/sk/smartid/signature/DigestInput.java b/src/main/java/ee/sk/smartid/signature/DigestInput.java index 5b2b67ea..411c199b 100644 --- a/src/main/java/ee/sk/smartid/signature/DigestInput.java +++ b/src/main/java/ee/sk/smartid/signature/DigestInput.java @@ -26,6 +26,8 @@ * #L% */ +import ee.sk.smartid.HashAlgorithm; + /** * Represents data to be signed. *

diff --git a/src/main/java/ee/sk/smartid/signature/RsaSsaPssParameters.java b/src/main/java/ee/sk/smartid/signature/RsaSsaPssParameters.java index 2a118f81..4da4a6bf 100644 --- a/src/main/java/ee/sk/smartid/signature/RsaSsaPssParameters.java +++ b/src/main/java/ee/sk/smartid/signature/RsaSsaPssParameters.java @@ -26,6 +26,8 @@ * #L% */ +import ee.sk.smartid.HashAlgorithm; + /** * Encapsulates multiple parameters of RSASSA-PSS */ diff --git a/src/main/java/ee/sk/smartid/signature/SignableData.java b/src/main/java/ee/sk/smartid/signature/SignableData.java index 903a876c..cec6ad5a 100644 --- a/src/main/java/ee/sk/smartid/signature/SignableData.java +++ b/src/main/java/ee/sk/smartid/signature/SignableData.java @@ -29,6 +29,8 @@ import java.io.Serializable; import java.util.Base64; +import ee.sk.smartid.DigestCalculator; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.permanent.SmartIdRequestSetupException; /** diff --git a/src/main/java/ee/sk/smartid/signature/SignableHash.java b/src/main/java/ee/sk/smartid/signature/SignableHash.java index 4ce78bf7..9cbbd02d 100644 --- a/src/main/java/ee/sk/smartid/signature/SignableHash.java +++ b/src/main/java/ee/sk/smartid/signature/SignableHash.java @@ -29,6 +29,7 @@ import java.io.Serializable; import java.util.Base64; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.permanent.SmartIdRequestSetupException; /** diff --git a/src/main/java/ee/sk/smartid/signature/SigningSignatureAlgorithm.java b/src/main/java/ee/sk/smartid/signature/SigningSignatureAlgorithm.java index 80a15902..fd684fe2 100644 --- a/src/main/java/ee/sk/smartid/signature/SigningSignatureAlgorithm.java +++ b/src/main/java/ee/sk/smartid/signature/SigningSignatureAlgorithm.java @@ -26,6 +26,8 @@ * #L% */ +import ee.sk.smartid.HashAlgorithm; + import java.util.Arrays; /** diff --git a/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java b/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java index 150f00fa..f24af0b1 100644 --- a/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java +++ b/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java @@ -28,12 +28,12 @@ import java.util.Base64; +import ee.sk.smartid.DigestCalculator; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.common.devicelink.CallbackUrl; import ee.sk.smartid.common.devicelink.UrlSafeTokenGenerator; import ee.sk.smartid.exception.SessionSecretMismatchException; import ee.sk.smartid.exception.permanent.SmartIdClientException; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; import jakarta.ws.rs.core.UriBuilder; /** diff --git a/src/main/java/ee/sk/smartid/util/InteractionUtil.java b/src/main/java/ee/sk/smartid/util/InteractionUtil.java index c30ad1bc..39a26114 100644 --- a/src/main/java/ee/sk/smartid/util/InteractionUtil.java +++ b/src/main/java/ee/sk/smartid/util/InteractionUtil.java @@ -33,11 +33,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import ee.sk.smartid.DigestCalculator; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.common.SmartIdInteraction; import ee.sk.smartid.exception.permanent.SmartIdClientException; import ee.sk.smartid.rest.dao.Interaction; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; /** * Utility for interactions related actions diff --git a/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java b/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java index 82b81846..b881e3f9 100644 --- a/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java +++ b/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java @@ -49,7 +49,6 @@ import ee.sk.smartid.rest.dao.SessionSignature; import ee.sk.smartid.rest.dao.SessionSignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.SessionStatus; -import ee.sk.smartid.signature.HashAlgorithm; class AuthenticationResponseMapperImplTest { diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java index c729215d..2766c53b 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationResponseValidatorTest.java @@ -58,7 +58,6 @@ import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.MaskGenAlgorithm; import ee.sk.smartid.signature.TrailerField; import ee.sk.smartid.util.InteractionUtil; diff --git a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java index 1da96d78..e3354398 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkAuthenticationSessionRequestBuilderTest.java @@ -71,7 +71,6 @@ import ee.sk.smartid.rest.dao.Interaction; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; class DeviceLinkAuthenticationSessionRequestBuilderTest { diff --git a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java index fe35e58b..dae450df 100644 --- a/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/DeviceLinkSignatureSessionRequestBuilderTest.java @@ -66,8 +66,6 @@ import ee.sk.smartid.rest.dao.DeviceLinkSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.DeviceLinkSignatureSessionRequest; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.SignableData; import ee.sk.smartid.signature.SignableHash; import ee.sk.smartid.signature.SigningSignatureAlgorithm; diff --git a/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java index 7d45689a..8dbefe83 100644 --- a/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/LinkedNotificationSignatureSessionRequestBuilderTest.java @@ -56,8 +56,6 @@ import ee.sk.smartid.rest.SmartIdConnector; import ee.sk.smartid.rest.dao.LinkedSignatureSessionRequest; import ee.sk.smartid.rest.dao.LinkedSignatureSessionResponse; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.SignableData; import ee.sk.smartid.signature.SignableHash; import ee.sk.smartid.signature.SigningSignatureAlgorithm; diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java index 31e94605..b57f0dca 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationResponseValidatorTest.java @@ -52,7 +52,6 @@ import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.MaskGenAlgorithm; import ee.sk.smartid.signature.TrailerField; diff --git a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java index cbabf221..23262885 100644 --- a/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationAuthenticationSessionRequestBuilderTest.java @@ -63,7 +63,6 @@ import ee.sk.smartid.rest.dao.NotificationAuthenticationSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; class NotificationAuthenticationSessionRequestBuilderTest { diff --git a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java index 19064f1e..6983240d 100644 --- a/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java +++ b/src/test/java/ee/sk/smartid/NotificationSignatureSessionRequestBuilderTest.java @@ -66,8 +66,6 @@ import ee.sk.smartid.rest.dao.NotificationSignatureSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.VerificationCode; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.SignableData; import ee.sk.smartid.signature.SignableHash; import ee.sk.smartid.signature.SigningSignatureAlgorithm; diff --git a/src/test/java/ee/sk/smartid/SmartIdClientTest.java b/src/test/java/ee/sk/smartid/SmartIdClientTest.java index 98abdf7f..277c671f 100644 --- a/src/test/java/ee/sk/smartid/SmartIdClientTest.java +++ b/src/test/java/ee/sk/smartid/SmartIdClientTest.java @@ -59,7 +59,6 @@ import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.VerificationCode; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.SignableData; import ee.sk.smartid.signature.SignableHash; import ee.sk.smartid.signature.SigningSignatureAlgorithm; diff --git a/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java b/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java index b9f65245..7c9bb111 100644 --- a/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java +++ b/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java @@ -42,8 +42,6 @@ import org.junit.jupiter.params.provider.NullAndEmptySource; import ee.sk.smartid.exception.permanent.SmartIdClientException; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; public class VerificationCodeCalculatorTest { diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index ebbd0d56..3fd05428 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -66,6 +66,7 @@ import ee.sk.smartid.DeviceLinkSignatureSessionRequestBuilder; import ee.sk.smartid.DeviceLinkType; import ee.sk.smartid.FileTrustedCAStoreBuilder; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.NotificationAuthenticationResponseValidator; import ee.sk.smartid.NotificationAuthenticationSessionRequestBuilder; import ee.sk.smartid.QrCodeGenerator; @@ -95,7 +96,6 @@ import ee.sk.smartid.rest.dao.NotificationSignatureSessionResponse; import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SessionStatus; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.RsaSsaPkcs1SignatureFactory; import ee.sk.smartid.signature.RsaSsaPssSignatureFactory; import ee.sk.smartid.signature.SignableData; diff --git a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java index 2eb81e77..432ed38a 100644 --- a/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/SmartIdRestIntegrationTest.java @@ -40,6 +40,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import ee.sk.smartid.DigestCalculator; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.RpChallengeGenerator; import ee.sk.smartid.SignatureProtocol; import ee.sk.smartid.SmartIdDemoIntegrationTest; @@ -64,8 +66,6 @@ import ee.sk.smartid.rest.dao.SemanticsIdentifier; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.signature.AuthenticationSignatureAlgorithm; -import ee.sk.smartid.signature.DigestCalculator; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.signature.SigningSignatureAlgorithm; import ee.sk.smartid.util.InteractionUtil; diff --git a/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java b/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java index 2e1e1a7b..0082e0ee 100644 --- a/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java +++ b/src/test/java/ee/sk/smartid/rest/SmartIdRestConnectorTest.java @@ -57,6 +57,7 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import ee.sk.smartid.CertificateLevel; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.SignatureProtocol; import ee.sk.smartid.SmartIdRestServiceStubs; import ee.sk.smartid.common.devicelink.interactions.DeviceLinkInteractionType; @@ -93,7 +94,6 @@ import ee.sk.smartid.rest.dao.SessionStatus; import ee.sk.smartid.rest.dao.SignatureAlgorithmParameters; import ee.sk.smartid.rest.dao.VerificationCode; -import ee.sk.smartid.signature.HashAlgorithm; import ee.sk.smartid.util.InteractionUtil; class SmartIdRestConnectorTest { diff --git a/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java b/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java index e4f23fed..4b600417 100644 --- a/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java +++ b/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java @@ -42,6 +42,8 @@ import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; +import ee.sk.smartid.DigestCalculator; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.permanent.SmartIdClientException; public class DigestCalculatorTest { diff --git a/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java b/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java index ae93cf3f..6bccc89d 100644 --- a/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java +++ b/src/test/java/ee/sk/smartid/signature/RsaSsaPssSignatureFactoryTest.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.permanent.SmartIdClientException; class RsaSsaPssSignatureFactoryTest { diff --git a/src/test/java/ee/sk/smartid/signature/SignableDataTest.java b/src/test/java/ee/sk/smartid/signature/SignableDataTest.java index a41250ac..116288fd 100644 --- a/src/test/java/ee/sk/smartid/signature/SignableDataTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignableDataTest.java @@ -34,6 +34,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; +import ee.sk.smartid.DigestCalculator; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.permanent.SmartIdRequestSetupException; class SignableDataTest { diff --git a/src/test/java/ee/sk/smartid/signature/SignableHashTest.java b/src/test/java/ee/sk/smartid/signature/SignableHashTest.java index 6cff5965..880343da 100644 --- a/src/test/java/ee/sk/smartid/signature/SignableHashTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignableHashTest.java @@ -35,6 +35,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; +import ee.sk.smartid.DigestCalculator; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.permanent.SmartIdRequestSetupException; class SignableHashTest { diff --git a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java index 652854ce..5cf2a5c0 100644 --- a/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java +++ b/src/test/java/ee/sk/smartid/signature/SignatureValueValidatorImplTest.java @@ -47,6 +47,7 @@ import org.junit.jupiter.params.provider.EnumSource; import ee.sk.smartid.CertificateUtil; +import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.UnprocessableSmartIdResponseException; import ee.sk.smartid.exception.permanent.SmartIdClientException; From e57caf418db3ceffc7de259db3a5a306a31d1a99 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 10 Mar 2026 22:52:16 +0200 Subject: [PATCH 32/36] Move DigestCalculatorTest to correct package --- .../ee/sk/smartid/{signature => }/DigestCalculatorTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename src/test/java/ee/sk/smartid/{signature => }/DigestCalculatorTest.java (97%) diff --git a/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java b/src/test/java/ee/sk/smartid/DigestCalculatorTest.java similarity index 97% rename from src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java rename to src/test/java/ee/sk/smartid/DigestCalculatorTest.java index 4b600417..ce42a81c 100644 --- a/src/test/java/ee/sk/smartid/signature/DigestCalculatorTest.java +++ b/src/test/java/ee/sk/smartid/DigestCalculatorTest.java @@ -1,4 +1,4 @@ -package ee.sk.smartid.signature; +package ee.sk.smartid; /*- * #%L @@ -42,8 +42,6 @@ import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; -import ee.sk.smartid.DigestCalculator; -import ee.sk.smartid.HashAlgorithm; import ee.sk.smartid.exception.permanent.SmartIdClientException; public class DigestCalculatorTest { From d25a5a04e4e54447b77d5e61b30b126698bf9203 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 10 Mar 2026 23:01:04 +0200 Subject: [PATCH 33/36] Restore previous year in license header for unchanged files --- src/main/java/ee/sk/smartid/DigestCalculator.java | 2 +- src/main/java/ee/sk/smartid/HashAlgorithm.java | 2 +- src/main/java/ee/sk/smartid/VerificationCodeCalculator.java | 2 +- src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java | 2 +- src/main/java/ee/sk/smartid/util/InteractionUtil.java | 2 +- .../ee/sk/smartid/AuthenticationResponseMapperImplTest.java | 2 +- src/test/java/ee/sk/smartid/DigestCalculatorTest.java | 2 +- src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/ee/sk/smartid/DigestCalculator.java b/src/main/java/ee/sk/smartid/DigestCalculator.java index 5495b19d..a8ace55e 100644 --- a/src/main/java/ee/sk/smartid/DigestCalculator.java +++ b/src/main/java/ee/sk/smartid/DigestCalculator.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/HashAlgorithm.java b/src/main/java/ee/sk/smartid/HashAlgorithm.java index 91f8ec75..f7e74a9d 100644 --- a/src/main/java/ee/sk/smartid/HashAlgorithm.java +++ b/src/main/java/ee/sk/smartid/HashAlgorithm.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java b/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java index 07ef3268..661658f5 100644 --- a/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java +++ b/src/main/java/ee/sk/smartid/VerificationCodeCalculator.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java b/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java index f24af0b1..b12e1fc2 100644 --- a/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java +++ b/src/main/java/ee/sk/smartid/util/CallbackUrlUtil.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/main/java/ee/sk/smartid/util/InteractionUtil.java b/src/main/java/ee/sk/smartid/util/InteractionUtil.java index 39a26114..2ba91cbd 100644 --- a/src/main/java/ee/sk/smartid/util/InteractionUtil.java +++ b/src/main/java/ee/sk/smartid/util/InteractionUtil.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java b/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java index b881e3f9..a671136a 100644 --- a/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java +++ b/src/test/java/ee/sk/smartid/AuthenticationResponseMapperImplTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/DigestCalculatorTest.java b/src/test/java/ee/sk/smartid/DigestCalculatorTest.java index ce42a81c..aa6ad3f0 100644 --- a/src/test/java/ee/sk/smartid/DigestCalculatorTest.java +++ b/src/test/java/ee/sk/smartid/DigestCalculatorTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java b/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java index 7c9bb111..847e2fa7 100644 --- a/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java +++ b/src/test/java/ee/sk/smartid/VerificationCodeCalculatorTest.java @@ -4,7 +4,7 @@ * #%L * Smart ID sample Java client * %% - * Copyright (C) 2018 - 2026 SK ID Solutions AS + * Copyright (C) 2018 - 2025 SK ID Solutions AS * %% * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From 1d8b905356f436627150d54e3a480383b8cbd9c7 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Wed, 11 Mar 2026 10:04:08 +0200 Subject: [PATCH 34/36] Update README.md and move DD4J code snippets from migration guide to readme --- MIGRATION_GUIDE.md | 85 +-------------------------- README.md | 142 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 124 insertions(+), 103 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index d5b57aa0..af1e1bc3 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -21,7 +21,7 @@ Changes needed in signing flows: - when using only signature algorithm RSASSA_PSS then use `new RsaSsaPssSignatureFactory(RsaSsaPssParameters)` - when using only legacy signature algorithms (`SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`) then use `new RsaSsaPkcs1SignatureFactory(SigningSignatureAlgorithm)` - when both RSASSA_PSS and legacy RSA algorithms are used then possible solution is: - ``` + ```java SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); @@ -39,88 +39,7 @@ The following classes are moved from `ee.sk.smartid` to `ee.sk.smartid.signature - `SigningSignatureAlgorithm` - `TrailerField` -When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.signature.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: -``` -return switch (signatureAlgorithm) { - case SHA256_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA256; - case SHA384_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA384; - case SHA512_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA512; -} -``` - -## device-link based signing example with legacy RSA algorithm and DigiDoc4J container - -Full device-link process without DigiDoc container is described in README.md - -Here we describe: -- first step in the process: how to get `SignableData` object when `DigiDoc4J` container is used -- last step in the process: how to add signature to `DigiDoc4J` container and how to validate signature - -### Getting SignableData - -Prerequisite: `SmartIdClient smartIdClient` configured and available to use. - -``` - var signatureCertificateLevel = CertificateLevel.QUALIFIED; - var documentNumber = "PNOEE-40504040001-DEM0-Q"; - var dataBytes = "Signable data".getBytes(); - var dataFileName = "test.txt"; - var dataFileMimeType = "text/plain"; - var signatureAlgorithm = SigningSignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION; - org.digidoc4j.DigestAlgorithm digestAlgorithm = DigestAlgorithm.SHA512; - - var certificateByDocumentNumberResult = smartIdClient - .createCertificateByDocumentNumber() - .withDocumentNumber(documentNumber) - .withCertificateLevel(signatureCertificateLevel) - .getCertificateByDocumentNumber(); - var certificate = certificateByDocumentNumberResult.certificate(); - - org.digidoc4j.Configuration configuration = new Configuration(Configuration.Mode.PROD); - org.digidoc4j.DataFile dataFile = new DataFile(dataBytes, dataFileName, dataFileMimeType); - org.digidoc4j.Container container = ContainerBuilder.aContainer() - .withConfiguration(configuration) - .withDataFile(dataFile) - .build(); - org.digidoc4j.DataToSign dataToSign = SignatureBuilder.aSignature(container) - .withSigningCertificate(certificate) - .withSignatureDigestAlgorithm(digestAlgorithm) - .withSignatureProfile(SignatureProfile.LT) - .buildDataToSign(); - byte[] dataToSignBytes = dataToSign.getDataToSign(); - SignableData signableData = new SignableData(dataToSignBytes, signatureAlgorithm.getHashAlgorithmForLegacy()); -``` - -### Validating signature - -Prerequisite: -- `container` and `dataToSign` are the same as in previous code fragment -- `SignatureResponse signatureResponse` is read from RP API with successful response - -``` - byte[] signatureValue = signatureResponse.getSignatureValue(); - - SignatureValueValidator validator = new SignatureValueValidatorImpl(); - SigningSignatureAlgorithm signatureAlgorithm = signatureResponse.getSignatureAlgorithm(); - SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() - ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) - : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); - validator.validate( - signatureValue, - dataToSign.getDataToSign(), - signatureResponse.getCertificate(), - signatureFactory); - - org.digidoc4j.Signature digiDoc4jSignature = dataToSign.finalize(signatureValue); - container.addSignature(digiDoc4jSignature); - - org.digidoc4j.ValidationResult validationResult = digiDoc4jSignature.validateSignature(); - - // possible data to use and DigiDoc4J container to save - boolean signatureValid = validationResult.isValid(); - Date timeStampCreationTime = digiDoc4jSignature.getTimeStampCreationTime(); - container.saveAsFile("targetPath"); -``` +For legacy RSA with DigiDoc4j there is new chapter in README.md: [Legacy algorithms for signing with DigiDoc4j](./README.md#legacy-algorithms-for-signing-with-digidoc4j) # Migrating from Smart-ID v2 to Smart-ID v3 API diff --git a/README.md b/README.md index 80a7df8a..a8a50c6c 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ This library supports Smart-ID API v3.1. * [Error handling for session status](#error-handling-for-session-status) * [Certificate by document number](#certificate-by-document-number) * [Example of querying certificate by document number](#example-of-querying-certificate-by-document-number) + * [Legacy algorithms for signing with DigiDoc4j](#legacy-algorithms-for-signing-with-digidoc4j) * [Linked signature session flow](#linked-signature-flow) * [Device link certificate choice session](#device-link-certificate-choice-session) * [Examples of initiating a device-link certificate choice session](#example-of-initiating-a-device-link-certificate-choice-session) @@ -203,9 +204,9 @@ More info available here https://sk-eid.github.io/smart-id-documentation/rp-api/ * `signatureProtocol`: Required. Signature protocol to use. Currently, the only allowed value is ACSP_V2. * `signatureProtocolParameters`: Required. Parameters for the ACSP_V2 signature protocol. * `rpChallenge`: Required. Base64-encoded value, length between 44 and 88 characters. - * `signatureAlgorithm`: Required. Signature algorithm name. Supported value only `rsassa-pss`. + * `signatureAlgorithm`: Required. Use `AuthenticationSignatureAlgorithm` enum. Supported value: `RSASSA_PSS`. * `signatureAlgorithmParameters`: Required. Parameters for the signature algorithm. - * `hashAlgorithm`: Required. Hash algorithm name. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. + * `hashAlgorithm`: Required. Use `HashAlgorithm` enum. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. * `interactions`: Required. Base64-encoded JSON string of an array of interaction objects. * Each interaction object includes: * `type`: Required. Type of interaction. Allowed types are `displayTextAndPIN`, `confirmationMessage`. @@ -240,6 +241,8 @@ DeviceLinkAuthenticationSessionRequestBuilder builder = smartIdClient .createDeviceLinkAuthentication() // to use anonymous authentication, do not set semantics identifier or document number .withRpChallenge(rpChallenge) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) + .withHashAlgorithm(HashAlgorithm.SHA3_512) .withInteractions(Collections.singletonList( DeviceLinkInteraction.displayTextAndPin("Logging into ") // Display text should be concise and specific. )); @@ -289,6 +292,8 @@ DeviceLinkAuthenticationSessionRequestBuilder builder = smartIdClient .createDeviceLinkAuthentication() .withSemanticsIdentifier(semanticsIdentifier) .withRpChallenge(rpChallenge.toBase64EncodedValue()) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) + .withHashAlgorithm(HashAlgorithm.SHA3_512) .withInteractions(Collections.singletonList( DeviceLinkInteraction.displayTextAndPin("Logging into ") // Display text should be concise and specific. )); @@ -331,6 +336,8 @@ DeviceLinkAuthenticationSessionRequestBuilder builder = smartIdClient .createDeviceLinkAuthentication() .withDocumentNumber(documentNumber) .withRpChallenge(rpChallenge.toBase64EncodedValue()) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) + .withHashAlgorithm(HashAlgorithm.SHA3_512) .withInteractions(Collections.singletonList( DeviceLinkInteraction.displayTextAndPin("Logging into ") // Display text should be concise and specific. )); @@ -377,6 +384,8 @@ DeviceLinkAuthenticationSessionRequestBuilder builder = smartIdClient .createDeviceLinkAuthentication() .withDocumentNumber(documentNumber) .withRpChallenge(rpChallenge.toBase64EncodedValue()) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) + .withHashAlgorithm(HashAlgorithm.SHA3_512) .withInteractions(Collections.singletonList( DeviceLinkInteraction.displayTextAndPin("Logging into ") // Display text should be concise and specific. )) @@ -419,9 +428,9 @@ The request parameters for the device-link signature session are as follows: * `signatureProtocol`: Required. Signature protocol to use. Currently, the only allowed value is RAW_DIGEST_SIGNATURE. * `signatureProtocolParameters`: Required. Parameters for the RAW_DIGEST_SIGNATURE signature protocol. * `digest`: Required. Base64 encoded digest to be signed. - * `signatureAlgorithm`: Required. Signature algorithm name. Only supported value is `rsassa-pss` - * `signatureAlgorithmParameters`: Required. Parameters for the signature algorithm. - * `hashAlgorithm`: Required. Hash algorithm name. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. + * `signatureAlgorithm`: Required. Use `SigningSignatureAlgorithm` enum. Supported: `RSASSA_PSS` (default), `SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, `SHA512_WITH_RSA_ENCRYPTION`. Legacy algorithms are compatible with DigiDoc4j. + * `signatureAlgorithmParameters`: Required for RSASSA_PSS; omit for legacy algorithms. Parameters for the signature algorithm. + * `hashAlgorithm`: Required. Use `HashAlgorithm` enum. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. * `interactions`: Required. Base64-encoded JSON string of an array of interaction objects. * Each interaction object includes: * `type`: Required. Type of interaction. Allowed types are `displayTextAndPIN`, `confirmationMessage`. @@ -461,6 +470,7 @@ DeviceLinkSessionResponse signatureResponse = client.createDeviceLinkSignature() .withCertificateLevel(CertificateLevel.QSCD) .withSignableData(signableData) .withSemanticsIdentifier(semanticsIdentifier) + .withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS) .withHashAlgorithm(HashAlgorithm.SHA_512) .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Please sign the "))) // Display text should be concise and specific. .withInitialCallbackUrl("https://example.com/callback") // Only needed for same-device flows(Web2App, App2App) @@ -494,6 +504,7 @@ DeviceLinkSessionResponse signatureResponse = smartIdClient.createDeviceLinkSign .withCertificateLevel(CertificateLevel.QSCD) .withSignableData(signableData) .withDocumentNumber(documentNumber) + .withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS) .withHashAlgorithm(HashAlgorithm.SHA_512) .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Please sign the "))) // Display text should be concise and specific. .initSignatureSession(); @@ -918,13 +929,17 @@ try { // Initialize the signature response validator with CertificateValidator SignatureResponseValidator signatureResponseValidator = new SignatureResponseValidator(certificateValidator); // Validate and map the session status. If the sessions end result is other than OK, then an exception will be thrown. - SignatureResponse signatureResponse = signatureResponseValidator.validate(signatureSessionStatus, CertificateLevel.QUALIFIED.name()); - // Validate signature value. This step can be skipped if other means of validating the signature value can be used. + SignatureResponse signatureResponse = signatureResponseValidator.validate(signatureSessionStatus, CertificateLevel.QUALIFIED); + // Validate signature value using suitable SignatureFactory, for RSASSA_PSS use RsaSsaPssSignatureFactory and for legacy RSA RsaSsaPkcs1SignatureFactory SignatureValueValidator signatureValueValidator = new SignatureValueValidatorImpl(); - signatureValueValidator.validate(signatureResponse.getSignatureValue(), - signableData.calculateHash(), + SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() + ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); + signatureValueValidator.validate( + signatureResponse.getSignatureValue(), + signableData.dataToSign(), certResponse.certificate(), - signatureResponse.getRsaSsaPssParameters()); + signatureFactory); // Process the response (e.g., save to database or pass to another system) handleSignatureResponse(signatureResponse); @@ -1003,6 +1018,86 @@ certificateValidator.validateCertificate(certResponse.certificate()); ``` Checkout out other ways to set up TrustedCaCertStore with CertificateValidator in [Set up CertificateValidator](#set-up-certificatevalidator). +## Legacy algorithms for signing with DigiDoc4j + +DigiDoc4j library does not support RSASSA-PSS. To sign with DigiDoc4j, use legacy algorithms: `SHA256_WITH_RSA_ENCRYPTION`, `SHA384_WITH_RSA_ENCRYPTION`, or `SHA512_WITH_RSA_ENCRYPTION` from `SigningSignatureAlgorithm`. + +When in signing using legacy RSA and DigiDoc4J then conversion from `ee.sk.smartid.signature.SigningSignatureAlgorithm` to DigiDoc4J `org.digidoc4j.DigestAlgorithm` is: +```java +switch (signatureAlgorithm) { + case SHA256_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA256; + case SHA384_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA384; + case SHA512_WITH_RSA_ENCRYPTION -> DigestAlgorithm.SHA512; +} +``` + +### Create `SignableData` with DigiDoc4j's `SignatureBuilder` using the queried certificate + +Sample code snippet (Prerequisite: `SmartIdClient smartIdClient` configured and available to use): + +```java +var signatureCertificateLevel = CertificateLevel.QUALIFIED; +var documentNumber = "PNOEE-40504040001-DEM0-Q"; +var dataBytes = "Signable data".getBytes(); +var dataFileName = "test.txt"; +var dataFileMimeType = "text/plain"; +var signatureAlgorithm = SigningSignatureAlgorithm.SHA512_WITH_RSA_ENCRYPTION; +org.digidoc4j.DigestAlgorithm digestAlgorithm = DigestAlgorithm.SHA512; + +var certificateByDocumentNumberResult = smartIdClient + .createCertificateByDocumentNumber() + .withDocumentNumber(documentNumber) + .withCertificateLevel(signatureCertificateLevel) + .getCertificateByDocumentNumber(); +var certificate = certificateByDocumentNumberResult.certificate(); + +org.digidoc4j.Configuration configuration = new Configuration(Configuration.Mode.PROD); +org.digidoc4j.DataFile dataFile = new DataFile(dataBytes, dataFileName, dataFileMimeType); +org.digidoc4j.Container container = ContainerBuilder.aContainer() + .withConfiguration(configuration) + .withDataFile(dataFile) + .build(); +org.digidoc4j.DataToSign dataToSign = SignatureBuilder.aSignature(container) + .withSigningCertificate(certificate) + .withSignatureDigestAlgorithm(digestAlgorithm) + .withSignatureProfile(SignatureProfile.LT) + .buildDataToSign(); +byte[] dataToSignBytes = dataToSign.getDataToSign(); +SignableData signableData = new SignableData(dataToSignBytes, signatureAlgorithm.getHashAlgorithmForLegacy()); +``` + +### Validating signature using DigiDoc4j + +Prerequisite: +- `container` and `dataToSign` are the same as in previous code fragment +- `SignatureResponse signatureResponse` is read from RP API with successful response + +Sample code snippet: +```java +byte[] signatureValue = signatureResponse.getSignatureValue(); + +SignatureValueValidator validator = new SignatureValueValidatorImpl(); +SigningSignatureAlgorithm signatureAlgorithm = signatureResponse.getSignatureAlgorithm(); +SignatureFactory signatureFactory = signatureResponse.getSignatureAlgorithm().isLegacyRsa() + ? new RsaSsaPkcs1SignatureFactory(signatureResponse.getSignatureAlgorithm()) + : new RsaSsaPssSignatureFactory(signatureResponse.getRsaSsaPssParameters()); +validator.validate( + signatureValue, + dataToSign.getDataToSign(), + signatureResponse.getCertificate(), + signatureFactory); + +org.digidoc4j.Signature digiDoc4jSignature = dataToSign.finalize(signatureValue); +container.addSignature(digiDoc4jSignature); + +org.digidoc4j.ValidationResult validationResult = digiDoc4jSignature.validateSignature(); + +// possible data to use and DigiDoc4J container to save +boolean signatureValid = validationResult.isValid(); +Date timeStampCreationTime = digiDoc4jSignature.getTimeStampCreationTime(); +container.saveAsFile("targetPath"); +``` + ## Linked signature flow In API v3.1 a new flow was introduced to link signature session to a previously completed certificate choice session. @@ -1062,9 +1157,9 @@ Second part of the linked signature flow. Will be used to start the signature se * `signatureProtocol`: Required. Signature protocol to use. Currently, the only allowed value is RAW_DIGEST_SIGNATURE. * `signatureProtocolParameters`: Required. Parameters for the RAW_DIGEST_SIGNATURE signature protocol. * `digest`: Required. Base64 encoded digest to be signed. - * `signatureAlgorithm`: Required. Signature algorithm name. Only supported value is `rsassa-pss` - * `signatureAlgorithmParameters`: Required. Parameters for the signature algorithm. - * `hashAlgorithm`: Required. Hash algorithm name. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. + * `signatureAlgorithm`: Required. Use `SigningSignatureAlgorithm` enum. Supported: `RSASSA_PSS` (default), legacy algorithms for DigiDoc4j compatibility. + * `signatureAlgorithmParameters`: Required for RSASSA_PSS; omit for legacy algorithms. + * `hashAlgorithm`: Required. Use `HashAlgorithm` enum. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. * `linkedSessionID`: Required. Session ID of the previously completed certificate choice session. * `interactions`: Required. Base64-encoded JSON string of an array of interaction objects. * Each interaction object includes: @@ -1091,6 +1186,7 @@ LinkedSignatureSessionResponse signatureSessionResponse = smartIdClient.createLi .withDocumentNumber(certificateChoiceResponse.getDocumentNumber()) .withLinkedSessionID(certificateChoiceSessionResponse.sessionID()) .withSignableData(new SignableData("dataToSign".getBytes(), HashAlgorithm.SHA_256)) + .withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS) .withInteractions(List.of(DeviceLinkInteraction.displayTextAndPin("Please sign the "))) // Display text should be concise and specific. .initSignatureSession(); @@ -1127,9 +1223,9 @@ Jump to [Query session status](#example-of-using-session-status-poller-to-query- * `signatureProtocol`: Required. Signature protocol to use. Currently, the only allowed value is ACSP_V2. * `signatureProtocolParameters`: Required. Parameters for the ACSP_V2 signature protocol. * `rpChallenge`: Required. Random value with size in range of 32-64 bytes. Must be Base64 encoded. - * `signatureAlgorithm`: Required. Signature algorithm name. Supported values is 'rsassa-pss' + * `signatureAlgorithm`: Required. Use `AuthenticationSignatureAlgorithm` enum. Supported value: `RSASSA_PSS`. * `signatureAlgorithmParameters`: Required. Parameters for the signature algorithm. - * `hashAlgorithm`: Required. Hash algorithm name. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. + * `hashAlgorithm`: Required. Use `HashAlgorithm` enum. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. * `interactions`: Required. An array of interaction objects defining the interactions in order of preference. * Each interaction object includes: * `type`: Required. Type of interaction. Allowed types are `displayTextAndPIN`, `confirmationMessage`, `confirmationMessageAndVerificationCodeChoice`. @@ -1162,6 +1258,8 @@ NotificationAuthenticationSessionResponse authenticationSessionResponse = client .withDocumentNumber(documentNumber) .withRpChallenge(rpChallenge.toBase64EncodedValue()) .withCertificateLevel(AuthenticationCertificateLevel.QUALIFIED) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) + .withHashAlgorithm(HashAlgorithm.SHA3_512) .withInteractions(Collections.singletonList( NotificationInteraction.displayTextAndPin("Logging into ") // Display text should be concise and specific. )) @@ -1194,6 +1292,8 @@ NotificationAuthenticationSessionResponse authenticationSessionResponse = client .withSemanticsIdentifier(semanticsIdentifier) .withRpChallenge(rpChallenge.toBase64EncodedValue()) .withCertificateLevel(AuthenticationCertificateLevel.QUALIFIED) + .withSignatureAlgorithm(AuthenticationSignatureAlgorithm.RSASSA_PSS) + .withHashAlgorithm(HashAlgorithm.SHA3_512) .withInteractions(Collections.singletonList( NotificationInteraction.displayTextAndPin("Logging into "))) // Display text should be concise and specific. .initAuthenticationSession(); @@ -1255,9 +1355,9 @@ The request parameters for the notification-based signature session are as follo * `signatureProtocol`: Required. Signature protocol to use. Currently, the only allowed value is RAW_DIGEST_SIGNATURE. * `signatureProtocolParameters`: Required. Parameters for the RAW_DIGEST_SIGNATURE signature protocol. * `digest`: Required. Base64 encoded digest to be signed. - * `signatureAlgorithm`: Required. Signature algorithm name. Only `rsassa-pss` is currently supported. - * `signatureAlgorithmParameters`: Required. Parameters for the signature algorithm. - * `hashAlgorithm`: Required. Hash algorithm used for digest. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. + * `signatureAlgorithm`: Required. Use `SigningSignatureAlgorithm` enum. Supported: `RSASSA_PSS` (default), legacy algorithms for DigiDoc4j compatibility. + * `signatureAlgorithmParameters`: Required for RSASSA_PSS; omit for legacy algorithms. + * `hashAlgorithm`: Required. Use `HashAlgorithm` enum. Supported values are `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, `SHA3-384`, `SHA3-512`. * `interactions`: Required. Base64-encoded string of interactions to be used for a session. The interactions are defined in order of preference. * Each interaction object includes: * `type`: Required. Type of interaction. Allowed types are `displayTextAndPIN`, `confirmationMessage`, `confirmationMessageAndVerificationCodeChoice`. @@ -1293,6 +1393,7 @@ NotificationSignatureSessionResponse signatureSessionResponse = smartIdClient.cr .withCertificateLevel(CertificateLevel.QSCD) .withSignableData(signableData) .withSemanticsIdentifier(semanticsIdentifier) + .withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS) .withInteractions(List.of( NotificationInteraction.confirmationMessage("Please sign the ")) // Display text should be concise and specific. ) @@ -1302,7 +1403,7 @@ NotificationSignatureSessionResponse signatureSessionResponse = smartIdClient.cr String sessionID = signatureSessionResponse.sessionID(); // Display verification code to the user -String verificationCode = signatureSessionResponse.vc().getValue(); +String verificationCode = signatureSessionResponse.vc().value(); ``` Jump to [Query session status](#example-of-using-session-status-poller-to-query-final-sessions-status) for an example of session querying. @@ -1322,7 +1423,8 @@ NotificationSignatureSessionResponse signatureResponse = client.createNotificati .withCertificateLevel(CertificateLevel.QUALIFIED) .withSignableData(signableData) .withDocumentNumber(documentNumber) - .withAllowedInteractionsOrder(List.of( + .withSignatureAlgorithm(SigningSignatureAlgorithm.RSASSA_PSS) + .withInteractions(List.of( NotificationInteraction.confirmationMessage("Please sign the "))) // Display text should be concise and specific. .initSignatureSession(); From 6fb11b23a252af4311ec310f5708f2e282ca0574 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Mon, 16 Mar 2026 15:29:10 +0200 Subject: [PATCH 35/36] Make some device-link tests working inside ReadmeIntegrationTest (#147) --- .../integration/DeviceLinkMockRequest.java | 45 ++++ .../integration/ReadmeIntegrationTest.java | 212 ++++++++++++------ src/test/resources/logback.xml | 1 + 3 files changed, 192 insertions(+), 66 deletions(-) create mode 100644 src/test/java/ee/sk/smartid/integration/DeviceLinkMockRequest.java diff --git a/src/test/java/ee/sk/smartid/integration/DeviceLinkMockRequest.java b/src/test/java/ee/sk/smartid/integration/DeviceLinkMockRequest.java new file mode 100644 index 00000000..6bc91961 --- /dev/null +++ b/src/test/java/ee/sk/smartid/integration/DeviceLinkMockRequest.java @@ -0,0 +1,45 @@ +package ee.sk.smartid.integration; + +/*- + * #%L + * Smart ID sample Java client + * %% + * Copyright (C) 2018 - 2026 SK ID Solutions AS + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Object to send to Smart-ID Mock service to simulate user-action in the Device Link flow. + * + * @param documentNumber Required. The document number of the user. + * @param deviceLink Required. The device link URL generated for device link flow + * @param flowType Required. Supported values QR, Web2App and App2App + * @param browserCookie Required for Web2App and App2App flows. The browser cookie value for the session. + * @param initialCallbackUrl Required for Web2App and App2App flows. The initial callback URL to which the user will be redirected. + */ +public record DeviceLinkMockRequest(String documentNumber, + String deviceLink, + String flowType, + @JsonInclude(JsonInclude.Include.NON_EMPTY) String browserCookie, + @JsonInclude(JsonInclude.Include.NON_EMPTY) String initialCallbackUrl) { +} diff --git a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java index 3fd05428..4043f1e6 100644 --- a/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java +++ b/src/test/java/ee/sk/smartid/integration/ReadmeIntegrationTest.java @@ -33,6 +33,9 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.security.KeyStoreException; @@ -40,12 +43,12 @@ import java.security.cert.CertificateException; import java.time.Duration; import java.time.Instant; -import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; @@ -104,10 +107,14 @@ import ee.sk.smartid.signature.SignatureValueValidator; import ee.sk.smartid.signature.SignatureValueValidatorImpl; import ee.sk.smartid.util.CallbackUrlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @SmartIdDemoIntegrationTest public class ReadmeIntegrationTest { + private static final Logger logger = LoggerFactory.getLogger(ReadmeIntegrationTest.class); + private static final Pattern NUMERIC_PATTERN = Pattern.compile("^[0-9]{4}$"); private SmartIdClient smartIdClient; @@ -123,15 +130,16 @@ void setUp() { smartIdClient.setTrustStore(keyStore); } - @Disabled("Testing with device-link demo accounts is not possible at the moment") @Nested class DeviceLinkBasedExamples { @Nested class Authentication { - @Test - void anonymousAuthentication_withApp2App() { + @Disabled("Testing with App2App and Web2App is not possible at the moment") + @ParameterizedTest + @EnumSource(value = DeviceLinkType.class, names = {"APP_2_APP", "WEB_2_APP"}) + void anonymousAuthentication_with_App2App_or_Web2App(DeviceLinkType deviceLinkType) throws IOException, InterruptedException { // For security reasons a new hash value must be created for each new authentication request String rpChallenge = RpChallengeGenerator.generate().toBase64EncodedValue(); // Store generated rpChallenge only on backend side. Do not expose it to the client side. @@ -167,15 +175,12 @@ void anonymousAuthentication_withApp2App() { // Will be used to calculate elapsed time being used in device link and in authCode Instant responseReceivedAt = authenticationSessionResponse.receivedAt(); - // Next steps: - // - Generate QR-code or device link to be displayed to the user using sessionToken, sessionSecret and receivedAt provided in the authenticationResponse - // - Start querying sessions status - // Build the device link URI (without the authCode parameter) - // This base URI will be used for QR code or App2App flows + // This base URI will be used for App2App or Web2App flows URI deviceLink = smartIdClient.createDynamicContent() + .withSchemeName("smart-id-demo") .withDeviceLinkBase(deviceLinkBase.toString()) - .withDeviceLinkType(DeviceLinkType.APP_2_APP) + .withDeviceLinkType(deviceLinkType) .withSessionType(SessionType.AUTHENTICATION) .withSessionToken(sessionToken) .withDigest(rpChallenge) @@ -184,6 +189,18 @@ void anonymousAuthentication_withApp2App() { .withInteractions(authenticationSessionRequest.interactions()) .buildDeviceLink(sessionSecret); + // In real application user is routed to Smart-ID app and enters PIN 1 there + // In this test submit device link to the Mock Service so it simulates the user completing the flow (see device_link_test_endpoint in Smart-ID documentation) + submitDeviceLinkToMockService( + new DeviceLinkMockRequest( + "PNOEE-40404040009-MOCK-Q", + deviceLink.toString(), + deviceLinkType.getValue(), + "a=b;c=d", + callbackUrl.initialCallbackUri().toString() + ) + ); + // Use the sessionId from the authentication session response to poll for session status updates SessionStatusPoller poller = smartIdClient.getSessionStatusPoller(); SessionStatus sessionStatus = poller.fetchFinalSessionStatus(sessionId); @@ -212,20 +229,20 @@ void anonymousAuthentication_withApp2App() { queryParameters.get("userChallengeVerifier"), "smart-id-demo"); - assertEquals("40504040001", authenticationIdentity.getIdentityCode()); + assertEquals("40404040009", authenticationIdentity.getIdentityCode()); assertEquals("OK", authenticationIdentity.getGivenName()); - assertEquals("TESTNUMBER", authenticationIdentity.getSurname()); - assertEquals("LT", authenticationIdentity.getCountry()); + assertEquals("TEST", authenticationIdentity.getSurname()); + assertEquals("EE", authenticationIdentity.getCountry()); } @Test - void authentication_withSemanticIdentifierAndQrCode() { + void authentication_withSemanticIdentifierAndQrCode() throws IOException, InterruptedException { var semanticsIdentifier = new SemanticsIdentifier( // 3 character identity type // (PAS-passport, IDC-national identity card or PNO - (national) personal number) SemanticsIdentifier.IdentityType.PNO, SemanticsIdentifier.CountryCode.EE, // 2 character ISO 3166-1 alpha-2 country code - "40504040001"); // identifier (according to country and identity type reference) + "40404040009"); // identifier (according to country and identity type reference) // For security reasons a new rpChallenge must be created for each new authentication request String rpChallenge = RpChallengeGenerator.generate().toBase64EncodedValue(); @@ -256,15 +273,12 @@ void authentication_withSemanticIdentifierAndQrCode() { // Will be used to calculate elapsed time being used in device link Instant responseReceivedAt = authenticationSessionResponse.receivedAt(); - // Next steps: - // - Generate QR-code or device link to be displayed to the user using sessionToken, sessionSecret and receivedAt provided in the authenticationResponse - // - Start querying sessions status - // Calculate elapsed seconds from response received time long elapsedSeconds = Duration.between(responseReceivedAt, Instant.now()).getSeconds(); // Build the device link URI (without the authCode parameter) // This base URI will be used for QR code or App2App flows URI deviceLink = smartIdClient.createDynamicContent() + .withSchemeName("smart-id-demo") .withDeviceLinkBase(deviceLinkBase.toString()) .withDeviceLinkType(DeviceLinkType.QR_CODE) .withSessionType(SessionType.AUTHENTICATION) @@ -274,16 +288,25 @@ void authentication_withSemanticIdentifierAndQrCode() { .withInteractions(authenticationSessionRequest.interactions()) .withLang("est") .buildDeviceLink(sessionSecret); - // Return URI to be used with QR-code generation library on the frontend side + + // In real application return URI to be used with QR-code generation library on the frontend side // or create QR-code data-URI from device link and return that to the client side String dataUri = QrCodeGenerator.generateDataUri(deviceLink.toString()); + // In this test submit device link to the Mock Service so it simulates the user scanning the QR and completing the flow + submitDeviceLinkToMockService(new DeviceLinkMockRequest( + "PNOEE-40404040009-MOCK-Q", + deviceLink.toString(), + DeviceLinkType.QR_CODE.getValue(), + "", + "")); + // Use sessionId to poll for session status updates SessionStatusPoller poller = smartIdClient.getSessionStatusPoller(); SessionStatus sessionStatus = poller.fetchFinalSessionStatus(sessionId); // The session can have states such as RUNNING or COMPLETE. Check that the session has completed successfully. - assertEquals("COMPLETED", sessionStatus.getState()); + assertEquals("COMPLETE", sessionStatus.getState()); // Validate the response and return user's identity TrustedCACertStore trustedCaCertStore = new FileTrustedCAStoreBuilder().build(); @@ -291,15 +314,15 @@ void authentication_withSemanticIdentifierAndQrCode() { AuthenticationIdentity authenticationIdentity = DeviceLinkAuthenticationResponseValidator.defaultSetupWithCertificateValidator(certificateValidator) .validate(sessionStatus, authenticationSessionRequest, null, "smart-id-demo"); - assertEquals("40504040001", authenticationIdentity.getIdentityCode()); + assertEquals("40404040009", authenticationIdentity.getIdentityCode()); assertEquals("OK", authenticationIdentity.getGivenName()); - assertEquals("TESTNUMBER", authenticationIdentity.getSurname()); + assertEquals("TEST", authenticationIdentity.getSurname()); assertEquals("EE", authenticationIdentity.getCountry()); } @Test - void authentication_withDocumentNumberAndQrCode() { - String documentNumber = "PNOLT-40504040001-MOCK-Q"; + void authentication_withDocumentNumberAndQrCode() throws IOException, InterruptedException { + String documentNumber = "PNOEE-40404040009-MOCK-Q"; // For security reasons a new rpChallenge must be created for each new authentication request String rpChallenge = RpChallengeGenerator.generate().toBase64EncodedValue(); @@ -331,20 +354,29 @@ void authentication_withDocumentNumberAndQrCode() { // Generate the base (unprotected) device link URI, which does not yet include the authCode long elapsedSeconds = Duration.between(responseReceivedAt, Instant.now()).getSeconds(); URI deviceLink = smartIdClient.createDynamicContent() + .withSchemeName("smart-id-demo") .withDeviceLinkBase(deviceLinkBase.toString()) .withDeviceLinkType(DeviceLinkType.QR_CODE) .withSessionType(SessionType.AUTHENTICATION) .withSessionToken(sessionToken) .withDigest(rpChallenge) - .withRelyingPartyName(Base64.getEncoder().encodeToString(smartIdClient.getRelyingPartyName().getBytes(StandardCharsets.UTF_8))) .withElapsedSeconds(elapsedSeconds) .withInteractions(authenticationSessionRequest.interactions()) .withLang("est") .buildDeviceLink(sessionSecret); - // Return URI to be used with QR-code generation library on the frontend side + + // In real application return URI to be used with QR-code generation library on the frontend side // or create QR-code data-URI from device link and return that to the client side String dataUri = QrCodeGenerator.generateDataUri(deviceLink.toString()); + // In this test submit device link to the Mock Service so it simulates the user scanning the QR and completing the flow + submitDeviceLinkToMockService(new DeviceLinkMockRequest( + documentNumber, + deviceLink.toString(), + DeviceLinkType.QR_CODE.getValue(), + "", + "")); + // Use sessionId to poll for session status updates SessionStatusPoller poller = smartIdClient.getSessionStatusPoller(); SessionStatus sessionStatus = poller.fetchFinalSessionStatus(sessionId); @@ -358,9 +390,9 @@ void authentication_withDocumentNumberAndQrCode() { AuthenticationIdentity authenticationIdentity = DeviceLinkAuthenticationResponseValidator.defaultSetupWithCertificateValidator(certificateValidator) .validate(sessionStatus, authenticationSessionRequest, null, "smart-id-demo"); - assertEquals("40504040001", authenticationIdentity.getIdentityCode()); + assertEquals("40404040009", authenticationIdentity.getIdentityCode()); assertEquals("OK", authenticationIdentity.getGivenName()); - assertEquals("TESTNUMBER", authenticationIdentity.getSurname()); + assertEquals("TEST", authenticationIdentity.getSurname()); assertEquals("EE", authenticationIdentity.getCountry()); } } @@ -368,9 +400,10 @@ void authentication_withDocumentNumberAndQrCode() { @Nested class Signature { - @Test - void signature_withDocumentNumberAndQRCode() { - String documentNumber = "PNOLT-40504040001-MOCK-Q"; + @ParameterizedTest + @EnumSource(SigningSignatureAlgorithm.class) + void signature_withDocumentNumberAndQRCode(SigningSignatureAlgorithm signatureAlgorithm) throws IOException, InterruptedException { + String documentNumber = "PNOEE-40404040009-MOCK-Q"; CertificateByDocumentNumberResult certResponse = smartIdClient .createCertificateByDocumentNumber() @@ -381,7 +414,8 @@ void signature_withDocumentNumberAndQRCode() { // DataToSign dataToSign = toDataToSign(container,certResponse.certificate()); // Create the signable data from DataToSign - var signableData = new SignableData("dataToSign".getBytes(), HashAlgorithm.SHA_256); + HashAlgorithm hashAlgorithm = signatureAlgorithm.isLegacyRsa() ? signatureAlgorithm.getHashAlgorithmForLegacy() : HashAlgorithm.SHA_512; + var signableData = new SignableData("dataToSign".getBytes(), hashAlgorithm); // Build the device link signature request List signatureInteractions = List.of(DeviceLinkInteraction.displayTextAndPin("Please sign the document")); @@ -389,7 +423,8 @@ void signature_withDocumentNumberAndQRCode() { .withCertificateLevel(CertificateLevel.QSCD) .withSignableData(signableData) .withDocumentNumber(documentNumber) - .withInteractions(signatureInteractions); + .withInteractions(signatureInteractions) + .withSignatureAlgorithm(signatureAlgorithm); DeviceLinkSessionResponse signatureSessionResponse = deviceLinkSignatureSessionRequestBuilder.initSignatureSession(); // Get SignatureSessionRequest after the request is made and store for validations DeviceLinkSignatureSessionRequest deviceLinkSignatureSessionRequest = deviceLinkSignatureSessionRequestBuilder.getSignatureSessionRequest(); @@ -402,9 +437,6 @@ void signature_withDocumentNumberAndQRCode() { Instant receivedAt = signatureSessionResponse.receivedAt(); URI deviceLinkBase = signatureSessionResponse.deviceLinkBase(); - // Generate QR-code or device link to be displayed to the user using sessionToken, sessionSecret and receivedAt provided in the signatureSessionResponse - // Start querying sessions status - // Calculate elapsed seconds from response received time long elapsedSeconds = Duration.between(receivedAt, Instant.now()).getSeconds(); // Generate auth code @@ -414,21 +446,29 @@ void signature_withDocumentNumberAndQRCode() { .withDeviceLinkType(DeviceLinkType.QR_CODE) .withSessionType(SessionType.SIGNATURE) .withSessionToken(sessionToken) - .withRelyingPartyName(Base64.getEncoder().encodeToString(smartIdClient.getRelyingPartyName().getBytes(StandardCharsets.UTF_8))) .withElapsedSeconds(elapsedSeconds) .withLang("est") .withInteractions(deviceLinkSignatureSessionRequest.interactions()) + .withDigest(deviceLinkSignatureSessionRequest.signatureProtocolParameters().digest()) .buildDeviceLink(sessionSecret); - // Return URI to be used with QR-code generation library on the frontend side + // In real application return URI to be used with QR-code generation library on the frontend side // or create QR-code data-URI from device link and return that to the client side String dataUri = QrCodeGenerator.generateDataUri(deviceLink.toString()); + // In this test submit device link to the Mock Service so it simulates the user scanning the QR and completing the flow + submitDeviceLinkToMockService(new DeviceLinkMockRequest( + documentNumber, + deviceLink.toString(), + DeviceLinkType.QR_CODE.getValue(), + "", + "")); + // Get the session status poller SessionStatusPoller poller = smartIdClient.getSessionStatusPoller(); // Get signatureSessionId from current session response and poll for session status SessionStatus signatureSessionStatus = poller.fetchFinalSessionStatus(signatureSessionId); - // Session can have two states RUNNING or COMPLETED, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) + // Session can have two states RUNNING or COMPLETE, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) assertEquals("COMPLETE", signatureSessionStatus.getState()); TrustedCACertStore trustedCaCertStore = new FileTrustedCAStoreBuilder().build(); @@ -448,21 +488,22 @@ void signature_withDocumentNumberAndQRCode() { signatureFactory); assertEquals("OK", signatureResponse.getEndResult()); - assertEquals("PNOLT-40504040001-MOCK-Q", signatureResponse.getDocumentNumber()); + assertEquals(documentNumber, signatureResponse.getDocumentNumber()); assertEquals(CertificateLevel.QUALIFIED, signatureResponse.getCertificateLevel()); assertEquals(CertificateLevel.QUALIFIED, signatureResponse.getRequestedCertificateLevel()); assertEquals("displayTextAndPIN", signatureResponse.getInteractionFlowUsed()); assertNotNull(signatureResponse.getCertificate()); } - @Test - void signature_withSemanticIdentifier() { + @ParameterizedTest + @EnumSource(SigningSignatureAlgorithm.class) + void signature_withSemanticIdentifier(SigningSignatureAlgorithm signatureAlgorithm) throws IOException, InterruptedException { var semanticIdentifier = new SemanticsIdentifier( // 3 character identity type // (PAS-passport, IDC-national identity card or PNO - (national) personal number) SemanticsIdentifier.IdentityType.PNO, SemanticsIdentifier.CountryCode.EE, // 2 character ISO 3166-1 alpha-2 country code - "40504040001"); // identifier (according to country and identity type reference) + "40404040009"); // identifier (according to country and identity type reference) NotificationCertificateChoiceSessionResponse certificateChoiceSessionResponse = smartIdClient .createNotificationCertificateChoice() @@ -487,22 +528,17 @@ void signature_withSemanticIdentifier() { // DataToSign dataToSign = toDataToSign(container,certResponse.certificate()); // Create the signable data - var signableData = new SignableData("dataToSign".getBytes(), HashAlgorithm.SHA_512); - - var semanticsIdentifier = new SemanticsIdentifier( - // 3 character identity type - // (PAS-passport, IDC-national identity card or PNO - (national) personal number) - SemanticsIdentifier.IdentityType.PNO, - SemanticsIdentifier.CountryCode.EE, // 2 character ISO 3166-1 alpha-2 country code - "40504040001"); // identifier (according to country and identity type reference) + HashAlgorithm hashAlgorithm = signatureAlgorithm.isLegacyRsa() ? signatureAlgorithm.getHashAlgorithmForLegacy() : HashAlgorithm.SHA3_512; + var signableData = new SignableData("dataToSign".getBytes(), hashAlgorithm); // Build the device link signature request List signatureInteractions = List.of(DeviceLinkInteraction.displayTextAndPin("Please sign the document")); DeviceLinkSignatureSessionRequestBuilder deviceLinkSignatureSessionRequestBuilder = smartIdClient.createDeviceLinkSignature() .withCertificateLevel(CertificateLevel.QUALIFIED) .withSignableData(signableData) - .withSemanticsIdentifier(semanticsIdentifier) - .withInteractions(signatureInteractions); + .withSemanticsIdentifier(semanticIdentifier) + .withInteractions(signatureInteractions) + .withSignatureAlgorithm(signatureAlgorithm); // Init signature session DeviceLinkSessionResponse signatureSessionResponse = deviceLinkSignatureSessionRequestBuilder.initSignatureSession(); @@ -517,29 +553,38 @@ void signature_withSemanticIdentifier() { String sessionSecret = signatureSessionResponse.sessionSecret(); Instant receivedAt = signatureSessionResponse.receivedAt(); - // Generate QR-code or device link to be displayed to the user using sessionToken, sessionSecret and receivedAt provided in the signatureSessionResponse - // Start querying sessions status - // Calculate elapsed seconds from response received time long elapsedSeconds = Duration.between(receivedAt, Instant.now()).getSeconds(); // Generate auth code URI deviceLink = smartIdClient.createDynamicContent() - .withDeviceLinkBase("smartid://") + .withSchemeName("smart-id-demo") + .withDeviceLinkBase(signatureSessionResponse.deviceLinkBase().toString()) .withDeviceLinkType(DeviceLinkType.QR_CODE) .withSessionType(SessionType.SIGNATURE) .withSessionToken(sessionToken) - .withRelyingPartyName(Base64.getEncoder().encodeToString(smartIdClient.getRelyingPartyName().getBytes(StandardCharsets.UTF_8))) .withElapsedSeconds(elapsedSeconds) .withLang("est") .withInteractions(deviceLinkSignatureSessionRequest.interactions()) // interactions string must be the same as in the signature session request + .withDigest(deviceLinkSignatureSessionRequest.signatureProtocolParameters().digest()) .buildDeviceLink(sessionSecret); - // Display QR-code to the user + + // In real application return URI to be used with QR-code generation library on the frontend side + // or create QR-code data-URI from device link and return that to the client side + String dataUri = QrCodeGenerator.generateDataUri(deviceLink.toString()); + + // In this test submit device link to the Mock Service so it simulates the user scanning the QR and completing the flow + submitDeviceLinkToMockService(new DeviceLinkMockRequest( + certificateChoiceResponse.getDocumentNumber(), + deviceLink.toString(), + DeviceLinkType.QR_CODE.getValue(), + "", + "")); // Get the session status poller poller = smartIdClient.getSessionStatusPoller(); // Get signatureSessionId from current session response and poll for session status SessionStatus signatureSessionStatus = poller.fetchFinalSessionStatus(signatureSessionId); - // Session can have two states RUNNING or COMPLETED, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) + // Session can have two states RUNNING or COMPLETE, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) assertEquals("COMPLETE", signatureSessionStatus.getState()); // Validate signature response @@ -557,7 +602,7 @@ void signature_withSemanticIdentifier() { signatureFactory); assertEquals("OK", signatureResponse.getEndResult()); - assertEquals("PNOLT-40504040001-MOCK-Q", signatureResponse.getDocumentNumber()); + assertEquals(certificateChoiceResponse.getDocumentNumber(), signatureResponse.getDocumentNumber()); assertEquals(CertificateLevel.QUALIFIED, signatureResponse.getCertificateLevel()); assertEquals(CertificateLevel.QUALIFIED, signatureResponse.getRequestedCertificateLevel()); assertEquals("displayTextAndPIN", signatureResponse.getInteractionFlowUsed()); @@ -772,7 +817,7 @@ void signature_withSemanticsIdentifier(SigningSignatureAlgorithm signatureAlgori // Get sessionID from current session response and poll for session status SessionStatus signatureSessionStatus = poller.fetchFinalSessionStatus(sessionID); - // Session can have two states RUNNING or COMPLETED, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) + // Session can have two states RUNNING or COMPLETE, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) assertEquals("COMPLETE", signatureSessionStatus.getState()); SignatureResponseValidator validator = new SignatureResponseValidator(certificateValidator); @@ -848,7 +893,7 @@ void signature_withDocumentNumber(SigningSignatureAlgorithm signatureAlgorithm) // Get sessionID from current session response and poll for session status SessionStatus signatureSessionStatus = poller.fetchFinalSessionStatus(signatureSessionId); - // Session can have two states RUNNING or COMPLETED, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) + // Session can have two states RUNNING or COMPLETE, check sessionStatus.getResult().getEndResult() for OK or error responses (f.e USER_REFUSED, TIMEOUT) assertEquals("COMPLETE", signatureSessionStatus.getState()); SignatureResponseValidator validator = new SignatureResponseValidator(certificateValidator); @@ -930,6 +975,7 @@ void signing_withQrCode() { // Build the device link URI // This base URI will be used for QR code or App2App flows URI deviceLink = smartIdClient.createDynamicContent() + .withSchemeName("smart-id-demo") .withDeviceLinkBase(deviceLinkBase.toString()) .withDeviceLinkType(DeviceLinkType.QR_CODE) .withSessionType(SessionType.CERTIFICATE_CHOICE) @@ -947,7 +993,7 @@ void signing_withQrCode() { SessionStatus certificateSessionStatus = poller.fetchFinalSessionStatus(certificateChoiceSessionId); // The session can have states such as RUNNING or COMPLETE. Check that the session has completed successfully. - assertEquals("COMPLETED", certificateSessionStatus.getState()); + assertEquals("COMPLETE", certificateSessionStatus.getState()); // Validate the certificate choice response CertificateValidatorImpl certificateValidator = new CertificateValidatorImpl(new FileTrustedCAStoreBuilder().build()); @@ -970,7 +1016,7 @@ void signing_withQrCode() { // Use sessionId to poll for signature session status updates SessionStatus signatureSessionStatus = poller.fetchFinalSessionStatus(signatureSessionResponse.sessionID()); - assertEquals("COMPLETED", signatureSessionStatus.getState()); + assertEquals("COMPLETE", signatureSessionStatus.getState()); // Validate signature response SignatureResponseValidator signatureResponseValidator = new SignatureResponseValidator(certificateValidator); @@ -980,6 +1026,40 @@ void signing_withQrCode() { } } + private static void submitDeviceLinkToMockService(DeviceLinkMockRequest deviceLinkMockRequest) throws IOException, InterruptedException { + var mapper = new ObjectMapper(); + String body = mapper.writeValueAsString(deviceLinkMockRequest); + + String url = "https://sid.demo.sk.ee/mock/device-link"; + + if (logger.isDebugEnabled()) { + logger.debug("POST {}", url); + } + if (logger.isTraceEnabled()) { + logger.trace("Request headers: {}", Collections.singletonMap("Content-Type", "application/json")); + logger.trace("Message body: {}", body); + } + + HttpClient client = HttpClient.newBuilder().build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + + if (logger.isDebugEnabled()) { + logger.debug("Response status: {}", response.statusCode()); + } + if (logger.isTraceEnabled()) { + logger.trace("Response body: {}", response.body()); + } + + if (response.statusCode() != 200) { + throw new RuntimeException("Mock device-link submission failed: " + response.statusCode() + " " + response.body()); + } + } + private static KeyStore getKeystore() { try (InputStream is = ReadmeIntegrationTest.class.getResourceAsStream("/demo_server_trusted_ssl_certs.jks")) { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index c05b1e63..a96c34e8 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -8,6 +8,7 @@ + From 5a8f496871c4a59aa634a4767942b846aff9d3d4 Mon Sep 17 00:00:00 2001 From: Kaido Hallik Date: Tue, 17 Mar 2026 15:14:38 +0200 Subject: [PATCH 36/36] Remove legacy-signature-algorithms from PR target branches where tests are running --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 716a69db..f0598fd2 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -4,7 +4,7 @@ on: push: branches: [ "master", "v3.1" ] pull_request: - branches: [ "master", "legacy-signature-algorithms" ] + branches: [ "master" ] permissions: contents: read