diff --git a/README.adoc b/README.adoc index dba1527e..8bd7969e 100644 --- a/README.adoc +++ b/README.adoc @@ -44,6 +44,9 @@ Use Basic Authentication to ensure that only authorized users can access your pr * Supports Form Authentication: Form Authentication enables users to authenticate themselves by submitting a username and password through a form, ensuring that only authorized users can access your resources. +* Supports Certificate-based Authentication: +Use a certificate from a Jenkins stored credential to authenticate your HTTPS requests to a remote server. + * Specify a required string in the response: Ensure that a specific string is present in the response by specifying it beforehand. If the string is not present, the build will fail, alerting you to the issue. @@ -270,6 +273,22 @@ def response = httpRequest authenticate: 'my-jenkins-credential-id', url: 'https://api.github.com/user/jenkinsci' ---- +You can send an SSL request with authentication by user certificate; +for a private CA, make sure to first add the CA certificate is as +"Trusted", then add the user key along with certification chain up +to same CA certificate, into your PKCS12 keystore file which you +upload to Jenkins credentials, and you also must use a non-trivial +password for that keystore. Keep in mind that for systems under test +which create their own self-signed CA and HTTPS protection, you can +programmatically create and upload the credentials, into a domain +where the job has write access (its folder etc.) + +[source,groovy] +---- +def response = httpRequest authentication: 'user_with_cert_and_ca', + url: 'https://sut123.local.domain:8443/api/v1/status/debug' +---- + A basic WebDAV upload can be built using ``MKCOL`` and ``PUT`` like so: [source,groovy] @@ -288,7 +307,7 @@ httpRequest authenticate: 'my-jenkins-credential-id', uploadFile: './local/path/to/report.html' ---- -For details on the Pipeline features, use the Pipeline snippet generator in the Pipeline job +For details on the Pipeline features, use the Pipeline snippet generator in the Pipeline job configuration. [WARNING] diff --git a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java index aec20027..9474ac98 100644 --- a/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java +++ b/src/main/java/jenkins/plugins/http_request/HttpRequestExecution.java @@ -242,7 +242,7 @@ private HttpRequestExecution( } } if (credential instanceof StandardCertificateCredentials) { - auth = new CertificateAuthentication((StandardCertificateCredentials) credential); + auth = new CertificateAuthentication((StandardCertificateCredentials) credential, this.ignoreSslErrors); } } } @@ -435,6 +435,15 @@ private ResponseContentSupplier executeRequest( CloseableHttpClient httpclient, HttpClientUtil clientUtil, HttpRequestBase httpRequestBase, HttpContext context) throws IOException { ResponseContentSupplier responseContentSupplier; +/* + // TODO: pick interesting fields/getters from these classes: + logger().println("Sending HTTP request with" + + " CloseableHttpClient=" + httpclient.toString() + + " HttpClientUtil=" + clientUtil.toString() + + " HttpRequestBase=" + httpRequestBase.toString() + + " HttpContext=" + context.toString() + ); +*/ try { final HttpResponse response = clientUtil.execute(httpclient, context, httpRequestBase, logger()); // The HttpEntity is consumed by the ResponseContentSupplier diff --git a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java index 54d44a57..39c42cc4 100644 --- a/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java +++ b/src/main/java/jenkins/plugins/http_request/auth/CertificateAuthentication.java @@ -2,21 +2,36 @@ import java.io.IOException; import java.io.PrintStream; +import java.security.KeyStore; import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContexts; +import org.apache.http.ssl.TrustStrategy; import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials; +import hudson.Util; + public class CertificateAuthentication implements Authenticator { private final StandardCertificateCredentials credentials; + private final boolean ignoreSslErrors; + public CertificateAuthentication(StandardCertificateCredentials credentials) { this.credentials = credentials; + this.ignoreSslErrors = false; + } + + public CertificateAuthentication(StandardCertificateCredentials credentials, boolean ignoreSslErrors) { + this.credentials = credentials; + this.ignoreSslErrors = ignoreSslErrors; } @Override @@ -30,12 +45,59 @@ public CloseableHttpClient authenticate(HttpClientBuilder clientBuilder, HttpRequestBase requestBase, PrintStream logger) throws IOException { try { - clientBuilder.setSSLContext( - SSLContexts.custom().loadKeyMaterial(credentials.getKeyStore(), - credentials.getPassword().getPlainText().toCharArray()).build()); + KeyStore keyStore = credentials.getKeyStore(); + // Note: modeled after CertificateCredentialsImpl.toCharArray() + // which ignores both null and "" empty passwords, even though + // technically the byte stream reader there *can* decipher with + // "" as the password. The null value is explicitly ignored by + // ultimate sun.security.pkcs12.PKCS12KeyStore::engineLoad(), + // for more context see comments in its sources. + String keyStorePass = Util.fixEmpty(credentials.getPassword().getPlainText()); + char[] keyStorePassChars = (keyStorePass == null ? null : keyStorePass.toCharArray()); + SSLContextBuilder contextBuilder = SSLContexts.custom(); + + if (keyStorePassChars == null) { + logger.println("WARNING: Jenkins Certificate Credential '" + + credentials.getId() + "' was saved without a password, " + + "so any certificates (and chain of trust) in it would " + + "be ignored by Java PKCS12 support!"); + } + + try { + TrustStrategy trustStrategy = null; + if (ignoreSslErrors) { + // e.g. for user certificate issued by test CA so + // is not persisted in the system 'cacerts' file. + // Hopefully it is at least added/trusted in the + // generated keystore... + trustStrategy = new TrustAllStrategy(); + //trustStrategy = new TrustSelfSignedStrategy(); + } + + contextBuilder = contextBuilder.loadTrustMaterial(keyStore, trustStrategy); + logger.println("Added Trust Material from provided KeyStore"); + } catch (Exception e) { + logger.println("Failed to add Trust Material from provided KeyStore (so Key Material might end up untrusted): " + e.getMessage()); + // Do no re-throw, maybe system trust would suffice? + // TODO: Can we identify lack of trust material in + // key store vs. inability to load what exists?.. + // And do we really care about the difference? + } + + contextBuilder = contextBuilder.loadKeyMaterial(keyStore, keyStorePassChars); + logger.println("Added Key Material from provided KeyStore"); + + clientBuilder = clientBuilder.setSSLContext(contextBuilder.build()); + logger.println("Set SSL context for the HTTP client builder"); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + contextBuilder.build()); + clientBuilder = clientBuilder.setSSLSocketFactory(sslsf); + logger.println("Set SSL socket factory for the HTTP client builder"); return clientBuilder.build(); } catch (Exception e) { + logger.println("Failed to set SSL context: " + e.getMessage()); throw new IOException(e); } } diff --git a/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java b/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java index ce9f29f4..5c876532 100644 --- a/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java +++ b/src/test/java/jenkins/plugins/http_request/HttpRequestStepCredentialsTest.java @@ -6,27 +6,54 @@ import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.CredentialsStore; +import com.cloudbees.plugins.credentials.SecretBytes; import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.cloudbees.plugins.credentials.domains.Domain; +import com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl; import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; import hudson.model.Fingerprint; import hudson.model.Result; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.Collections; import jenkins.model.Jenkins; + +import org.apache.commons.io.FileUtils; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.htmlunit.html.HtmlPage; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; /** * @author Mark Waite */ public class HttpRequestStepCredentialsTest extends HttpRequestTestBase { + // For developers: set to `true` so that pipeline console logs show + // up in System.out (and/or System.err) of the plugin test run by + // mvn test -Dtest="HttpRequestStepCredentialsTest" + private boolean verbosePipelines = false; + String getLogAsStringPlaintext(WorkflowRun f) throws java.io.IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + f.getLogText().writeLogTo(0, baos); + return baos.toString(); + } + + // From CertificateCredentialImplTest + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + private File p12simple; + private File p12trusted; private StandardCredentials getInvalidCredential() { String username = "bad-user"; @@ -36,6 +63,32 @@ private StandardCredentials getInvalidCredential() { return new UsernamePasswordCredentialsImpl(scope, id, "desc: " + id, username, password); } + private StandardCredentials getCertificateCredentialSimple() throws IOException { + if (p12simple == null) { + // Contains a private key + openvpn certs, + // as alias named "1" (according to keytool) + p12simple = tmp.newFile("test.p12"); + FileUtils.copyURLToFile(HttpRequestStepCredentialsTest.class.getResource("test.p12"), p12simple); + } + + SecretBytes uploadedKeystore = SecretBytes.fromBytes(Files.readAllBytes(p12simple.toPath())); + CertificateCredentialsImpl.UploadedKeyStoreSource storeSource = new CertificateCredentialsImpl.UploadedKeyStoreSource(uploadedKeystore); + return new CertificateCredentialsImpl(null, "cred_cert_simple", null, "password", storeSource); + } + + private StandardCredentials getCertificateCredentialTrusted() throws IOException { + if (p12trusted == null) { + // Contains a private key + openvpn certs as alias named "1", + // and another alias named "ca" with trustedKeyEntry for CA + p12trusted = tmp.newFile("testTrusted.p12"); + FileUtils.copyURLToFile(HttpRequestStepCredentialsTest.class.getResource("testTrusted.p12"), p12trusted); + } + + SecretBytes uploadedKeystore = SecretBytes.fromBytes(Files.readAllBytes(p12trusted.toPath())); + CertificateCredentialsImpl.UploadedKeyStoreSource storeSource = new CertificateCredentialsImpl.UploadedKeyStoreSource(uploadedKeystore); + return new CertificateCredentialsImpl(null, "cred_cert_with_ca", null, "password", storeSource); + } + private CredentialsStore store = null; @Before @@ -105,4 +158,166 @@ public void trackCredentials() throws Exception { assertThat(page.getElementById("usage-present"), notNullValue()); assertThat(page.getAnchorByText(proj.getFullDisplayName()), notNullValue()); } + + // A set of tests with certificate credentials in different contexts + // TODO: Test on remote agent as in https://github.com/jenkinsci/credentials-plugin/pull/391 + // but this requires that PR to be merged first, so credentials-plugin + // processes snapshot() and readable keystore data gets to remote agent. + // Note that the tests below focus on ability of the plugin to load and + // process the key store specified by the credential, rather than that + // it is usable further. It would be a separate effort to mock up a web + // server protected by HTTPS and using certificates for login (possibly + // user and server backed by two different CA's), and query that. + String cpsScriptCredentialTestHttpRequest(String id, String runnerTag) { + // Note: we accept any outcome (for the plugin, unresolved host is HTTP-404) + // but it may not crash making use of the credential + // Note: cases withLocalCertLookup also need cpsScriptCredentialTestImports() + return "def authentication='" + id + "';\n" + + "\n" + + "echo \"Querying HTTPS with credential...\"\n" + + "def response = httpRequest(url: 'https://github.xcom/api/v3',\n" + + " httpMode: 'GET',\n" + + " authentication: authentication,\n" + + " consoleLogResponseBody: true,\n" + + " contentType : 'APPLICATION_FORM',\n" + + " validResponseCodes: '100:599',\n" + + " quiet: false)\n" + + "println('First HTTP Request Plugin Status: '+ response.getStatus())\n" + + "println('First HTTP Request Plugin Response: '+ response.getContent())\n" + + "\n" + + "echo \"Querying HTTPS with credential again (reentrability)...\"\n" + + "response = httpRequest(url: 'https://github.xcom/api/v3',\n" + + " httpMode: 'GET',\n" + + " authentication: authentication,\n" + + " consoleLogResponseBody: true,\n" + + " contentType : 'APPLICATION_FORM',\n" + + " validResponseCodes: '100:599',\n" + + " quiet: false)\n" + + "println('Second HTTP Request Plugin Status: '+ response.getStatus())\n" + + "println('Second HTTP Request Plugin Response: '+ response.getContent())\n" + + "\n"; + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertSimpleHttpRequestOnController() throws Exception { + // Check that credentials are usable with pipeline script + // running without a node{} + StandardCredentials credential = getCertificateCredentialSimple(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + cpsScriptCredentialTestHttpRequest("cred_cert_simple", "CONTROLLER BUILT-IN"); + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_simple", run); + // Currently we always try adding the material + // and report if not failed trying (might have + // had 0 entries to add though): + //j.assertLogNotContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertSimpleHttpRequestOnNodeLocal() throws Exception { + // Check that credentials are usable with pipeline script + // running on a node{} (provided by the controller) + StandardCredentials credential = getCertificateCredentialSimple(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + "node {\n" + + cpsScriptCredentialTestHttpRequest("cred_cert_simple", "CONTROLLER NODE") + + "}\n"; + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_simple", run); + // Currently we always try adding the material + // and report if not failed trying (might have + // had 0 entries to add though): + //j.assertLogNotContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertTrustedHttpRequestOnController() throws Exception { + // Check that credentials are usable with pipeline script + // running without a node{} + StandardCredentials credential = getCertificateCredentialTrusted(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + cpsScriptCredentialTestHttpRequest("cred_cert_with_ca", "CONTROLLER BUILT-IN"); + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_with_ca", run); + j.assertLogContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + + @Test + @Issue({"JENKINS-70000", "JENKINS-70101"}) + public void testCertTrustedHttpRequestOnNodeLocal() throws Exception { + // Check that credentials are usable with pipeline script + // running on a node{} (provided by the controller) + StandardCredentials credential = getCertificateCredentialTrusted(); + store.addCredentials(Domain.global(), credential); + + // Configure the build to use the credential + WorkflowJob proj = j.jenkins.createProject(WorkflowJob.class, "proj"); + String script = + "node {\n" + + cpsScriptCredentialTestHttpRequest("cred_cert_with_ca", "CONTROLLER NODE") + + "}\n"; + proj.setDefinition(new CpsFlowDefinition(script, false)); + + // Execute the build + WorkflowRun run = proj.scheduleBuild2(0).get(); + if (verbosePipelines) System.out.println(getLogAsStringPlaintext(run)); + + // Check expectations + j.assertBuildStatus(Result.SUCCESS, run); + // Got to the end? + j.assertLogContains("HTTP Request Plugin Response: ", run); + j.assertLogContains("Using authentication: cred_cert_with_ca", run); + j.assertLogContains("Added Trust Material from provided KeyStore", run); + j.assertLogContains("Added Key Material from provided KeyStore", run); + j.assertLogContains("Treating UnknownHostException", run); + } + } diff --git a/src/test/resources/jenkins/plugins/http_request/README.txt b/src/test/resources/jenkins/plugins/http_request/README.txt new file mode 100644 index 00000000..3ff1a6dc --- /dev/null +++ b/src/test/resources/jenkins/plugins/http_request/README.txt @@ -0,0 +1,145 @@ +Test keystores: + +* test.p12 was picked from credentials-plugin + It includes one entry aliased "1" with a private key, + its cert, and issuing CA cert (as a simple chain). + The keystore password is "password" + +* testTrusted.p12 is a modification of that, adding an + entry aliased "ca" with that same CA cert imported as + trusted: +```` +# Show certs in BASE64 format, last of these is CA: +:; keytool -list -keystore test.p12 -storepass password -alias 1 -rfc + +# Save second block to "ca.pem" + +# Re-import: +:; cp test.p12 testTrusted.p12 +:; keytool -importcert -trustcacerts -alias ca -file ca.pem -keystore testTrusted.p12 -storepass password +... +Trust this certificate? [no]: yes +Certificate was added to keystore +```` + +Verification: +```` +:; keytool -list -keystore testTrusted.p12 -storepass password -rfc +Keystore type: PKCS12 +Keystore provider: SUN + +Your keystore contains 2 entries + +Alias name: 1 +Creation date: 23.11.2022 +Entry type: PrivateKeyEntry +Certificate chain length: 2 +Certificate[1]: +-----BEGIN CERTIFICATE----- +MIIDRzCCArCgAwIBAgIBATANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL +MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t +VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA1MDgw +NDE4MTYyMFoXDTE1MDgwMjE4MTYyMFowfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgT +AkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZvcnQtRnVuc3Rv +bjEPMA0GA1UEAxMGcGtjczEyMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlk +b21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMOT0PRbWiTEJTUjjiwW +yPC7hR2ruxshzWcgWZUuNg5RARnnsQfGpBK+kKp4QsJSunVCo2fmUFkU/UGYVVXK +nHMEcDtX2JqVY/bAPjxptn5k1bnvMFkKFnaAZl5Mi0K0s+D9U0ivpIaw1QXdQbw+ +w3STcv1kpy8rmyerH6KOXL1bAgMBAAGjge4wgeswCQYDVR0TBAIwADAsBglghkgB +hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE +FP9dcedV6TFtLIWOWXxIQ5h6JR45MIGQBgNVHSMEgYgwgYWAFImmYOO66j6v/GR/ +TL2M0kiN4MxGoWqkaDBmMQswCQYDVQQGEwJLRzELMAkGA1UECBMCTkExEDAOBgNV +BAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4tVEVTVDEhMB8GCSqGSIb3DQEJ +ARYSbWVAbXlob3N0Lm15ZG9tYWluggEAMA0GCSqGSIb3DQEBBAUAA4GBABP/5mXw +ttXKG6dqQl5kPisFs/c+0j64xytp5/cdB/zMpEWRWTBXtyL3T5T16xs52kJS0VfT +t+jezYbeu/dCdBL8Moz3RTYb1aY2/xymZ433kWjvgtrOzgGlaW3eKXcQpQEyK2v/ +J4q7+oDCElBRilZCm0mBcQsySKsZjGm8BMjh +-----END CERTIFICATE----- +Certificate[2]: +-----BEGIN CERTIFICATE----- +MIIDBjCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL +MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t +VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy +NTE0NDA1NVoXDTE0MTEyMzE0NDA1NVowZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgT +Ak5BMRAwDgYDVQQHEwdCSVNIS0VLMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxITAf +BgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAqPjWJnesPu6bR/iec4FMz3opVaPdBHxg+ORKNmrnVZPh0t8/ +ZT34KXkYoI9B82scurp8UlZVXG8JdUsz+yai8ti9+g7vcuyKUtcCIjn0HLgmdPu5 +gFX25lB0pXw+XIU031dOfPvtROdG5YZN5yCErgCy7TE7zntLnkEDuRmyU6cCAwEA +AaOBwzCBwDAdBgNVHQ4EFgQUiaZg47rqPq/8ZH9MvYzSSI3gzEYwgZAGA1UdIwSB +iDCBhYAUiaZg47rqPq/8ZH9MvYzSSI3gzEahaqRoMGYxCzAJBgNVBAYTAktHMQsw +CQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMMT3BlblZQTi1U +RVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW6CAQAwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBfJoiWYrYdjM0mKPEzUQk0nLYTovBP +I0es/2rfGrin1zbcFY+4dhVBd1E/StebnG+CP8r7QeEIwu7x8gYDdOLLsZn+2vBL +e4jNU1ClI6Q0L7jrzhhunQ5mAaZztVyYwFB15odYcdN2iO0tP7jtEsvrRqxICNy3 +8itzViPTf5W4sA== +-----END CERTIFICATE----- + + +******************************************* +******************************************* + + +Alias name: ca +Creation date: 23.11.2022 +Entry type: trustedCertEntry + +-----BEGIN CERTIFICATE----- +MIIDBjCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL +MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t +VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy +NTE0NDA1NVoXDTE0MTEyMzE0NDA1NVowZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgT +Ak5BMRAwDgYDVQQHEwdCSVNIS0VLMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxITAf +BgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEAqPjWJnesPu6bR/iec4FMz3opVaPdBHxg+ORKNmrnVZPh0t8/ +ZT34KXkYoI9B82scurp8UlZVXG8JdUsz+yai8ti9+g7vcuyKUtcCIjn0HLgmdPu5 +gFX25lB0pXw+XIU031dOfPvtROdG5YZN5yCErgCy7TE7zntLnkEDuRmyU6cCAwEA +AaOBwzCBwDAdBgNVHQ4EFgQUiaZg47rqPq/8ZH9MvYzSSI3gzEYwgZAGA1UdIwSB +iDCBhYAUiaZg47rqPq/8ZH9MvYzSSI3gzEahaqRoMGYxCzAJBgNVBAYTAktHMQsw +CQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMMT3BlblZQTi1U +RVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW6CAQAwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBfJoiWYrYdjM0mKPEzUQk0nLYTovBP +I0es/2rfGrin1zbcFY+4dhVBd1E/StebnG+CP8r7QeEIwu7x8gYDdOLLsZn+2vBL +e4jNU1ClI6Q0L7jrzhhunQ5mAaZztVyYwFB15odYcdN2iO0tP7jtEsvrRqxICNy3 +8itzViPTf5W4sA== +-----END CERTIFICATE----- + + +******************************************* +******************************************* + + + +Warning: +<1> uses the MD5withRSA signature algorithm which is considered a security risk and is disabled. +<1> uses a 1024-bit RSA key which is considered a security risk. This key size will be disabled in a future update. +<1> uses a 1024-bit RSA key which is considered a security risk. This key size will be disabled in a future update. + uses a 1024-bit RSA key which is considered a security risk. This key size will be disabled in a future update. + + + +# Keytool forbids to export private key; openssl can do it: +:; openssl pkcs12 -in testTrusted.p12 -nodes -nocerts -password pass:password +Bag Attributes + friendlyName: 1 + localKeyID: 2E 39 A5 71 AE FD F1 64 40 83 69 72 3A B6 3D 64 05 18 58 B7 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMOT0PRbWiTEJTUj +jiwWyPC7hR2ruxshzWcgWZUuNg5RARnnsQfGpBK+kKp4QsJSunVCo2fmUFkU/UGY +VVXKnHMEcDtX2JqVY/bAPjxptn5k1bnvMFkKFnaAZl5Mi0K0s+D9U0ivpIaw1QXd +Qbw+w3STcv1kpy8rmyerH6KOXL1bAgMBAAECgYBommqsByATggUUgsvLsPQQLXto +/yy3ukCN47OGIo0u4wxfupfovMmMbPga9O9f17d6eAXF0F0xCBTcPImHtTIvMLIt +UY4U4xwtdlEM3G5ToBxNvCHvtkkDiUVW8AorZfLFY9Agnsc3cTarrvEkdtzyYN8k +246tqACTJZEW8b/QoQJBAP/cd5sEPACChyHx7jr44mMBUppfu/5QC3+0N39XhTx+ +gXNfpRe/V77Qn0CMcH8RqkQVWTaSzPrpzJcAXgc9cB8CQQDDrvois8c+5ZSGu8MG +1zNCEjxTU9BBjWEkGLgwMwsH+5BlA7QT8B9QGWYiCJ4pJVJQ37AyQrUqt4at19Yb +vdtFAkEAgylFtxXInIpNM72N3nVPuGkpKzIAcTIfcuuzt3fqOUSwn7BcNXxFQvA3 +cyOLV9h6bER1Y2CF6+qGkrIBgbyhCQJBAKR14vRXdBWAjhvOolKVexcEjH7b6iOt +1v6nZ+XagGLtIqZDPo2jOi3vqs7fv02FeHFQDp2vQuPr6t0gkWovXqECQFEAAAj3 +TKXvRs1jL5gKNifOBJgeEqzZJXpLMkWDGTgImu+VyKcGE6+pie0okh4rmIoJqNx0 +EzBIslSYYUz8Q+A= +-----END PRIVATE KEY----- +```` + diff --git a/src/test/resources/jenkins/plugins/http_request/test.p12 b/src/test/resources/jenkins/plugins/http_request/test.p12 new file mode 100644 index 00000000..253d4081 Binary files /dev/null and b/src/test/resources/jenkins/plugins/http_request/test.p12 differ diff --git a/src/test/resources/jenkins/plugins/http_request/testTrusted.p12 b/src/test/resources/jenkins/plugins/http_request/testTrusted.p12 new file mode 100644 index 00000000..4c81ef13 Binary files /dev/null and b/src/test/resources/jenkins/plugins/http_request/testTrusted.p12 differ