Skip to content

Instantly share code, notes, and snippets.

@jimklimov
Last active December 8, 2023 11:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimklimov/07913a3e30a7d7ec50e9c946117c994f to your computer and use it in GitHub Desktop.
Save jimklimov/07913a3e30a7d7ec50e9c946117c994f to your computer and use it in GitHub Desktop.
Jenkins script to dump credential contents
// Inspired by https://www.codurance.com/publications/2019/05/30/accessing-and-dumping-jenkins-credentials
// and https://stackoverflow.com/questions/35205665/jenkins-credentials-store-access-via-groovy
// and sources for found relevant credential classes (see their individual getters)
// Copyright (C) 2022-2023 by Jim Klimov, MIT License
import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials;
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
//com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
com.cloudbees.plugins.credentials.Credentials.class,
Jenkins.instance,
null,
null
)
String printBytes(byte[] bytes) {
def bc = 0
def bs = ""
def plaintext = true
def s = ""
def p = ""
for (byte b in bytes) {
if (!(b == 0x0A || b == 0x0D || (b >= 0x20 && b <= 0x7F))) {
plaintext = false
}
if (bc % 16 == 0) {
if (p != "") { s += "\t" + p; p = ""}
s += String.format("\n[%08d] ", bc)
}
bc ++
s += String.format(" %02X", b)
// HOW could a byte-print cause this? => java.util.IllegalFormatCodePointException: Code point = 0xffffff82
try {
p += String.format("%c", ( (b == 0x0A || b == 0x0D) ? 0x20 : b))
bs += String.format("%c", b)
} catch(IllegalFormatCodePointException) {
p += '.'
bs += '.'
}
}
if (plaintext) {
return ("PLAINTEXT (${bytes.size()} chars):\n" + bs)
} else {
return ("BINARY (${bytes.size()} bytes):" + s)
}
}
for (c in creds) {
switch (c.class) {
case com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey:
println(String.format("=== BasicSSHUserPrivateKey\n* id=%s\n* desc=%s\n* passphrase=%s\n* username=%s\n* key(s)=%s\n", c.id, c.description, c.passphrase, c.username, c.privateKeySource.getPrivateKeys()))
break
case com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl:
println(String.format("=== UsernamePasswordCredentialsImpl\n* id=%s\n* desc=%s\n* user=%s\n* pass=%s\n", c.id, c.description, c.username, c.password))
break
case org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl:
println(String.format("=== StringCredentialsImpl (secret text)\n* id=%s\n* desc=%s\n* secret=%s\n", c.id, c.description, c.secret))
break
case com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl:
try {
def keyStore = c.getKeyStore()
println(String.format("=== CertificateCredentialsImpl\n* id=%s\n* desc=%s\n* pass=%s\n* keystore=%s\n* keystore.type=%s\n", c.id, c.description, c.password, keyStore?.toString(), keyStore?.type))
// Follows https://github.com/jenkinsci/credentials-plugin/blob/master/src/main/java/com/cloudbees/plugins/credentials/impl/CertificateCredentialsImpl.java validateCertificateKeystore()
if (keyStore?.size() > 0) {
char[] passwordChars = c.password?.getPlainText()?.toCharArray();
StringBuilder buf = new StringBuilder();
boolean first = true;
def num = 0;
for (Enumeration<String> enumeration = keyStore.aliases(); enumeration.hasMoreElements(); num++) {
String alias = enumeration.nextElement();
if (first) {
first = false;
} else {
buf.append(", ");
}
buf.append(alias);
if (keyStore.isCertificateEntry(alias)) {
def data = keyStore.getCertificate(alias);
println("* [entry #${num}]\tCERT\talias:'${alias}'\t<${data.class.name}>: " + data)
} else if (keyStore.isKeyEntry(alias)) {
try {
def data = keyStore.getKey(alias, passwordChars);
println("* [entry #${num}]\tKEY\talias:'${alias}'\t<${data.class.name}>: " + data)
} catch (/*UnrecoverableEntryException*/ Exception e) {
println("* [entry #${num}]\tKEY\talias:'${alias}'\tLoadKeyFailed" + (passwordChars == null ? " (with <null> password)" : "") + ": " + e.getMessage())
}
}
}
println(String.format("\n* subjectDN=%s\n* collected keystore aliases=%s\n",
StandardCertificateCredentials.NameProvider.getSubjectDN(keyStore),
buf.toString()));
} else {
println("FAILED to inspect keystore: seems empty\n")
}
} catch (/*KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException*/ Exception e) {
println("FAILED to inspect keystore: " + e.getMessage() + "\n")
}
break
case org.jenkinsci.plugins.plaincredentials.impl.FileCredentialsImpl:
println(String.format("=== FileCredentialsImpl\n* id=%s\n* desc=%s\n* filename=%s\n* secret=%s\n", c.id, c.description, c.fileName, printBytes(c.secretBytes.getPlainData())))
break
default:
println(String.format("=== ${c.class}\n* id=%s\n* desc=%s\ndump=%s\n", c.id, c.description, c.collect{it}))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment