Skip to content

Commit cbe44de

Browse files
author
Olesja Aarma
committed
Merge branch 'RM-4444_mobile-id_encrypt' into 'SID'
RM-4444: implement encryption using MID in lib See merge request cdoc2/cdoc2-java-ref-impl!86
2 parents 28d9b99 + 9f700ea commit cbe44de

File tree

8 files changed

+200
-82
lines changed

8 files changed

+200
-82
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ public static DecryptionKeyMaterial getDecryptionKeyMaterial(
120120
}
121121

122122
private static DecryptionKeyMaterial getSidDecryptionKeyMaterial(String idCode) {
123-
SemanticIdentification semanticIdentifier = SemanticIdentification.forSid(idCode);
123+
SemanticIdentification semanticIdentifier = SemanticIdentification
124+
.forKeyShares(idCode, SemanticIdentification.AuthenticationType.SID);
124125

125126
return DecryptionKeyMaterial.fromAuthMeans(semanticIdentifier);
126127
}

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

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import java.util.regex.Matcher;
44
import java.util.regex.Pattern;
55

6+
import static ee.cyber.cdoc2.util.IdCodeValidationUtil.getValidatedIdentityCode;
7+
68

79
/**
8-
* Uuser data for Mobile-ID authentication request
10+
* User data for Mobile-ID authentication request
911
* @param phoneNumber user phone number
1012
* @param identityCode user national identity number
1113
*/
@@ -15,16 +17,14 @@ public record MobileIdUserData(
1517
) {
1618

1719
private static final String PHONE_NUMBER_PATTERN = "\\+37\\d{5,10}";
18-
private static final String IDENTITY_CODE_PATTERN = "\\d{11}";
1920
private static final Pattern phoneNrPpattern = Pattern.compile(PHONE_NUMBER_PATTERN);
20-
private static final Pattern idPattern = Pattern.compile(IDENTITY_CODE_PATTERN);
2121

2222
public MobileIdUserData(String phoneNumber, String identityCode) {
23-
this.phoneNumber = validatePhoneNumber(phoneNumber);
24-
this.identityCode = validateIdentityCode(identityCode);
23+
this.phoneNumber = getValidatedPhoneNumber(phoneNumber);
24+
this.identityCode = getValidatedIdentityCode(identityCode);
2525
}
2626

27-
private String validatePhoneNumber(String phoneNr) {
27+
private String getValidatedPhoneNumber(String phoneNr) {
2828
Matcher matcher = phoneNrPpattern.matcher(phoneNr);
2929
if (!matcher.matches()) {
3030
throw new IllegalArgumentException("Invalid phone number: " + phoneNr);
@@ -33,13 +33,4 @@ private String validatePhoneNumber(String phoneNr) {
3333
return phoneNr;
3434
}
3535

36-
private String validateIdentityCode(String idCode) {
37-
Matcher matcher = idPattern.matcher(idCode);
38-
if (!matcher.matches()) {
39-
throw new IllegalArgumentException("Invalid identity number: " + idCode);
40-
}
41-
42-
return idCode;
43-
}
44-
4536
}

cdoc2-lib/src/main/java/ee/cyber/cdoc2/crypto/SemanticIdentification.java

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,27 @@
22

33
import ee.sk.smartid.rest.dao.SemanticsIdentifier;
44

5+
import static ee.cyber.cdoc2.util.IdCodeValidationUtil.getValidatedIdentityCode;
6+
57

68
/**
79
* Identification for {@link ee.cyber.cdoc2.crypto.KeyShareRecipientType#SID_MID}
810
*/
911
public class SemanticIdentification {
1012

13+
private static final String COLON_SEPARATOR = ":";
1114
public static final String ETSI_IDENTIFIER_PREFIX = "etsi/";
1215
private static final String ETSI_IDENTIFIER_SEPARATOR = "'";
1316

1417
private final String identifier;
1518

1619
/**
17-
* Identifiers for Smart ID.
20+
* Identifiers for Smart ID or Mobile ID.
1821
* @param authType authentication method
1922
* @param etsiIdentifier for natural person identifier (ETSI identifier)
2023
*/
2124
protected SemanticIdentification(AuthenticationType authType, SemanticsIdentifier etsiIdentifier) {
22-
this.identifier = authType + ":" + etsiIdentifier;
23-
}
24-
25-
/**
26-
* Identifiers for Mobile ID.
27-
* @param authType authentication method
28-
* @param etsiIdentifier for natural person identifier (ETSI identifier)
29-
* @param mobileNumber mobile number
30-
*/
31-
protected SemanticIdentification(
32-
AuthenticationType authType,
33-
SemanticsIdentifier etsiIdentifier,
34-
String mobileNumber
35-
) {
36-
this.identifier = authType + ":" + etsiIdentifier + ":" + mobileNumber;
25+
this.identifier = authType + COLON_SEPARATOR + etsiIdentifier;
3726
}
3827

3928
public String getIdentifier() {
@@ -51,18 +40,12 @@ public SemanticIdentification fromString(String semanticIdentification) {
5140
SemanticsIdentifier semanticsIdentifier
5241
= new SemanticsIdentifier(extractEtsiIdentifier(semanticIdentification));
5342

54-
switch (authType) {
55-
case SID -> {
56-
return new SemanticIdentification(authType, semanticsIdentifier);
57-
}
58-
case MID -> {
59-
// ToDo add mobile number here
60-
return new SemanticIdentification(authType, semanticsIdentifier, "mobile_number");
61-
}
62-
default -> throw new IllegalStateException(
63-
"Unexpected authentication type: " + authTypeString
64-
);
43+
if (authType == AuthenticationType.SID || authType == AuthenticationType.MID) {
44+
return new SemanticIdentification(authType, semanticsIdentifier);
6545
}
46+
throw new IllegalStateException(
47+
"Unexpected authentication type: " + authTypeString
48+
);
6649
}
6750

6851
/**
@@ -98,28 +81,16 @@ public static AuthenticationType of(String type) {
9881
/**
9982
* Convert person identification code into semantic identifier format for Smart ID encryption
10083
* */
101-
public static SemanticIdentification forSid(String identificationCode) {
102-
SemanticsIdentifier etsiIdentifier = new SemanticsIdentifier(
103-
SemanticsIdentifier.IdentityType.PNO,
104-
SemanticsIdentifier.CountryCode.EE,
105-
identificationCode
106-
);
107-
return new SemanticIdentification(AuthenticationType.SID, etsiIdentifier);
108-
}
109-
110-
/**
111-
* Convert person identification code into semantic identifier format for Mobile ID encryption
112-
* */
113-
public static SemanticIdentification forMid(String identificationCode) {
84+
public static SemanticIdentification forKeyShares(
85+
String identificationCode, AuthenticationType authType
86+
) {
87+
getValidatedIdentityCode(identificationCode);
11488
SemanticsIdentifier etsiIdentifier = new SemanticsIdentifier(
11589
SemanticsIdentifier.IdentityType.PNO,
11690
SemanticsIdentifier.CountryCode.EE,
11791
identificationCode
11892
);
119-
return new SemanticIdentification(
120-
// ToDo add mobile number here
121-
AuthenticationType.MID, etsiIdentifier, "mobile_number"
122-
);
93+
return new SemanticIdentification(authType, etsiIdentifier);
12394
}
12495

12596
}

cdoc2-lib/src/main/java/ee/cyber/cdoc2/crypto/keymaterial/encrypt/EtsiIdentifierEncKeyMaterialBuilder.java

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,7 @@ public EtsiIdentifierEncKeyMaterialBuilder forEId(boolean forEId)
7070
* @return the list of EncryptionKeyMaterial
7171
*/
7272
public EtsiIdentifierEncKeyMaterialBuilder forSid(String[] sidCodes) {
73-
if (null != sidCodes) {
74-
List<EncryptionKeyMaterial> keyMaterials = Arrays.stream(sidCodes)
75-
.map(idCode -> {
76-
SemanticIdentification semanticIdentifier = SemanticIdentification.forSid(idCode);
77-
KeyLabelParams keyLabelParams
78-
= createKeySharesKeyLabelParams(semanticIdentifier.getIdentifier());
79-
80-
return EncryptionKeyMaterial.fromAuthMeans(semanticIdentifier, keyLabelParams);
81-
})
82-
.toList();
83-
84-
recipients.addAll(keyMaterials);
85-
}
86-
return this;
73+
return withKeyShares(sidCodes, SemanticIdentification.AuthenticationType.SID);
8774
}
8875

8976
/**
@@ -92,11 +79,17 @@ public EtsiIdentifierEncKeyMaterialBuilder forSid(String[] sidCodes) {
9279
* @return the list of EncryptionKeyMaterial
9380
*/
9481
public EtsiIdentifierEncKeyMaterialBuilder forMid(String[] midCodes) {
95-
if (null != midCodes) {
96-
List<EncryptionKeyMaterial> keyMaterials = Arrays.stream(midCodes)
82+
return withKeyShares(midCodes, SemanticIdentification.AuthenticationType.MID);
83+
}
84+
85+
private EtsiIdentifierEncKeyMaterialBuilder withKeyShares(
86+
String[] idCodes, SemanticIdentification.AuthenticationType authType
87+
) {
88+
if (null != idCodes) {
89+
List<EncryptionKeyMaterial> keyMaterials = Arrays.stream(idCodes)
9790
.map(idCode -> {
98-
// ToDo add mobile number here
99-
SemanticIdentification semanticIdentifier = SemanticIdentification.forMid(idCode);
91+
SemanticIdentification semanticIdentifier = SemanticIdentification
92+
.forKeyShares(idCode, authType);
10093
KeyLabelParams keyLabelParams
10194
= createKeySharesKeyLabelParams(semanticIdentifier.getIdentifier());
10295

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package ee.cyber.cdoc2.util;
2+
3+
import java.util.regex.Matcher;
4+
import java.util.regex.Pattern;
5+
6+
7+
/**
8+
* Utility class for national ID code validation.
9+
*/
10+
public final class IdCodeValidationUtil {
11+
12+
private static final String IDENTITY_CODE_PATTERN = "\\d{11}";
13+
private static final Pattern idPattern = Pattern.compile(IDENTITY_CODE_PATTERN);
14+
15+
private IdCodeValidationUtil() { }
16+
17+
/**
18+
* Validates national identity code.
19+
* @param idCode identity code
20+
* @return identity code
21+
* @throws IllegalArgumentException if identity code validation has failed
22+
*/
23+
public static String getValidatedIdentityCode(String idCode) {
24+
Matcher matcher = idPattern.matcher(idCode);
25+
if (!matcher.matches()) {
26+
throw new IllegalArgumentException("Identity number must be of 11 numbers: " + idCode);
27+
}
28+
29+
int checksum = Integer.parseInt(idCode.substring(10));
30+
String idNumberWithoutChecksum = idCode.substring(0, 10);
31+
32+
int calculatedChecksum = getCalculatedChecksum(idNumberWithoutChecksum);
33+
34+
if (checksum != calculatedChecksum) {
35+
throw new IllegalArgumentException("Invalid identity number: " + idCode);
36+
}
37+
38+
return idCode;
39+
}
40+
41+
private static int getCalculatedChecksum(String idNumberWithoutChecksum) {
42+
int[] firstTierMultipliers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 1};
43+
int calculatedChecksum = calculateChecksum(idNumberWithoutChecksum, firstTierMultipliers);
44+
if (10 == calculatedChecksum) {
45+
return getSecondTierCalculatedChecksum(idNumberWithoutChecksum);
46+
}
47+
48+
return calculatedChecksum;
49+
}
50+
51+
private static int getSecondTierCalculatedChecksum(String idNumberWithoutChecksum) {
52+
int[] secondTierMultipliers = {3, 4, 5, 6, 7, 8, 9, 1, 2, 3};
53+
int calculatedChecksum = calculateChecksum(idNumberWithoutChecksum, secondTierMultipliers);
54+
if (10 == calculatedChecksum) {
55+
calculatedChecksum = 0;
56+
}
57+
return calculatedChecksum;
58+
}
59+
60+
private static int calculateChecksum(String idNumberWithoutChecksum, int[] tierMultipliers) {
61+
int sum = 0;
62+
for (int i = 0; i < tierMultipliers.length; i++) {
63+
sum =
64+
sum + (tierMultipliers[i]
65+
* Integer.parseInt(idNumberWithoutChecksum.substring(i, i + 1)));
66+
}
67+
return sum % 11;
68+
}
69+
70+
}

cdoc2-lib/src/test/java/ee/cyber/cdoc2/ClientConfigurationUtil.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
public final class ClientConfigurationUtil {
2020

21+
public static final String MOBILE_ID_PROPERTIES_PATH = "mobile-id/mobile_id-test.properties";
2122
public static final String SMART_ID_PROPERTIES_PATH = "smart-id/smart_id-test.properties";
2223

2324
private ClientConfigurationUtil() { }
@@ -39,7 +40,7 @@ public static SmartIdClientConfiguration registerFromProperties(Properties prope
3940

4041
public static MobileIdClientConfiguration getMobileIdConfiguration() throws ConfigurationLoadingException {
4142
Properties properties = loadProperties(
42-
CLASSPATH + "mobile-id/mobile_id-test.properties"
43+
CLASSPATH + MOBILE_ID_PROPERTIES_PATH
4344
);
4445
Cdoc2Configuration configuration = new MobileIdClientConfigurationImpl(properties);
4546

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ee.cyber.cdoc2;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static ee.cyber.cdoc2.util.IdCodeValidationUtil.getValidatedIdentityCode;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
8+
9+
class IdCodeValidationUtilTest {
10+
11+
@Test
12+
void testSuccessfulIdCodeValidation() {
13+
String validatedIdCode = getValidatedIdentityCode("51307149560");
14+
assertEquals("51307149560", validatedIdCode);
15+
16+
getValidatedIdentityCode("60001019939");
17+
getValidatedIdentityCode("60001019983");
18+
getValidatedIdentityCode("60001019961");
19+
getValidatedIdentityCode("60001019972");
20+
getValidatedIdentityCode("50001018908");
21+
getValidatedIdentityCode("30303039914");
22+
}
23+
24+
}

0 commit comments

Comments
 (0)