Skip to content
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

Handle password with empty string for certificate auth #581

Merged
merged 1 commit into from
Dec 10, 2024

Conversation

timja
Copy link
Member

@timja timja commented Dec 7, 2024

image

Testing done

Uploaded a cert generated inside Azure Key Vault

Tested with jenkinsci/azure-credentials-plugin#274
and checked end 2 end and I can provision VMs with https://plugins.jenkins.io/azure-vm-agents using certificate auth

Submitter checklist

  • Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • Ensure that the pull request title represents the desired changelog entry
  • Please describe what you did
  • Link to relevant issues in GitHub or Jira
  • Link to relevant pull requests, esp. upstream and downstream changes
  • Ensure you have provided tests - that demonstrates feature works or fixes the issue

private static char[] toCharArray(@NonNull Secret password) {
String plainText = Util.fixEmpty(password.getPlainText());
return plainText == null ? null : plainText.toCharArray();
return password.getPlainText().toCharArray();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty string is used at least commonly in Azure if not elsewhere for PFX files.

Shouldn't modify what the user has passed here

@@ -248,7 +246,7 @@ public FormValidation doCheckPassword(@QueryParameter String value) {
return FormValidation.error(Messages.CertificateCredentialsImpl_ShortPasswordFIPS());
}
if (pw.isEmpty()) {
return FormValidation.warning(Messages.CertificateCredentialsImpl_NoPassword());
return FormValidation.ok(Messages.CertificateCredentialsImpl_NoPassword());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. warnings/errors shouldn't be shown when you open a form
  2. this is a valid usecase but its still worth making people aware so I left it as ok

@@ -624,9 +622,7 @@ protected static FormValidation validateCertificateKeystore(byte[] keystoreBytes
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
return FormValidation.warning(e, Messages.CertificateCredentialsImpl_LoadKeystoreFailed());
} finally {
if (passwordChars != null) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer possible to be null here

@@ -739,6 +735,9 @@ public FormValidation doCheckCertChain(@QueryParameter String value) {
List<PEMEncodable> pemEncodables = PEMEncodable.decodeAll(pemCerts, null);
long count = pemEncodables.stream().map(PEMEncodable::toCertificate).filter(Objects::nonNull).count();
if (count < 1) {
if (Util.fixEmpty(value) == null) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warnings/errors shouldn't be shown when you open a form

The form has a clear empty state so we don't need the same content as we did with the password for the pfx:

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree if Jenkins did not allow you to hit "Save" or Apply when the form was in an invalid state or otherwise called out mandatory parameters from optional ones, but it does neither.

Hitting save leads to an angry Jenkins which is IMO a worse UX that the error.

2024-12-10 11:23:30.372+0000 [id=41]    WARNING h.i.i.InstallUncaughtExceptionHandler#handleException: Caught unhandled exception with ID a655cb61-a72d-4d07-a85b-97a161a42acd
java.io.IOException: expected one key but got 0
        at PluginClassLoader for credentials//com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl$PEMEntryKeyStoreSource.toKeyStore(CertificateCredentialsImpl.java:703)
        at PluginClassLoader for credentials//com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl$PEMEntryKeyStoreSource.toKeyStore(CertificateCredentialsImpl.java:691)
        at PluginClassLoader for credentials//com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl.<init>(CertificateCredentialsImpl.java:140)
Caused: java.lang.IllegalArgumentException: KeyStore is not valid.

@@ -771,6 +770,9 @@ public FormValidation doCheckPrivateKey(@QueryParameter String value,
List<PEMEncodable> pemEncodables = PEMEncodable.decodeAll(key, toCharArray(Secret.fromString(password)));
long count = pemEncodables.stream().map(PEMEncodable::toPrivateKey).filter(Objects::nonNull).count();
if (count == 0) {
if (Util.fixEmpty(value) == null) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warnings/errors shouldn't be shown when you open a form

The form has a clear empty state so we don't need the same content as we did with the password for the pfx:

image

Comment on lines -27 to -28
CertificateCredentialsImpl.LoadKeyFailed=Could retrieve key "{0}"
CertificateCredentialsImpl.LoadKeyFailedQueryEmptyPassword=Could retrieve key "{0}". You may need to provide a password
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these error messages made no sense before

@timja timja marked this pull request as ready for review December 7, 2024 23:02
@timja timja requested a review from a team as a code owner December 7, 2024 23:02
@timja timja requested a review from jtnord December 7, 2024 23:02
Copy link
Member

@jtnord jtnord left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this fixes the issue on azure, but breaks for others where there is no password at all (rather than an empty password)

*/
@CheckForNull
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a difference between no password and an empty password.

It seems like Azure is using the empty password rather than no password (why I have no idea).

https://stackoverflow.com/a/53523999 appears to confirm this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure looks like other systems try both empty string and null

Copy link
Member

@jtnord jtnord left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested with manual uploads of unprotected PEMs and worked fine.

Attempted to create a passwordless pkcs12 and failed (openssl and via java KeyStore) so taking this as a non password protected pkcs12 store is exceptionally rare.

@jtnord
Copy link
Member

jtnord commented Dec 10, 2024

Thanks for the investigation and fix

@jtnord jtnord merged commit 6017143 into jenkinsci:master Dec 10, 2024
18 checks passed
@timja timja deleted the azure-pfx branch December 10, 2024 12:15
@timja
Copy link
Member Author

timja commented Dec 10, 2024

Attempted to create a passwordless pkcs12 and failed (openssl and via java KeyStore) so taking this as a non password protected pkcs12 store is exceptionally rare.

I saw a bug report on the Java issue tracker which says it should be possible from Java 18 but YMMV, I expect this is the more common case...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants