Skip to content

Commit 3c8715a

Browse files
committed
Merge branch 'RM-3354_key-capsule-expiry-cli' into 'master'
RM-3354: Added Key capsule expiry option to CLI See merge request cdoc2/cdoc2-java-ref-impl!19
2 parents fed9f93 + ec4d3a3 commit 3c8715a

File tree

17 files changed

+230
-34
lines changed

17 files changed

+230
-34
lines changed

cdoc2-cli/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ Certificate (`-c` option):
5858
java -jar target/cdoc2-cli-*.jar create --server=config/localhost/localhost.properties -f /tmp/localhost.cdoc -c keys/cdoc2client-certificate.pem README.md
5959
```
6060

61+
Key capsule expiration date can be requested when adding expiry duration:
62+
```
63+
-ex P365D
64+
```
65+
Default expiration duration will be used if it is not requested by the client. Default and max
66+
expiration durations are configurable values in put-server and get-server.
67+
68+
6169
### Encryption with symmetric key
6270

6371
Generate key with openssl (minimum length 32 bytes):

cdoc2-cli/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
</parent>
99

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

1414
<properties>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public static void main(String... args) {
4747
CommandLine.usage(new CDocInfoCmd(), System.out);
4848
}
4949
int exitCode = new CommandLine(new CDocCli()).execute(args);
50+
5051
System.exit(exitCode);
5152
}
5253

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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package ee.cyber.cdoc2.cli.commands;
22

3-
43
import ee.cyber.cdoc2.cli.SymmetricKeyUtil;
54
import ee.cyber.cdoc2.CDocBuilder;
65
import ee.cyber.cdoc2.CDocValidationException;
@@ -18,6 +17,8 @@
1817

1918
import java.io.File;
2019
import java.security.PublicKey;
20+
import java.time.Duration;
21+
import java.time.format.DateTimeParseException;
2122
import java.util.Arrays;
2223
import java.util.LinkedHashMap;
2324
import java.util.List;
@@ -28,13 +29,17 @@
2829

2930
import static ee.cyber.cdoc2.cli.CDocCommonHelper.getServerProperties;
3031

32+
3133
//S106 - Standard outputs should not be used directly to log anything
3234
//CLI needs to interact with standard outputs
3335
@SuppressWarnings("java:S106")
3436
@Command(name = "create", aliases = {"c", "encrypt"}, showAtFileInUsageHelp = true)
3537
public class CDocCreateCmd implements Callable<Void> {
38+
3639
private static final Logger log = LoggerFactory.getLogger(CDocCreateCmd.class);
3740

41+
private static final String DURATION_FORMAT = "P(n)DT(n)H(n)M(n)S";
42+
3843
// default server configuration disabled, until public key server is up and running
3944
//private static final String DEFAULT_SERVER_PROPERTIES = "classpath:localhost.properties";
4045

@@ -75,7 +80,7 @@ private void setProperty(Map<String, String> props) {
7580

7681
@Option(names = {"-S", "--server"},
7782
paramLabel = "FILE.properties",
78-
description = "Key server connection properties file"
83+
description = "key server connection properties file"
7984
// default server configuration disabled, until public key server is up and running
8085
//, arity = "0..1"
8186
//, fallbackValue = DEFAULT_SERVER_PROPERTIES
@@ -85,6 +90,12 @@ private void setProperty(Map<String, String> props) {
8590
@Parameters(paramLabel = "FILE", description = "one or more files to encrypt", arity = "1..*")
8691
private File[] inputFiles;
8792

93+
@Option(names = { "-exp", "--expiry" }, paramLabel = DURATION_FORMAT,
94+
description = "Key capsule expiry duration",
95+
converter = DurationConverter.class
96+
)
97+
private Duration keyCapsuleExpiryDuration;
98+
8899
@Option(names = { "-h", "--help" }, usageHelp = true, description = "display a help message")
89100
private boolean helpRequested = false;
90101

@@ -123,6 +134,10 @@ public Void call() throws Exception {
123134
.withRecipients(recipients)
124135
.withPayloadFiles(Arrays.asList(inputFiles));
125136

137+
if (keyCapsuleExpiryDuration != null) {
138+
setExpiryDurationOrLogWarn(cDocBuilder);
139+
}
140+
126141
if (keyServerPropertiesFile != null) {
127142
Properties p = getServerProperties(keyServerPropertiesFile);
128143
cDocBuilder.withServerProperties(p);
@@ -135,6 +150,16 @@ public Void call() throws Exception {
135150
return null;
136151
}
137152

153+
private void setExpiryDurationOrLogWarn(CDocBuilder cDocBuilder) {
154+
if (null != this.recipient.secrets || null != recipient.password) {
155+
String warnMsg = "Key capsule expiry duration cannot be requested for symmetric key "
156+
+ "encryption";
157+
log.warn("WARNING: {}", warnMsg);
158+
} else {
159+
cDocBuilder.withCapsuleExpiryDuration(keyCapsuleExpiryDuration);
160+
}
161+
}
162+
138163
private void addSymmetricKeysWithLabels(List<EncryptionKeyMaterial> recipients)
139164
throws CDocValidationException {
140165

@@ -148,4 +173,17 @@ private void addSymmetricKeysWithLabels(List<EncryptionKeyMaterial> recipients)
148173
}
149174
}
150175

176+
public static class DurationConverter implements CommandLine.ITypeConverter<Duration> {
177+
@Override
178+
public Duration convert(String arg) {
179+
try {
180+
return Duration.parse(arg);
181+
} catch (DateTimeParseException e) {
182+
throw new CommandLine.TypeConversionException(
183+
"Expiry duration format should be " + DURATION_FORMAT
184+
);
185+
}
186+
}
187+
}
188+
151189
}

cdoc2-lib/src/main/java/ee/cyber/cdoc2/CDocBuilder.java

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

33
import ee.cyber.cdoc2.client.ExtApiException;
4+
import ee.cyber.cdoc2.client.KeyCapsuleClient;
45
import ee.cyber.cdoc2.client.KeyCapsuleClientImpl;
56
import ee.cyber.cdoc2.container.Envelope;
67
import ee.cyber.cdoc2.crypto.Crypto;
@@ -29,6 +30,7 @@
2930
import java.security.PublicKey;
3031
import java.security.interfaces.ECPublicKey;
3132
import java.security.interfaces.RSAPublicKey;
33+
import java.time.Duration;
3234
import java.util.Base64;
3335
import java.util.LinkedList;
3436
import java.util.List;
@@ -43,6 +45,7 @@ public class CDocBuilder {
4345

4446
private List<File> payloadFiles;
4547
private final List<EncryptionKeyMaterial> recipients = new LinkedList<>();
48+
private Duration keyCapsuleExpiryDuration;
4649
private Properties serverProperties;
4750

4851
public CDocBuilder withPayloadFiles(List<File> files) {
@@ -60,12 +63,19 @@ public CDocBuilder withRecipients(List<EncryptionKeyMaterial> recipientsEncKM) {
6063
return this;
6164
}
6265

66+
public CDocBuilder withCapsuleExpiryDuration(Duration xExpiryDuration) {
67+
this.keyCapsuleExpiryDuration = xExpiryDuration;
68+
return this;
69+
}
70+
6371
public CDocBuilder withServerProperties(Properties p) {
6472
this.serverProperties = p;
6573
return this;
6674
}
6775

68-
public void buildToFile(File outputCDocFile) throws CDocException, IOException, CDocValidationException {
76+
public void buildToFile(File outputCDocFile)
77+
throws CDocException, IOException, CDocValidationException {
78+
6979
if (outputCDocFile == null) {
7080
throw new CDocValidationException("Must provide CDOC output filename ");
7181
}
@@ -108,11 +118,19 @@ private OpenOption getOpenOption() {
108118

109119
private Envelope prepareEnvelope()
110120
throws ExtApiException, GeneralSecurityException, IOException {
121+
111122
if (serverProperties == null) {
112123
return Envelope.prepare(recipients, null);
113124
} else {
114125
// for encryption, do not init mTLS client as this might require smart-card
115-
return Envelope.prepare(recipients, KeyCapsuleClientImpl.create(serverProperties, false));
126+
KeyCapsuleClient client = KeyCapsuleClientImpl.create(serverProperties, false);
127+
if (null != keyCapsuleExpiryDuration) {
128+
client.setExpiryDuration(keyCapsuleExpiryDuration);
129+
}
130+
return Envelope.prepare(
131+
recipients,
132+
client
133+
);
116134
}
117135
}
118136

@@ -238,4 +256,5 @@ void validatePayloadFiles() throws CDocValidationException {
238256
}
239257
}
240258
}
259+
241260
}

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/EcCapsuleClient.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ public interface EcCapsuleClient extends ServerClient {
1111
* @return transactionId to retrieve senderKey from the server
1212
* @throws ExtApiException if error happens
1313
*/
14-
String storeSenderKey(ECPublicKey receiverKey, ECPublicKey senderKey) throws ExtApiException;
14+
String storeSenderKey(
15+
ECPublicKey receiverKey,
16+
ECPublicKey senderKey
17+
) throws ExtApiException;
1518

1619
/**
1720
* Retrieve previously stored sender key using transaction id

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/EcCapsuleClientImpl.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ public EcCapsuleClientImpl(KeyCapsuleClient keyCapsulesClient) {
2222
}
2323

2424
@Override
25-
public String storeSenderKey(ECPublicKey receiverKey, ECPublicKey senderKey) throws ExtApiException {
25+
public String storeSenderKey(
26+
ECPublicKey receiverKey,
27+
ECPublicKey senderKey
28+
) throws ExtApiException {
2629

2730
EllipticCurve curve;
2831
try {
@@ -47,7 +50,7 @@ public String storeSenderKey(ECPublicKey receiverKey, ECPublicKey senderKey) thr
4750
.recipientId(ECKeys.encodeEcPubKeyForTls(curve, receiverKey))
4851
.ephemeralKeyMaterial(ECKeys.encodeEcPubKeyForTls(curve, senderKey));
4952

50-
return keyCapsulesClient.storeCapsule(capsule, null);
53+
return keyCapsulesClient.storeCapsule(capsule);
5154
}
5255

5356
@Override
@@ -79,4 +82,5 @@ public Optional<ECPublicKey> getSenderKey(String transactionId) throws ExtApiExc
7982
public String getServerIdentifier() {
8083
return keyCapsulesClient.getServerIdentifier();
8184
}
85+
8286
}

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/KeyCapsuleClient.java

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

3-
import jakarta.annotation.Nullable;
4-
53
import ee.cyber.cdoc2.client.model.Capsule;
64

7-
import java.time.OffsetDateTime;
5+
import java.time.Duration;
86
import java.util.Optional;
97

108

@@ -13,9 +11,14 @@
1311
*/
1412
public interface KeyCapsuleClient extends ServerClient {
1513

16-
String storeCapsule(Capsule capsule) throws ExtApiException;
14+
/**
15+
* When set, then client sends X-ExpiryTime header
16+
* @param duration Duration is converted into exact dateTime
17+
* when {@link #storeCapsule(Capsule)} is called
18+
*/
19+
void setExpiryDuration(Duration duration);
1720

18-
String storeCapsule(Capsule capsule, @Nullable OffsetDateTime xExpiryTime) throws ExtApiException;
21+
String storeCapsule(Capsule capsule) throws ExtApiException;
1922

2023
Optional<Capsule> getCapsule(String id) throws ExtApiException;
2124

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/KeyCapsuleClientImpl.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ee.cyber.cdoc2.client;
22

3+
import ee.cyber.cdoc2.client.api.ApiException;
34
import ee.cyber.cdoc2.crypto.Pkcs11Tools;
45
import ee.cyber.cdoc2.CDocConfiguration;
56
import ee.cyber.cdoc2.CDocUserException;
@@ -13,6 +14,7 @@
1314
import java.security.NoSuchAlgorithmException;
1415
import java.security.cert.Certificate;
1516
import java.security.cert.CertificateException;
17+
import java.time.Duration;
1618
import java.time.OffsetDateTime;
1719
import java.util.Objects;
1820
import java.util.Optional;
@@ -21,6 +23,8 @@
2123
import org.slf4j.Logger;
2224
import org.slf4j.LoggerFactory;
2325

26+
import static ee.cyber.cdoc2.util.DurationUtil.getExpiryTime;
27+
2428

2529
/**
2630
* KeyCapsuleClient initialization from properties file.
@@ -33,6 +37,8 @@ public final class KeyCapsuleClientImpl implements KeyCapsuleClient, KeyCapsuleC
3337
private final Cdoc2KeyCapsuleApiClient getClient; // mTLS client
3438
@Nullable
3539
private KeyStore clientKeyStore; //initialised only from #create(Properties)
40+
@Nullable
41+
private Duration capsuleExpiryDuration; //initialised only when #setExpiryDuration() was called
3642

3743
private KeyCapsuleClientImpl(
3844
String serverIdentifier,
@@ -222,24 +228,33 @@ private static KeyStore loadTrustKeyStore(Properties p) throws KeyStoreException
222228
}
223229

224230
@Override
225-
public String storeCapsule(Capsule capsule) throws ExtApiException {
226-
return storeCapsule(capsule, null);
231+
public void setExpiryDuration(Duration duration) {
232+
this.capsuleExpiryDuration = duration;
227233
}
228234

229235
@Override
230-
public String storeCapsule(Capsule capsule, @Nullable OffsetDateTime xExpiryTime) throws ExtApiException {
236+
public String storeCapsule(Capsule capsule) throws ExtApiException {
231237
Objects.requireNonNull(postClient);
232238

233239
String result = null;
234240
try {
235-
result = postClient.createCapsule(capsule, xExpiryTime);
241+
result = createCapsule(capsule);
236242
} catch (Exception e) {
237243
log.error("Failed to create capsule", e);
238244
handleOpenApiException(e);
239245
}
240246
return result;
241247
}
242248

249+
private String createCapsule(Capsule capsule) throws ApiException {
250+
if (null != capsuleExpiryDuration) {
251+
OffsetDateTime expiryTime = getExpiryTime(capsuleExpiryDuration);
252+
return postClient.createCapsule(capsule, expiryTime);
253+
} else {
254+
return postClient.createCapsule(capsule);
255+
}
256+
}
257+
243258
@Override
244259
public Optional<Capsule> getCapsule(String id) throws ExtApiException {
245260
if (getClient == null) {

cdoc2-lib/src/main/java/ee/cyber/cdoc2/client/RsaCapsuleClient.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
import java.util.Optional;
55

66
public interface RsaCapsuleClient extends ServerClient {
7-
String storeRsaCapsule(RSAPublicKey recipient, byte[] encryptedKek) throws ExtApiException;
7+
8+
String storeRsaCapsule(
9+
RSAPublicKey recipient,
10+
byte[] encryptedKek
11+
) throws ExtApiException;
812

913
Optional<byte[]> getEncryptedKek(String transactionId) throws ExtApiException;
14+
1015
}

0 commit comments

Comments
 (0)