Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion devtools/cli/src/main/java/io/quarkus/cli/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@

import io.quarkus.cli.common.HelpOption;
import io.quarkus.cli.common.OutputOptionMixin;
import io.quarkus.cli.config.Decrypt;
import io.quarkus.cli.config.Encrypt;
import io.quarkus.cli.config.RemoveConfig;
import io.quarkus.cli.config.SetConfig;
import picocli.CommandLine;
import picocli.CommandLine.Command;

@Command(name = "config", header = "Manage Quarkus configuration", subcommands = { SetConfig.class, RemoveConfig.class,
Encrypt.class })
Encrypt.class, Decrypt.class })
public class Config implements Callable<Integer> {
@CommandLine.Mixin(name = "output")
protected OutputOptionMixin output;
Expand Down
76 changes: 76 additions & 0 deletions devtools/cli/src/main/java/io/quarkus/cli/config/Decrypt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.quarkus.cli.config;

import static io.quarkus.devtools.messagewriter.MessageIcons.SUCCESS_ICON;
import static java.nio.charset.StandardCharsets.UTF_8;

import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.concurrent.Callable;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import io.quarkus.cli.config.Encrypt.KeyFormat;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;

@Command(name = "decrypt", aliases = "dec", header = "Decrypt Secrets", description = "Decrypt a Secret value using the AES/GCM/NoPadding algorithm as a default.")
public class Decrypt extends BaseConfigCommand implements Callable<Integer> {
@Parameters(index = "0", paramLabel = "SECRET", description = "The secret value to decrypt")
String secret;

@Parameters(index = "1", paramLabel = "DECRYPTION KEY", description = "The decryption key")
String decryptionKey;

@Option(names = { "-f", "--format" }, description = "The decryption key format (base64 / plain)", defaultValue = "base64")
KeyFormat decryptionKeyFormat;

@Option(hidden = true, names = { "-a", "--algorithm" }, description = "Algorithm", defaultValue = "AES")
String algorithm;

@Option(hidden = true, names = { "-m", "--mode" }, description = "Mode", defaultValue = "GCM")
String mode;

@Option(hidden = true, names = { "-p", "--padding" }, description = "Padding", defaultValue = "NoPadding")
String padding;

@Option(hidden = true, names = { "-q", "--quiet" }, defaultValue = "false")
boolean quiet;

@Override
public Integer call() throws Exception {
if (decryptionKey.startsWith("\\\"") && decryptionKey.endsWith("\"\\")) {
decryptionKey = decryptionKey.substring(2, decryptionKey.length() - 2);
}

byte[] decryptionKeyBytes;
if (decryptionKeyFormat.equals(KeyFormat.base64)) {
decryptionKeyBytes = Base64.getUrlDecoder().decode(decryptionKey);
} else {
decryptionKeyBytes = decryptionKey.getBytes(UTF_8);
}

Cipher cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding);
ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.getUrlDecoder().decode(secret.getBytes(UTF_8)));
int ivLength = byteBuffer.get();
byte[] iv = new byte[ivLength];
byteBuffer.get(iv);
byte[] encrypted = new byte[byteBuffer.remaining()];
byteBuffer.get(encrypted);

MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(decryptionKeyBytes);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sha256.digest(), "AES"), new GCMParameterSpec(128, iv));
String decrypted = new String(cipher.doFinal(encrypted), UTF_8);

if (!quiet) {
String success = SUCCESS_ICON + " The secret @|bold " + secret + "|@ was decrypted to @|bold " + decrypted + "|@";
output.info(success);
}

return 0;
}
}
6 changes: 3 additions & 3 deletions devtools/cli/src/main/java/io/quarkus/cli/config/Encrypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@

@Command(name = "encrypt", aliases = "enc", header = "Encrypt Secrets", description = "Encrypt a Secret value using the AES/GCM/NoPadding algorithm as a default. The encryption key is generated unless a specific key is set with the --key option.")
public class Encrypt extends BaseConfigCommand implements Callable<Integer> {
@Parameters(index = "0", paramLabel = "SECRET", description = "The Secret value to encrypt")
@Parameters(index = "0", paramLabel = "SECRET", description = "The secret value to encrypt")
String secret;

@Option(names = { "-k", "--key" }, description = "The Encryption Key")
@Option(names = { "-k", "--key" }, description = "The encryption Key")
String encryptionKey;

@Option(names = { "-f", "--format" }, description = "The Encryption Key Format (base64 / plain)", defaultValue = "base64")
@Option(names = { "-f", "--format" }, description = "The encryption key format (base64 / plain)", defaultValue = "base64")
KeyFormat encryptionKeyFormat;

@Option(hidden = true, names = { "-a", "--algorithm" }, description = "Algorithm", defaultValue = "AES")
Expand Down
39 changes: 39 additions & 0 deletions devtools/cli/src/test/java/io/quarkus/cli/config/DecryptTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.quarkus.cli.config;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.nio.file.Path;
import java.util.Scanner;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.io.TempDir;

import io.quarkus.cli.CliDriver;

@DisabledOnOs(value = OS.WINDOWS, disabledReason = "Parsing the stdout is not working on Github Windows, maybe because of the console formatting. I did try it in a Windows box and it works fine.")
public class DecryptTest {
@TempDir
Path tempDir;

@Test
void decryptPlain() throws Exception {
CliDriver.Result result = CliDriver.execute(tempDir, "config", "decrypt",
"DPZqAC4GZNAXi6_43A4O2SBmaQssGkq6PS7rz8tzHDt1", "somearbitrarycrazystringthatdoesnotmatter", "-f=plain");
Scanner scanner = new Scanner(result.getStdout());
String[] split = scanner.nextLine().split(" ");
String secret = split[split.length - 1];
assertEquals("1234", secret);
}

@Test
void decryptBase64() throws Exception {
CliDriver.Result result = CliDriver.execute(tempDir, "config", "decrypt",
"DJNrZ6LfpupFv6QbXyXhvzD8eVDnDa_kTliQBpuzTobDZxlg", "c29tZWFyYml0cmFyeWNyYXp5c3RyaW5ndGhhdGRvZXNub3RtYXR0ZXI");
Scanner scanner = new Scanner(result.getStdout());
String[] split = scanner.nextLine().split(" ");
String secret = split[split.length - 1];
assertEquals("decoded", secret);
}
}
Loading