From 5da1e5894d039b4d9becb97fbbdd24759d4fb078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28=40MarkL4YG=29?= Date: Sat, 23 Nov 2024 12:36:38 +0100 Subject: [PATCH] feat: Verify key is unencrypted using two random passphrases --- .../openssl/OpenSSLCertificateCreator.java | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java index b3d1007..18730c3 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLCertificateCreator.java @@ -15,6 +15,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -25,7 +26,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.zeroturnaround.exec.ProcessExecutor; @@ -135,6 +135,7 @@ public class OpenSSLCertificateCreator { "genrsa", "-out", keyFile.toString(), + "-aes256", "-passout", "env:KEY_PASS", Integer.toString(request.getRequestedKeyLength()) @@ -282,20 +283,28 @@ public class OpenSSLCertificateCreator { } } + /** + * Checks whether the provided key file is encrypted using a passphrase + */ public boolean isKeyEncrypted(@NonNull Path keyFile) throws InterruptedException, CommandLineOperationException { - // If any random passphrase works, the key is not encrypted. - return !verifyKeyPassphrase(keyFile, null); + // If the key is not encrypted, any passphrase will work -> so generate a random one to check. + String passphrase = UUID.randomUUID().toString(); + boolean firstPass = verifyKeyPassphrase(keyFile, passphrase); + if (firstPass) { + // Try with another random passphrase in case we randomly got lucky guessing the passphrase the first time. + passphrase = UUID.randomUUID().toString(); + return !verifyKeyPassphrase(keyFile, passphrase); + } + return true; } /** * Verifies a passphrase against a provided key. - * @implNote Due to the implementation of the OpenSSL cli, any password will be valid for unencrypted keys. (Check with {@link #isKeyEncrypted(Path) or by passing {@code null} as the passphrase.) + * @implNote Due to the implementation of the OpenSSL cli, any password will be valid for unencrypted keys. (Check with {@link #isKeyEncrypted(Path).) */ - public boolean verifyKeyPassphrase(@NonNull Path keyFile, @Nullable String passphrase) + public boolean verifyKeyPassphrase(@NonNull Path keyFile, @NonNull String passphrase) throws CommandLineOperationException, InterruptedException { - // Run OpenSSL command: openssl rsa -check -in -passin pass: -noout and check exit code try { - String keyPass = passphrase != null ? passphrase : passwordProvider.generateNewPassword(); StartedProcess verifyCommand = new ProcessExecutor() .command( resolveOpenSSL(), @@ -304,16 +313,14 @@ public class OpenSSLCertificateCreator { "-in", keyFile.toString(), "-passin", - "pass:" + keyPass, + "pass:" + passphrase, "-noout" ) .redirectOutput(Slf4jStream.ofCaller().asError()) .redirectError(Slf4jStream.ofCaller().asError()) .start(); var verifyResult = verifyCommand.getFuture().get(); - boolean commandSuccess = verifyResult.getExitValue() == 0; - log.trace("Key check {} with passphrase provided={}", commandSuccess, passphrase != null); - return commandSuccess; + return verifyResult.getExitValue() == 0; } catch (IOException | InterruptedException | ExecutionException e) { throw new CommandLineOperationException("Failed to verify key encryption", e); }