Skip to content

Commit a2dbe67

Browse files
committed
Merge branch 'RM-4086_verification_code' into 'SID'
RM-4086: verification code and interaction parameters See merge request cdoc2/cdoc2-java-ref-impl!98
2 parents de1e55b + d07d98b commit a2dbe67

25 files changed

+586
-92
lines changed

cdoc2-cli/src/main/java/ee/cyber/cdoc2/cli/commands/CDocDecryptCmd.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ private void setKeyServerPropertiesFile(String server) {
5656
System.setProperty(KEY_CAPSULES_PROPERTIES, keyServerPropertiesFile);
5757
}
5858

59-
6059
@CommandLine.Parameters(description = "one or more files to decrypt", paramLabel = "fileToExtract")
6160
private String[] filesToExtract = new String[0];
6261

cdoc2-cli/src/main/java/ee/cyber/cdoc2/cli/util/CDocDecryptionHelper.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import ee.cyber.cdoc2.crypto.PemTools;
1919
import ee.cyber.cdoc2.crypto.Pkcs11Tools;
2020
import ee.cyber.cdoc2.crypto.AuthenticationIdentifier;
21+
import ee.cyber.cdoc2.crypto.jwt.InteractionParams;
22+
import ee.cyber.cdoc2.crypto.jwt.InteractionParamsConfigurable;
2123
import ee.cyber.cdoc2.crypto.keymaterial.DecryptionKeyMaterial;
2224
import ee.cyber.cdoc2.crypto.keymaterial.LabeledPassword;
2325
import ee.cyber.cdoc2.crypto.keymaterial.LabeledSecret;
@@ -110,12 +112,12 @@ public static DecryptionKeyMaterial getDecryptionKeyMaterial(
110112
}
111113

112114
if (isWithSid && decryptionKm == null) {
113-
decryptionKm = getSidDecryptionKeyMaterial(decryptArguments.getSid());
115+
decryptionKm = getSidDecryptionKeyMaterial(decryptArguments.getSid(), cdocFile);
114116
}
115117

116118
if (isWithMid && decryptionKm == null) {
117119
decryptionKm = getMidDecryptionKeyMaterial(
118-
decryptArguments.getMid(), decryptArguments.getMidPhone()
120+
decryptArguments.getMid(), decryptArguments.getMidPhone(), cdocFile
119121
);
120122
}
121123

@@ -127,25 +129,49 @@ public static DecryptionKeyMaterial getDecryptionKeyMaterial(
127129
return decryptionKm;
128130
}
129131

130-
private static DecryptionKeyMaterial getSidDecryptionKeyMaterial(String idCode) {
132+
private static DecryptionKeyMaterial getSidDecryptionKeyMaterial(String idCode, File cdocFile) {
131133
AuthenticationIdentifier authIdentifier = AuthenticationIdentifier.forKeyShares(
132134
createSemanticsIdentifier(idCode), AuthenticationIdentifier.AuthenticationType.SID
133135
);
134136

135-
return DecryptionKeyMaterial.fromAuthMeans(authIdentifier);
137+
DecryptionKeyMaterial dkm = DecryptionKeyMaterial.fromAuthMeans(authIdentifier);
138+
addInteractionParameters(cdocFile, dkm);
139+
return dkm;
140+
136141
}
137142

143+
/**
144+
*
145+
* @param idCode estonian national identity code
146+
* @param phoneNumber user phone number international format +372...
147+
* @param cdocFile cdoc2 file decrypted
148+
* @return
149+
*/
138150
private static DecryptionKeyMaterial getMidDecryptionKeyMaterial(
139151
String idCode,
140-
String phoneNumber
152+
String phoneNumber,
153+
File cdocFile
141154
) {
142155

143156
AuthenticationIdentifier authIdentifier = AuthenticationIdentifier.forMidDecryption(
144157
createSemanticsIdentifier(idCode),
145158
getValidatedPhoneNumber(phoneNumber)
146159
);
147160

148-
return DecryptionKeyMaterial.fromAuthMeans(authIdentifier);
161+
DecryptionKeyMaterial dkm = DecryptionKeyMaterial.fromAuthMeans(authIdentifier);
162+
addInteractionParameters(cdocFile, dkm);
163+
return dkm;
164+
}
165+
166+
private static void addInteractionParameters(File cdocFile, DecryptionKeyMaterial dkm) {
167+
if (dkm instanceof InteractionParamsConfigurable paramsConfigurable) {
168+
169+
InteractionParams interactionParams = (cdocFile == null)
170+
? InteractionParams.displayTextAndPin()
171+
: InteractionParams.displayTextAndVCCForDocument(cdocFile.toPath().getFileName().toString());
172+
interactionParams.addAuthListener(e -> System.out.println("Verification code:" + e.getVerificationCode()));
173+
paramsConfigurable.init(interactionParams);
174+
}
149175
}
150176

151177
private static DecryptionKeyMaterial getPasswordDecryptionKeyMaterial(

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/mobileid/MobileIdClient.java

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package ee.cyber.cdoc2.client.mobileid;
22

3+
import ee.cyber.cdoc2.crypto.jwt.InteractionParams;
34
import ee.sk.mid.MidAuthentication;
45
import ee.sk.mid.MidClient;
6+
import ee.sk.mid.MidDisplayTextFormat;
57
import ee.sk.mid.MidHashToSign;
8+
import ee.sk.mid.MidLanguage;
69
import ee.sk.mid.rest.dao.request.MidAuthenticationRequest;
710

811
import java.io.IOException;
@@ -12,6 +15,7 @@
1215
import java.security.NoSuchAlgorithmException;
1316
import java.security.cert.CertificateException;
1417

18+
import jakarta.annotation.Nullable;
1519
import org.slf4j.Logger;
1620
import org.slf4j.LoggerFactory;
1721

@@ -30,9 +34,11 @@ public class MobileIdClient {
3034

3135
private static final String CERT_NOT_FOUND = "Mobile ID trusted SSL certificates not found";
3236

33-
private final MobileIdClientWrapper mobileIdClientWrapper;
37+
// protected to allow overriding MobileIdClient to allow more control over interaction
38+
protected final MobileIdClientWrapper mobileIdClientWrapper;
3439

35-
private final MobileIdClientConfiguration mobileIdClientConfig;
40+
// protected to allow overriding MobileIdClient to allow more control over interaction
41+
protected final MobileIdClientConfiguration mobileIdClientConfig;
3642

3743
/**
3844
* Constructor for Mobile-ID Client
@@ -42,35 +48,94 @@ public MobileIdClient(MobileIdClientConfiguration conf) {
4248
this.mobileIdClientConfig = conf;
4349
MidClient midClient = configureMobileIdClient();
4450
this.mobileIdClientWrapper = new MobileIdClientWrapper(midClient);
51+
52+
}
53+
54+
protected MobileIdClient(MobileIdClientConfiguration conf, MobileIdClientWrapper wrapper) {
55+
this.mobileIdClientConfig = conf;
56+
this.mobileIdClientWrapper = wrapper;
4557
}
4658

4759
/**
4860
* Authentication request to Mobile ID client. Returns raw MidAuthentication that contains MidSignature and signing
4961
* Certificate
5062
* @param userData user request data
5163
* @param authenticationHash Base64 encoded hash function output to be signed
64+
* @param interactionParams Optional parameters to drive user interaction and to get verification code.
65+
* {@code null} when not in use
5266
* @return MidAuthentication object that contains MidSignature and Certificate
5367
*/
5468
public MidAuthentication startAuthentication(
5569
MobileIdUserData userData,
56-
MidHashToSign authenticationHash
70+
MidHashToSign authenticationHash,
71+
@Nullable InteractionParams interactionParams
5772
) throws CdocMobileIdClientException {
5873

59-
// ToDo display verification code and text to the user in RM-4086
60-
//String verificationCode = authenticationHash.calculateVerificationCode();
61-
6274
MidAuthenticationRequest request = MidAuthenticationRequest.newBuilder()
6375
.withPhoneNumber(userData.phoneNumber())
6476
.withNationalIdentityNumber(userData.identityCode())
6577
.withHashToSign(authenticationHash)
66-
.withLanguage(mobileIdClientConfig.getDisplayTextLanguage())
67-
.withDisplayText(mobileIdClientConfig.getDisplayText())
68-
.withDisplayTextFormat(mobileIdClientConfig.getDisplayTextFormat())
78+
.withLanguage(getLanguage(interactionParams))
79+
.withDisplayText(getDisplayText(interactionParams))
80+
.withDisplayTextFormat(getEncoding(interactionParams))
6981
.build();
7082

7183
return mobileIdClientWrapper.authenticate(request, authenticationHash);
7284
}
7385

86+
/**
87+
* Get MID language from interactionParams if defined, otherwise get default value from configuration
88+
*/
89+
protected MidLanguage getLanguage(@Nullable InteractionParams interactionParams) {
90+
MidLanguage lang = mobileIdClientConfig.getDefaultDisplayTextLanguage();
91+
if (interactionParams != null) {
92+
String iLang = interactionParams.getLanguage();
93+
if (iLang != null) {
94+
try {
95+
lang = MidLanguage.valueOf(iLang);
96+
} catch (IllegalArgumentException e) {
97+
log.warn("Illegal MidLanguage value, using {}", lang, e);
98+
}
99+
}
100+
}
101+
return lang;
102+
}
103+
104+
/**
105+
* Get MidDisplayTextFormat from interactionParams if defined, otherwise get default value from configuration
106+
*/
107+
protected MidDisplayTextFormat getEncoding(@Nullable InteractionParams interactionParams) {
108+
MidDisplayTextFormat enc = mobileIdClientConfig.getDefaultDisplayTextFormat();
109+
if (interactionParams != null) {
110+
String iEnc = interactionParams.getEncoding();
111+
if (iEnc != null) {
112+
try {
113+
enc = MidDisplayTextFormat.valueOf(iEnc);
114+
} catch (IllegalArgumentException e) {
115+
log.warn("Illegal MidDisplayTextFormat value, using {}", enc, e);
116+
}
117+
}
118+
}
119+
120+
return enc;
121+
}
122+
123+
/** Get displayText from interactionParams if defined, otherwise get default value from configuration */
124+
protected String getDisplayText(@Nullable InteractionParams interactionParams) {
125+
126+
// Mobile-ID doesn't support interactionType and text length is limited to 100 bytes -
127+
// 50 chars for UCS2 and 100 chars for GSM7
128+
// https://github.com/SK-EID/MID?tab=readme-ov-file#323-request-parameters
129+
130+
String textAndPIN = mobileIdClientConfig.getDefaultDisplayText();
131+
if (interactionParams != null) {
132+
textAndPIN = (getEncoding(interactionParams) == MidDisplayTextFormat.GSM7)
133+
? interactionParams.getDisplayText(100) // GSM7
134+
: interactionParams.getDisplayText(50); // UCS2
135+
}
136+
return textAndPIN;
137+
}
138+
74139
/**
75140
* Mobile ID client configuration
76141
*/

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/smartid/SmartIdClient.java

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ee.cyber.cdoc2.client.smartid;
22

33
import ee.cyber.cdoc2.config.SmartIdClientConfiguration;
4+
import ee.cyber.cdoc2.crypto.jwt.InteractionParams;
45
import ee.sk.smartid.AuthenticationIdentity;
56
import ee.sk.smartid.AuthenticationHash;
67
import ee.sk.smartid.SmartIdAuthenticationResponse;
@@ -11,13 +12,18 @@
1112
import ee.sk.smartid.exception.useraction.SessionTimeoutException;
1213
import ee.sk.smartid.exception.useraction.UserRefusedException;
1314
import ee.sk.smartid.exception.useraction.UserSelectedWrongVerificationCodeException;
15+
import ee.sk.smartid.rest.dao.Interaction;
1416
import ee.sk.smartid.rest.dao.SemanticsIdentifier;
1517

18+
import jakarta.annotation.Nullable;
1619
import org.slf4j.Logger;
1720
import org.slf4j.LoggerFactory;
1821

1922
import ee.cyber.cdoc2.exceptions.CdocSmartIdClientException;
2023

24+
import java.util.List;
25+
26+
import static ee.cyber.cdoc2.crypto.jwt.InteractionParams.InteractionType.DISPLAY_TEXT_AND_PIN;
2127

2228
/**
2329
* Client for communicating with the Smart ID client API.
@@ -38,18 +44,22 @@ public SmartIdClient(SmartIdClientConfiguration conf) {
3844
* @param authenticationHash Base64 encoded hash function output to be signed
3945
* @param certificationLevel Level of certificate requested, can either be
4046
* {@code QUALIFIED} or {@code ADVANCED}
47+
* @param interactionParams Optional parameters to drive user interaction and to get verification code.
48+
* {@code null} when not in use
4149
* @return SmartIdAuthenticationResponse object
4250
*/
4351
public SmartIdAuthenticationResponse authenticate(
4452
SemanticsIdentifier semanticsIdentifier,
4553
AuthenticationHash authenticationHash,
46-
String certificationLevel
54+
String certificationLevel,
55+
@Nullable InteractionParams interactionParams
4756
) throws CdocSmartIdClientException {
4857
String errorMsg = "Failed to authenticate Smart ID client request for " + semanticsIdentifier.getIdentifier();
4958

5059
try {
5160
return smartIdClientWrapper.authenticate(
52-
semanticsIdentifier, authenticationHash, certificationLevel
61+
semanticsIdentifier, authenticationHash, certificationLevel,
62+
getSIDInteractions(semanticsIdentifier, authenticationHash, certificationLevel, interactionParams)
5363
);
5464
} catch (UserAccountNotFoundException ex) {
5565
throw logNoUserAccountErrorAndThrow(errorMsg);
@@ -64,6 +74,49 @@ public SmartIdAuthenticationResponse authenticate(
6474
}
6575
}
6676

77+
/**
78+
* Convert cdoc2 specific InteractionParams to Smart-ID Interaction list. Has same parameters as authenticate, so
79+
* that this method can be overridden
80+
* @param semanticsIdentifier ETSI semantics identifier
81+
* @param authenticationHash Base64 encoded hash function output to be signed
82+
* @param certificationLevel Level of certificate requested, can either be
83+
* {@code QUALIFIED} or {@code ADVANCED}
84+
* @param interactionParams generic InteractionParams that is used to create Smart-ID {@code Interaction} list
85+
* @return list of SID Interaction objects
86+
*/
87+
protected List<Interaction> getSIDInteractions(SemanticsIdentifier semanticsIdentifier,
88+
AuthenticationHash authenticationHash,
89+
String certificationLevel,
90+
@Nullable InteractionParams interactionParams
91+
) {
92+
String displayText200 = (interactionParams == null)
93+
? InteractionParams.DEFAULT_DISPLAY_TEXT
94+
: interactionParams.getDisplayText200();
95+
96+
String displayText60 = (interactionParams == null)
97+
? InteractionParams.DEFAULT_DISPLAY_TEXT
98+
: interactionParams.getDisplayText60();
99+
100+
var interactionType = (interactionParams == null)
101+
? DISPLAY_TEXT_AND_PIN
102+
: interactionParams.getInteractionType();
103+
104+
switch (interactionType) {
105+
case DISPLAY_TEXT_AND_PIN:
106+
return List.of(Interaction.displayTextAndPIN(displayText60));
107+
case VERIFICATION_CODE_CHOICE:
108+
return List.of(Interaction.verificationCodeChoice(displayText60));
109+
case CONFIRMATION_MESSAGE:
110+
return List.of(Interaction.confirmationMessage(displayText200));
111+
case CONFIRMATION_MESSAGE_AND_VERIFICATION_CODE_CHOICE:
112+
return List.of(Interaction.confirmationMessageAndVerificationCodeChoice(displayText200));
113+
default:
114+
log.error("Unknown interaction type {}", interactionType);
115+
return List.of(Interaction.displayTextAndPIN(displayText60));
116+
}
117+
}
118+
119+
67120
public AuthenticationIdentity validateResponse(SmartIdAuthenticationResponse authResponse)
68121
throws CdocSmartIdClientException {
69122
return smartIdClientWrapper.validateResponse(authResponse);

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/smartid/SmartIdClientWrapper.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.security.NoSuchAlgorithmException;
2121
import java.security.cert.CertificateException;
2222
import java.security.cert.X509Certificate;
23-
import java.util.Collections;
2423
import java.util.Enumeration;
2524
import java.util.LinkedList;
2625
import java.util.List;
@@ -29,7 +28,8 @@
2928
import ee.cyber.cdoc2.exceptions.ConfigurationLoadingException;
3029
import ee.cyber.cdoc2.exceptions.CdocSmartIdClientException;
3130
import ee.cyber.cdoc2.util.Resources;
32-
31+
import org.slf4j.Logger;
32+
import org.slf4j.LoggerFactory;
3333

3434
/**
3535
* Smart-ID Client
@@ -42,6 +42,7 @@ public class SmartIdClientWrapper {
4242
private final SmartIdClientConfiguration smartIdClientConfig;
4343
private final AuthenticationResponseValidator authenticationResponseValidator;
4444

45+
private static final Logger log = LoggerFactory.getLogger(SmartIdClientWrapper.class);
4546
/**
4647
* Constructor for Smart-ID Client wrapper
4748
* @param conf Smart-ID client configuration
@@ -75,12 +76,14 @@ private static SmartIdClient configureSmartIdClient(SmartIdClientConfiguration c
7576
* @param authenticationHash Base64 encoded hash function output to be signed
7677
* @param certificationLevel Level of certificate requested, can either be
7778
* {@code QUALIFIED} or {@code ADVANCED}
79+
* @param interactions interaction parameters used to drive Smart-ID UI
7880
* @return SmartIdAuthenticationResponse object
7981
*/
8082
public SmartIdAuthenticationResponse authenticate(
8183
SemanticsIdentifier semanticsIdentifier,
8284
AuthenticationHash authenticationHash,
83-
String certificationLevel
85+
String certificationLevel,
86+
List<Interaction> interactions
8487
) throws UserAccountNotFoundException,
8588
UserRefusedException,
8689
UserSelectedWrongVerificationCodeException,
@@ -94,10 +97,7 @@ public SmartIdAuthenticationResponse authenticate(
9497
.withSemanticsIdentifier(semanticsIdentifier)
9598
.withAuthenticationHash(authenticationHash)
9699
.withCertificateLevel(certificationLevel)
97-
// Smart-ID app will display verification code to the user and user must insert PIN1
98-
.withAllowedInteractionsOrder(
99-
Collections.singletonList(Interaction.displayTextAndPIN("Log in to self-service?"))
100-
)
100+
.withAllowedInteractionsOrder(interactions)
101101
// Commented out as EIDPRX fails request parsing when this property is present
102102
//.withShareMdClientIpAddress(true)
103103
.authenticate();
@@ -107,6 +107,7 @@ public SmartIdAuthenticationResponse authenticate(
107107
return authResponse;
108108
}
109109

110+
110111
public AuthenticationIdentity validateResponse(
111112
SmartIdAuthenticationResponse authResponse
112113
) throws CdocSmartIdClientException {

0 commit comments

Comments
 (0)