aboutsummaryrefslogtreecommitdiffstats
path: root/vespaclient-java/src/main/java/com/yahoo
diff options
context:
space:
mode:
Diffstat (limited to 'vespaclient-java/src/main/java/com/yahoo')
-rw-r--r--vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java3
-rw-r--r--vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java36
-rw-r--r--vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/EncryptTool.java2
-rw-r--r--vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java104
-rw-r--r--vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ToolUtils.java25
5 files changed, 146 insertions, 24 deletions
diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java
index 0498154aa91..26868207bd3 100644
--- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java
+++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java
@@ -5,6 +5,7 @@ import com.yahoo.vespa.security.tool.crypto.ConvertBaseTool;
import com.yahoo.vespa.security.tool.crypto.DecryptTool;
import com.yahoo.vespa.security.tool.crypto.EncryptTool;
import com.yahoo.vespa.security.tool.crypto.KeygenTool;
+import com.yahoo.vespa.security.tool.crypto.ResealTool;
import com.yahoo.vespa.security.tool.crypto.TokenInfoTool;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@@ -47,7 +48,7 @@ public class Main {
private static final List<Tool> TOOLS = List.of(
new KeygenTool(), new EncryptTool(), new DecryptTool(), new TokenInfoTool(),
- new ConvertBaseTool());
+ new ConvertBaseTool(), new ResealTool());
private static Optional<Tool> toolFromCliArgs(String[] args) {
if (args.length == 0) {
diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java
index f1c166ba934..2cc724538d4 100644
--- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java
+++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java
@@ -27,10 +27,10 @@ import java.util.Optional;
*/
public class DecryptTool implements Tool {
- static final String OUTPUT_FILE_OPTION = "output-file";
- static final String RECIPIENT_PRIVATE_KEY_FILE_OPTION = "recipient-private-key-file";
- static final String KEY_ID_OPTION = "key-id";
- static final String TOKEN_OPTION = "token";
+ static final String OUTPUT_FILE_OPTION = "output-file";
+ static final String PRIVATE_KEY_FILE_OPTION = "private-key-file";
+ static final String EXPECTED_KEY_ID_OPTION = "expected-key-id";
+ static final String TOKEN_OPTION = "token";
private static final List<Option> OPTIONS = List.of(
Option.builder("o")
@@ -41,17 +41,16 @@ public class DecryptTool implements Tool {
"quotes) to write plaintext to STDOUT instead of a file.")
.build(),
Option.builder("k")
- .longOpt(RECIPIENT_PRIVATE_KEY_FILE_OPTION)
+ .longOpt(PRIVATE_KEY_FILE_OPTION)
.hasArg(true)
.required(false)
- .desc("Recipient private key file in Base58 encoded format")
+ .desc("Private key file in Base58 encoded format")
.build(),
- Option.builder("i")
- .longOpt(KEY_ID_OPTION)
+ Option.builder("e")
+ .longOpt(EXPECTED_KEY_ID_OPTION)
.hasArg(true)
.required(false)
- .desc("Numeric ID of recipient key. If this is not provided, " +
- "the key ID stored as part of the token is not verified.")
+ .desc("Expected key ID in token. If this is not provided, the key ID is not verified.")
.build(),
Option.builder("t")
.longOpt(TOKEN_OPTION)
@@ -85,21 +84,15 @@ public class DecryptTool implements Tool {
throw new IllegalArgumentException("Expected exactly 1 file argument to decrypt");
}
var inputArg = leftoverArgs[0];
- var maybeKeyId = Optional.ofNullable(arguments.hasOption(KEY_ID_OPTION)
- ? arguments.getOptionValue(KEY_ID_OPTION)
+ var maybeKeyId = Optional.ofNullable(arguments.hasOption(EXPECTED_KEY_ID_OPTION)
+ ? arguments.getOptionValue(EXPECTED_KEY_ID_OPTION)
: null);
var outputArg = CliUtils.optionOrThrow(arguments, OUTPUT_FILE_OPTION);
- var privKeyPath = Paths.get(CliUtils.optionOrThrow(arguments, RECIPIENT_PRIVATE_KEY_FILE_OPTION));
var tokenString = CliUtils.optionOrThrow(arguments, TOKEN_OPTION);
var sealedSharedKey = SealedSharedKey.fromTokenString(tokenString.strip());
- if (maybeKeyId.isPresent()) {
- var myKeyId = KeyId.ofString(maybeKeyId.get());
- if (!myKeyId.equals(sealedSharedKey.keyId())) {
- // Don't include raw key bytes array verbatim in message (may contain control chars etc).
- throw new IllegalArgumentException("Key ID specified with --key-id does not match key ID " +
- "used when generating the supplied token");
- }
- }
+ ToolUtils.verifyExpectedKeyId(sealedSharedKey, maybeKeyId);
+
+ var privKeyPath = Paths.get(CliUtils.optionOrThrow(arguments, PRIVATE_KEY_FILE_OPTION));
var privateKey = KeyUtils.fromBase58EncodedX25519PrivateKey(Files.readString(privKeyPath).strip());
var secretShared = SharedKeyGenerator.fromSealedKey(sealedSharedKey, privateKey);
var cipher = SharedKeyGenerator.makeAesGcmDecryptionCipher(secretShared);
@@ -108,7 +101,6 @@ public class DecryptTool implements Tool {
var outStream = CliUtils.outputStreamToFileOrStream(outputArg, invocation.stdOut())) {
CipherUtils.streamEncipher(inStream, outStream, cipher);
}
-
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/EncryptTool.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/EncryptTool.java
index 886433f00f8..962b42f4c22 100644
--- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/EncryptTool.java
+++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/EncryptTool.java
@@ -46,7 +46,7 @@ public class EncryptTool implements Tool {
.longOpt(KEY_ID_OPTION)
.hasArg(true)
.required(false)
- .desc("Numeric ID of recipient key")
+ .desc("ID of recipient key")
.build());
@Override
diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java
new file mode 100644
index 00000000000..e9bc0ae8fee
--- /dev/null
+++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java
@@ -0,0 +1,104 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.security.tool.crypto;
+
+import com.yahoo.security.KeyId;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.SealedSharedKey;
+import com.yahoo.security.SharedKeyGenerator;
+import com.yahoo.vespa.security.tool.CliUtils;
+import com.yahoo.vespa.security.tool.Tool;
+import com.yahoo.vespa.security.tool.ToolDescription;
+import com.yahoo.vespa.security.tool.ToolInvocation;
+import org.apache.commons.cli.Option;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Tooling for resealing a token for another recipient. This allows for delegating
+ * decryption to another party without having to reveal the private key of the original
+ * recipient.
+ *
+ * @author vekterli
+ */
+public class ResealTool implements Tool {
+
+ static final String PRIVATE_KEY_FILE_OPTION = "private-key-file";
+ static final String EXPECTED_KEY_ID_OPTION = "expected-key-id";
+ static final String RECIPIENT_KEY_ID_OPTION = "key-id";
+ static final String RECIPIENT_PUBLIC_KEY_OPTION = "recipient-public-key";
+
+ private static final List<Option> OPTIONS = List.of(
+ Option.builder("k")
+ .longOpt(PRIVATE_KEY_FILE_OPTION)
+ .hasArg(true)
+ .required(false)
+ .desc("Private key file in Base58 encoded format")
+ .build(),
+ Option.builder("e")
+ .longOpt(EXPECTED_KEY_ID_OPTION)
+ .hasArg(true)
+ .required(false)
+ .desc("Expected key ID in token. If this is not provided, the key ID is not verified.")
+ .build(),
+ Option.builder("r")
+ .longOpt(RECIPIENT_PUBLIC_KEY_OPTION)
+ .hasArg(true)
+ .required(false)
+ .desc("Recipient X25519 public key in Base58 encoded format")
+ .build(),
+ Option.builder("i")
+ .longOpt(RECIPIENT_KEY_ID_OPTION)
+ .hasArg(true)
+ .required(false)
+ .desc("ID of recipient key")
+ .build());
+
+ @Override
+ public String name() {
+ return "reseal";
+ }
+
+ @Override
+ public ToolDescription description() {
+ return new ToolDescription(
+ "<token> <options>",
+ "Reseals the input token for another recipient, allowing that recipient to " +
+ "decrypt the file that the input token was originally created for.\n" +
+ "Prints new token to STDOUT.",
+ "Note: this is a BETA tool version; its interface may be changed at any time",
+ OPTIONS);
+ }
+
+ @Override
+ public int invoke(ToolInvocation invocation) {
+ try {
+ var arguments = invocation.arguments();
+ var leftoverArgs = arguments.getArgs();
+ if (leftoverArgs.length != 1) {
+ throw new IllegalArgumentException("Expected exactly 1 token argument to re-seal");
+ }
+ var tokenString = leftoverArgs[0];
+ var maybeKeyId = Optional.ofNullable(arguments.hasOption(EXPECTED_KEY_ID_OPTION)
+ ? arguments.getOptionValue(EXPECTED_KEY_ID_OPTION)
+ : null);
+ var sealedSharedKey = SealedSharedKey.fromTokenString(tokenString.strip());
+ ToolUtils.verifyExpectedKeyId(sealedSharedKey, maybeKeyId);
+
+ var recipientPubKey = KeyUtils.fromBase58EncodedX25519PublicKey(CliUtils.optionOrThrow(arguments, RECIPIENT_PUBLIC_KEY_OPTION).strip());
+ var recipientKeyId = KeyId.ofString(CliUtils.optionOrThrow(arguments, RECIPIENT_KEY_ID_OPTION));
+ var privKeyPath = Paths.get(CliUtils.optionOrThrow(arguments, PRIVATE_KEY_FILE_OPTION));
+ var privateKey = KeyUtils.fromBase58EncodedX25519PrivateKey(Files.readString(privKeyPath).strip());
+ var secretShared = SharedKeyGenerator.fromSealedKey(sealedSharedKey, privateKey);
+ var resealedShared = SharedKeyGenerator.reseal(secretShared, recipientPubKey, recipientKeyId);
+
+ invocation.stdOut().println(resealedShared.sealedSharedKey().toTokenString());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return 0;
+ }
+}
diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ToolUtils.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ToolUtils.java
new file mode 100644
index 00000000000..32e9c6679f6
--- /dev/null
+++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ToolUtils.java
@@ -0,0 +1,25 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.security.tool.crypto;
+
+import com.yahoo.security.KeyId;
+import com.yahoo.security.SealedSharedKey;
+
+import java.util.Optional;
+
+/**
+ * @author vekterli
+ */
+public class ToolUtils {
+
+ static void verifyExpectedKeyId(SealedSharedKey sealedSharedKey, Optional<String> maybeKeyId) {
+ if (maybeKeyId.isPresent()) {
+ var myKeyId = KeyId.ofString(maybeKeyId.get());
+ if (!myKeyId.equals(sealedSharedKey.keyId())) {
+ // Don't include raw key bytes array verbatim in message (may contain control chars etc.)
+ throw new IllegalArgumentException("Key ID specified with --expected-key-id does not match key ID " +
+ "used when generating the supplied token");
+ }
+ }
+ }
+
+}