feat: Verify key is unencrypted using two random passphrases
This commit is contained in:
parent
334d97c883
commit
5da1e5894d
1 changed files with 18 additions and 11 deletions
|
@ -15,6 +15,7 @@ import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -25,7 +26,6 @@ import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.zeroturnaround.exec.ProcessExecutor;
|
import org.zeroturnaround.exec.ProcessExecutor;
|
||||||
|
@ -135,6 +135,7 @@ public class OpenSSLCertificateCreator {
|
||||||
"genrsa",
|
"genrsa",
|
||||||
"-out",
|
"-out",
|
||||||
keyFile.toString(),
|
keyFile.toString(),
|
||||||
|
"-aes256",
|
||||||
"-passout",
|
"-passout",
|
||||||
"env:KEY_PASS",
|
"env:KEY_PASS",
|
||||||
Integer.toString(request.getRequestedKeyLength())
|
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 {
|
public boolean isKeyEncrypted(@NonNull Path keyFile) throws InterruptedException, CommandLineOperationException {
|
||||||
// If any random passphrase works, the key is not encrypted.
|
// If the key is not encrypted, any passphrase will work -> so generate a random one to check.
|
||||||
return !verifyKeyPassphrase(keyFile, null);
|
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.
|
* 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, <em>any password</em> 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 {
|
throws CommandLineOperationException, InterruptedException {
|
||||||
// Run OpenSSL command: openssl rsa -check -in <keyFile> -passin pass:<randomKey> -noout and check exit code
|
|
||||||
try {
|
try {
|
||||||
String keyPass = passphrase != null ? passphrase : passwordProvider.generateNewPassword();
|
|
||||||
StartedProcess verifyCommand = new ProcessExecutor()
|
StartedProcess verifyCommand = new ProcessExecutor()
|
||||||
.command(
|
.command(
|
||||||
resolveOpenSSL(),
|
resolveOpenSSL(),
|
||||||
|
@ -304,16 +313,14 @@ public class OpenSSLCertificateCreator {
|
||||||
"-in",
|
"-in",
|
||||||
keyFile.toString(),
|
keyFile.toString(),
|
||||||
"-passin",
|
"-passin",
|
||||||
"pass:" + keyPass,
|
"pass:" + passphrase,
|
||||||
"-noout"
|
"-noout"
|
||||||
)
|
)
|
||||||
.redirectOutput(Slf4jStream.ofCaller().asError())
|
.redirectOutput(Slf4jStream.ofCaller().asError())
|
||||||
.redirectError(Slf4jStream.ofCaller().asError())
|
.redirectError(Slf4jStream.ofCaller().asError())
|
||||||
.start();
|
.start();
|
||||||
var verifyResult = verifyCommand.getFuture().get();
|
var verifyResult = verifyCommand.getFuture().get();
|
||||||
boolean commandSuccess = verifyResult.getExitValue() == 0;
|
return verifyResult.getExitValue() == 0;
|
||||||
log.trace("Key check {} with passphrase provided={}", commandSuccess, passphrase != null);
|
|
||||||
return commandSuccess;
|
|
||||||
} catch (IOException | InterruptedException | ExecutionException e) {
|
} catch (IOException | InterruptedException | ExecutionException e) {
|
||||||
throw new CommandLineOperationException("Failed to verify key encryption", e);
|
throw new CommandLineOperationException("Failed to verify key encryption", e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue