Skip to content

Commit 94ccfc5

Browse files
committed
RM-3397: KeyLabel machine readable format
1 parent da32c52 commit 94ccfc5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1781
-520
lines changed

cdoc2-cli/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ java -jar target/cdoc2-cli-*.jar decrypt --secret "label_b64secret:base64,aejUgx
119119
Key and label can be safely stored in a password manager.
120120

121121

122-
123122
### Decryption
124123
To decrypt:
125124
- CDOC2 file `/tmp/mydoc.cdoc`
@@ -354,3 +353,18 @@ default 10.0
354353

355354
Decrypting will be stopped if compressed file compression ratio is over compressionThreshold
356355

356+
#### ee.cyber.cdoc2.key-label.machine-readable-format.enabled
357+
default true
358+
359+
Key label format can be defined while encrypting. Machine parsable format is enabled by default
360+
and free text format is allowed if the property disabled.
361+
Machine-readable format is following, where `<data>` is the key label value:
362+
```
363+
data:[<mediatype>][;base64],<data>
364+
```
365+
366+
#### ee.cyber.cdoc2.key-label.file-name.added
367+
default true
368+
369+
Key label `<data>` field contains different parameters. File name is one of them. For security
370+
purpose it can be hidden in configuration. File name is added by default.

cdoc2-cli/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
</parent>
99

1010
<artifactId>cdoc2-cli</artifactId>
11-
<version>1.4.1-SNAPSHOT</version>
11+
<version>1.5.0-SNAPSHOT</version>
1212
<description>Command line utility to create/process CDOC2 files</description>
1313

1414
<properties>
@@ -42,7 +42,7 @@
4242
<dependency>
4343
<groupId>ee.cyber.cdoc2</groupId>
4444
<artifactId>cdoc2-lib</artifactId>
45-
<version>1.4.1-SNAPSHOT</version>
45+
<version>1.5.0-SNAPSHOT</version>
4646
</dependency>
4747
</dependencies>
4848

cdoc2-cli/src/main/java/ee/cyber/cdoc2/cli/SymmetricKeyUtil.java

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import java.nio.file.Files;
55
import java.nio.file.Path;
66
import java.security.GeneralSecurityException;
7+
import java.util.ArrayList;
78
import java.util.Arrays;
89
import java.util.Base64;
9-
import java.util.LinkedList;
1010
import java.util.List;
1111

1212
import ee.cyber.cdoc2.container.recipients.PBKDF2Recipient;
@@ -57,24 +57,33 @@ private SymmetricKeyUtil() { }
5757

5858
private static final Logger log = LoggerFactory.getLogger(SymmetricKeyUtil.class);
5959

60-
public static List<EncryptionKeyMaterial> extractEncryptionKeyMaterialFromSecrets(
61-
String[] secrets
62-
) throws CDocValidationException {
63-
if (secrets == null || secrets.length == 0) {
64-
return List.of();
65-
}
60+
public static List<FormattedOptionParts> getSecretsAndLabels(String[] secrets)
61+
throws CDocValidationException {
6662

67-
List<EncryptionKeyMaterial> result = new LinkedList<>();
63+
List<FormattedOptionParts> secretsAndLabels = new ArrayList<>();
64+
if (null != secrets && secrets.length > 0) {
65+
for (String secret: secrets) {
66+
FormattedOptionParts splitSecret
67+
= splitFormattedOption(secret, EncryptionKeyOrigin.SECRET);
68+
secretsAndLabels.add(splitSecret);
69+
}
70+
}
71+
return secretsAndLabels;
72+
}
6873

69-
for (String secret: secrets) {
70-
FormattedOptionParts splitSecret
71-
= splitFormattedOption(secret, EncryptionKeyOrigin.SECRET);
74+
/**
75+
* Extract symmetric key material from formatted
76+
* or "label123:base64,aejUgxxSQXqiiyrxSGACfMiIRBZq5KjlCwr/xVNY/B0="
77+
* @param formattedSecrets "<label>:<secret>"
78+
* @return
79+
*/
80+
public static List<EncryptionKeyMaterial> getEncryptionKeyMaterialFromFormattedSecrets(String[] formattedSecrets)
81+
throws CDocValidationException {
7282

73-
EncryptionKeyMaterial km
74-
= SymmetricKeyTools.getEncryptionKeyMaterialFromSecret(splitSecret);
75-
result.add(km);
76-
}
77-
return result;
83+
return getSecretsAndLabels(formattedSecrets).stream()
84+
.map(SymmetricKeyTools::extractKeyMaterialFromSecret)
85+
.map(e -> EncryptionKeyMaterial.fromSecret(e.getKey(), e.getValue()))
86+
.toList();
7887
}
7988

8089
public static EncryptionKeyMaterial extractEncryptionKeyMaterialFromSecret(
@@ -84,18 +93,17 @@ public static EncryptionKeyMaterial extractEncryptionKeyMaterialFromSecret(
8493
return null;
8594
}
8695

87-
FormattedOptionParts splitSecret
88-
= splitFormattedOption(secret, EncryptionKeyOrigin.SECRET);
96+
FormattedOptionParts splitSecret = splitFormattedOption(secret, EncryptionKeyOrigin.SECRET);
8997

9098
return SymmetricKeyTools.getEncryptionKeyMaterialFromSecret(splitSecret);
9199
}
92100

93101
public static EncryptionKeyMaterial extractEncryptionKeyMaterialFromPassword(
94-
FormattedOptionParts passwordAndLabel
95-
) {
96-
return EncryptionKeyMaterial.fromPassword(
97-
passwordAndLabel.optionChars(), passwordAndLabel.label()
98-
);
102+
String password
103+
) throws CDocValidationException {
104+
FormattedOptionParts splitPasswordAndLabel = getSplitPasswordAndLabel(password, true);
105+
106+
return SymmetricKeyTools.getEncryptionKeyMaterialFromPassword(splitPasswordAndLabel);
99107
}
100108

101109
/**
@@ -150,9 +158,10 @@ public static FormattedOptionParts splitFormattedOption(
150158
* @param verifyPw if password should be asked twice to verify that they match (encryption).
151159
* @return FormattedOptionParts with extracted password and label
152160
*/
153-
public static FormattedOptionParts getSplitPasswordAndLabel(String formattedPassword,
154-
boolean verifyPw)
155-
throws CDocValidationException {
161+
public static FormattedOptionParts getSplitPasswordAndLabel(
162+
String formattedPassword,
163+
boolean verifyPw
164+
) throws CDocValidationException {
156165
FormattedOptionParts passwordAndLabel;
157166
if (formattedPassword.isEmpty()) {
158167
passwordAndLabel = InteractiveCommunicationUtil.readPasswordAndLabelInteractively(verifyPw);
@@ -180,9 +189,10 @@ public static FormattedOptionParts getSplitPasswordAndLabel(String formattedPass
180189
* @return
181190
* @throws CDocValidationException
182191
*/
183-
public static FormattedOptionParts getSplitPasswordAndLabel(String formattedPassword,
184-
List<PBKDF2Recipient> pbkdf2Recipients)
185-
throws CDocValidationException {
192+
public static FormattedOptionParts getSplitPasswordAndLabel(
193+
String formattedPassword,
194+
List<PBKDF2Recipient> pbkdf2Recipients
195+
) throws CDocValidationException {
186196

187197
if (pbkdf2Recipients.size() == 1) {
188198
String label = pbkdf2Recipients.get(0).getRecipientKeyLabel();
@@ -261,4 +271,13 @@ private static void excludeSecretKeyInPlainText(EncryptionKeyOrigin keyOrigin) {
261271
}
262272
}
263273

274+
public static FormattedOptionParts getPasswordAndLabel(String password)
275+
throws CDocValidationException {
276+
277+
if (null == password) {
278+
return null;
279+
}
280+
return getSplitPasswordAndLabel(password, true);
281+
}
282+
264283
}

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

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
package ee.cyber.cdoc2.cli.commands;
22

3+
import ee.cyber.cdoc2.FormattedOptionParts;
34
import ee.cyber.cdoc2.cli.SymmetricKeyUtil;
45
import ee.cyber.cdoc2.CDocBuilder;
5-
import ee.cyber.cdoc2.CDocValidationException;
6-
import ee.cyber.cdoc2.FormattedOptionParts;
76
import ee.cyber.cdoc2.crypto.keymaterial.EncryptionKeyMaterial;
8-
import ee.cyber.cdoc2.crypto.EllipticCurve;
9-
import ee.cyber.cdoc2.crypto.PemTools;
10-
import ee.cyber.cdoc2.util.SkLdapUtil;
117
import org.slf4j.Logger;
128
import org.slf4j.LoggerFactory;
139
import picocli.CommandLine;
@@ -16,16 +12,13 @@
1612
import picocli.CommandLine.Command;
1713

1814
import java.io.File;
19-
import java.security.PublicKey;
2015
import java.time.Duration;
2116
import java.time.format.DateTimeParseException;
2217
import java.util.Arrays;
23-
import java.util.LinkedHashMap;
2418
import java.util.List;
2519
import java.util.Map;
2620
import java.util.Properties;
2721
import java.util.concurrent.Callable;
28-
import java.util.stream.Collectors;
2922

3023
import static ee.cyber.cdoc2.cli.CDocCommonHelper.getServerProperties;
3124

@@ -103,46 +96,54 @@ private void setProperty(Map<String, String> props) {
10396
public Void call() throws Exception {
10497

10598
if (log.isDebugEnabled()) {
106-
log.debug("create --file {} --pubkey {} --cert {} {}",
99+
log.debug("create --file {} --pubkey {} --cert {} --secret {} --password {} {}",
107100
cdocFile,
108101
Arrays.toString(recipient.pubKeys),
109102
Arrays.toString(recipient.certs),
103+
(recipient.secrets != null) ? "****" : null,
104+
(recipient.password != null) ? "****" : null,
110105
Arrays.toString(inputFiles));
111106
}
112107

113-
//Map of PublicKey, keyLabel
114-
Map<PublicKey, String> recipientsMap = new LinkedHashMap<>();
115-
116-
recipientsMap.putAll(PemTools.loadPubKeysWithKeyLabel(this.recipient.pubKeys));
117-
recipientsMap.putAll(PemTools.loadCertKeysWithLabel(this.recipient.certs));
118108

119-
// fetch authentication certificates' public keys for natural person identity codes
120-
Map<PublicKey, String> ldapKeysWithLabels =
121-
SkLdapUtil.getPublicKeysWithLabels(this.recipient.identificationCodes).entrySet()
122-
.stream()
123-
.filter(entry -> EllipticCurve.isSupported(entry.getKey()))
124-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
125-
recipientsMap.putAll(ldapKeysWithLabels);
126109

127-
List<EncryptionKeyMaterial> recipients = recipientsMap.entrySet().stream()
128-
.map(entry -> EncryptionKeyMaterial.fromPublicKey(entry.getKey(), entry.getValue()))
129-
.collect(Collectors.toList());
130-
131-
addSymmetricKeysWithLabels(recipients);
132110

133111
CDocBuilder cDocBuilder = new CDocBuilder()
134-
.withRecipients(recipients)
135112
.withPayloadFiles(Arrays.asList(inputFiles));
136113

137-
if (keyCapsuleExpiryDuration != null) {
138-
setExpiryDurationOrLogWarn(cDocBuilder);
139-
}
140-
141114
if (keyServerPropertiesFile != null) {
142115
Properties p = getServerProperties(keyServerPropertiesFile);
143116
cDocBuilder.withServerProperties(p);
144117
}
145118

119+
120+
List<EncryptionKeyMaterial> symmetricKMs =
121+
SymmetricKeyUtil.getEncryptionKeyMaterialFromFormattedSecrets(recipient.secrets);
122+
123+
List<EncryptionKeyMaterial> recipients = EncryptionKeyMaterial.collectionBuilder()
124+
.fromPublicKey(this.recipient.pubKeys)
125+
.fromX509Certificate(this.recipient.certs)
126+
// fetch authentication certificates' public keys for natural person identity codes
127+
.fromEId(this.recipient.identificationCodes)
128+
.build();
129+
130+
if (recipient.password != null) {
131+
FormattedOptionParts passwordAndLabel
132+
= SymmetricKeyUtil.getPasswordAndLabel(recipient.password);
133+
134+
recipients.addAll(
135+
EncryptionKeyMaterial.collectionBuilder().fromPassword(
136+
passwordAndLabel.optionChars(), passwordAndLabel.label()).build());
137+
}
138+
139+
recipients.addAll(symmetricKMs);
140+
141+
cDocBuilder.withRecipients(recipients);
142+
143+
if (keyCapsuleExpiryDuration != null) {
144+
setExpiryDurationOrLogWarn(cDocBuilder);
145+
}
146+
146147
cDocBuilder.buildToFile(cdocFile);
147148

148149
log.info("Created {}", cdocFile.getAbsolutePath());
@@ -160,19 +161,6 @@ private void setExpiryDurationOrLogWarn(CDocBuilder cDocBuilder) {
160161
}
161162
}
162163

163-
private void addSymmetricKeysWithLabels(List<EncryptionKeyMaterial> recipients)
164-
throws CDocValidationException {
165-
166-
recipients.addAll(SymmetricKeyUtil.extractEncryptionKeyMaterialFromSecrets(
167-
recipient.secrets)
168-
);
169-
if (null != recipient.password) {
170-
FormattedOptionParts password
171-
= SymmetricKeyUtil.getSplitPasswordAndLabel(recipient.password, true);
172-
recipients.add(SymmetricKeyUtil.extractEncryptionKeyMaterialFromPassword(password));
173-
}
174-
}
175-
176164
public static class DurationConverter implements CommandLine.ITypeConverter<Duration> {
177165
@Override
178166
public Duration convert(String arg) {

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
import java.util.Objects;
1818
import java.util.concurrent.Callable;
1919

20+
import static ee.cyber.cdoc2.crypto.KeyLabelTools.extractKeyLabelParams;
21+
import static ee.cyber.cdoc2.crypto.KeyLabelTools.keyLabelParamsForDisplaying;
22+
23+
24+
2025
//S106 Standard outputs should not be used directly to log anything
2126
//CLI needs to interact with standard outputs
2227
@SuppressWarnings("java:S106")
@@ -46,13 +51,16 @@ public Void call() throws Exception {
4651

4752
String type = getHumanReadableType(recipient);
4853

49-
String label = recipient.getRecipientKeyLabel();
54+
Map<String, String> keyLabelParams
55+
= extractKeyLabelParams(recipient.getRecipientKeyLabel());
5056

5157
String server = (recipient instanceof ServerRecipient)
52-
? "(server: " + ((ServerRecipient) recipient).getKeyServerId() + ")"
53-
: "";
58+
? "(server: " + ((ServerRecipient) recipient).getKeyServerId() + ")"
59+
: "";
5460

55-
System.out.println(type + ": " + label + " " + server);
61+
System.out.println(
62+
type + ": " + keyLabelParamsForDisplaying(keyLabelParams) + " " + server
63+
);
5664
}
5765

5866
return null;

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import ee.cyber.cdoc2.cli.SymmetricKeyUtil;
1616
import ee.cyber.cdoc2.CDocReEncrypter;
1717
import ee.cyber.cdoc2.CDocValidationException;
18-
import ee.cyber.cdoc2.FormattedOptionParts;
1918
import ee.cyber.cdoc2.client.KeyCapsuleClientFactory;
2019
import ee.cyber.cdoc2.crypto.keymaterial.DecryptionKeyMaterial;
2120
import ee.cyber.cdoc2.crypto.keymaterial.EncryptionKeyMaterial;
@@ -134,13 +133,13 @@ private EncryptionKeyMaterial extractSymmetricKeyEncKeyMaterial()
134133
throws CDocValidationException {
135134

136135
if (null != this.reEncryptPassword) {
137-
FormattedOptionParts splitPasswordAndLabel
138-
= SymmetricKeyUtil.getSplitPasswordAndLabel(this.reEncryptPassword, true);
139-
return SymmetricKeyUtil.extractEncryptionKeyMaterialFromPassword(splitPasswordAndLabel);
136+
return SymmetricKeyUtil.extractEncryptionKeyMaterialFromPassword(this.reEncryptPassword);
140137
}
141138

142139
if (null != this.reEncryptSecret) {
143-
return SymmetricKeyUtil.extractEncryptionKeyMaterialFromSecret(this.reEncryptSecret);
140+
return SymmetricKeyUtil.extractEncryptionKeyMaterialFromSecret(
141+
this.reEncryptSecret
142+
);
144143
}
145144

146145
throw new IllegalArgumentException("Cannot re-create document without password");

0 commit comments

Comments
 (0)