-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
solved issue "certutil: large passwords not set" #30944 #36689
Changes from all commits
9f2be48
51cfd77
c0943be
de70a40
1da475e
2e58e7a
6fe7e60
b885cae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,6 +108,11 @@ public class CertificateTool extends LoggingAwareMultiCommand { | |
Pattern.compile("[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1," + MAX_FILENAME_LENGTH + "}"); | ||
private static final int DEFAULT_KEY_SIZE = 2048; | ||
|
||
// Older versions of OpenSSL had a max internal password length. | ||
// We issue warnings when writing files with passwords that would not be usable in those versions of OpenSSL. | ||
static final String OLD_OPENSSL_VERSION = "1.1.0"; | ||
static final int MAX_PASSWORD_OLD_OPENSSL = 50; | ||
|
||
/** | ||
* Wraps the certgen object parser. | ||
*/ | ||
|
@@ -349,9 +354,8 @@ CAInfo getCAInfo(Terminal terminal, OptionSet options, Environment env) throws E | |
private CAInfo loadPkcs12CA(Terminal terminal, OptionSet options, Environment env) throws Exception { | ||
Path path = resolvePath(options, caPkcs12PathSpec); | ||
char[] passwordOption = getChars(caPasswordSpec.value(options)); | ||
|
||
Map<Certificate, Key> keys = withPassword("CA (" + path + ")", passwordOption, | ||
terminal, password -> CertParsingUtils.readPkcs12KeyPairs(path, password, a -> password)); | ||
Map<Certificate, Key> keys = withPassword("CA (" + path + ")", passwordOption, terminal, false, | ||
password -> CertParsingUtils.readPkcs12KeyPairs(path, password, a -> password)); | ||
|
||
if (keys.size() != 1) { | ||
throw new IllegalArgumentException("expected a single key in file [" + path.toAbsolutePath() + "] but found [" + | ||
|
@@ -391,10 +395,11 @@ CAInfo generateCA(Terminal terminal, OptionSet options) throws Exception { | |
|
||
if (options.hasArgument(caPasswordSpec)) { | ||
char[] password = getChars(caPasswordSpec.value(options)); | ||
checkAndConfirmPasswordLengthForOpenSSLCompatibility(password, terminal, false); | ||
return new CAInfo(caCert, keyPair.getPrivate(), true, password); | ||
} | ||
if (options.has(caPasswordSpec)) { | ||
return withPassword("CA Private key", null, terminal, p -> new CAInfo(caCert, keyPair.getPrivate(), true, p.clone())); | ||
return withPassword("CA Private key", null, terminal, true, p -> new CAInfo(caCert, keyPair.getPrivate(), true, p.clone())); | ||
} | ||
return new CAInfo(caCert, keyPair.getPrivate(), true, null); | ||
} | ||
|
@@ -541,9 +546,9 @@ static void writePkcs12(String fileName, OutputStream output, String alias, Cert | |
char[] password, Terminal terminal) throws Exception { | ||
final KeyStore pkcs12 = KeyStore.getInstance("PKCS12"); | ||
pkcs12.load(null); | ||
withPassword(fileName, password, terminal, p12Password -> { | ||
withPassword(fileName, password, terminal, true, p12Password -> { | ||
if (isAscii(p12Password)) { | ||
pkcs12.setKeyEntry(alias, pair.key, p12Password, new Certificate[]{pair.cert}); | ||
pkcs12.setKeyEntry(alias, pair.key, p12Password, new Certificate[] { pair.cert }); | ||
if (caCert != null) { | ||
pkcs12.setCertificateEntry("ca", caCert); | ||
} | ||
|
@@ -784,7 +789,7 @@ void generateAndWriteSignedCertificates(Path output, boolean writeZipFile, Optio | |
final String keyFileName = entryBase + ".key"; | ||
outputStream.putNextEntry(new ZipEntry(keyFileName)); | ||
if (usePassword) { | ||
withPassword(keyFileName, outputPassword, terminal, password -> { | ||
withPassword(keyFileName, outputPassword, terminal, true, password -> { | ||
pemWriter.writeObject(pair.key, getEncrypter(password)); | ||
return null; | ||
}); | ||
|
@@ -924,16 +929,47 @@ static PEMEncryptor getEncrypter(char[] password) { | |
return new JcePEMEncryptorBuilder("AES-128-CBC").setProvider(BC_PROV).build(password); | ||
} | ||
|
||
private static <T, E extends Exception> T withPassword(String description, char[] password, Terminal terminal, | ||
/** | ||
* Checks whether the supplied password exceeds the maximum length supported by older OpenSSL versions. | ||
* A warning message is printed to the terminal if the password is too long. If {@code confirm} is true, then the user | ||
* (via the terminal) is asked to confirm whether to continue with the potentially problematic password. | ||
* @return {@code false} if the password is too long <em>and</em> the user elects to reject it, otherwise {@code true}. | ||
*/ | ||
static boolean checkAndConfirmPasswordLengthForOpenSSLCompatibility(char[] password, Terminal terminal, boolean confirm) { | ||
if (password.length > MAX_PASSWORD_OLD_OPENSSL) { | ||
terminal.println( | ||
Verbosity.SILENT, | ||
"Warning: Your password exceeds " | ||
+ MAX_PASSWORD_OLD_OPENSSL | ||
+ " characters. Versions of OpenSSL older than " | ||
+ OLD_OPENSSL_VERSION | ||
+ " may not be able to read this file." | ||
); | ||
if (confirm) { | ||
return terminal.promptYesNo("Do you want to continue?", true); | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
private static <T, E extends Exception> T withPassword(String description, char[] password, Terminal terminal, boolean checkLength, | ||
CheckedFunction<char[], T, E> body) throws E { | ||
if (password == null) { | ||
char[] promptedValue = terminal.readSecret("Enter password for " + description + " : "); | ||
try { | ||
return body.apply(promptedValue); | ||
} finally { | ||
Arrays.fill(promptedValue, (char) 0); | ||
while (true) { | ||
char[] promptedValue = terminal.readSecret("Enter password for " + description + " : "); | ||
if (checkLength && checkAndConfirmPasswordLengthForOpenSSLCompatibility(promptedValue, terminal, true) == false) { | ||
continue; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The formatting of thise code (spaces around brackets etc) is different to the way the rest of this source code is formatted. By the time we get to the end of this review process, we'll want to have it formatted in the same style as the surrounding code. |
||
try { | ||
return body.apply(promptedValue); | ||
} finally { | ||
Arrays.fill(promptedValue, (char) 0); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The We need to only perform this check/warning if the password is being applied to a new file. |
||
} | ||
} else { | ||
if (checkLength) { | ||
checkAndConfirmPasswordLengthForOpenSSLCompatibility(password, terminal, false); | ||
} | ||
return body.apply(password); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? They're constants, so we don't need the braces in order to see where the variable part is substituted in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the braces are for emphasis. But if they are just for variables, then you are right that we don't need them here.