Skip to content

Commit

Permalink
[JENKINS-55582] Turn modules into plugins (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick authored Feb 23, 2021
1 parent 36a658c commit 256a0e5
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 14 deletions.
7 changes: 7 additions & 0 deletions .mvn/extensions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
<extension>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>git-changelist-maven-extension</artifactId>
<version>1.2</version>
</extension>
</extensions>
2 changes: 2 additions & 0 deletions .mvn/maven.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-Pconsume-incrementals
-Pmight-produce-incrementals
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// Builds a module using https://github.com/jenkins-infra/pipeline-library
buildPlugin(configurations: buildPlugin.recommendedConfigurations())
buildPlugin()
27 changes: 15 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>3.42</version>
<version>4.16</version>
<relativePath />
</parent>

<groupId>org.jenkins-ci.modules</groupId>
<artifactId>instance-identity</artifactId>
<version>2.3-SNAPSHOT</version>
<packaging>jenkins-module</packaging>
<version>${revision}${changelist}</version>
<packaging>hpi</packaging>

<name>Instance Identity</name>
<description>Maintains an RSA key pair that can serve as a foundation of authentication when communicating with Jenkins</description>
<url>https://github.com/jenkinsci/${project.artifactId}-module</url> <!-- TODO -module → -plugin -->
<licenses>
<license>
<name>MIT License</name>
Expand All @@ -24,15 +24,18 @@
</licenses>

<scm>
<connection>scm:git:git://github.com/jenkinsci/${project.artifactId}-module.git</connection>
<developerConnection>scm:git:ssh://[email protected]/jenkinsci/${project.artifactId}-module.git</developerConnection>
<url>https://github.com/jenkinsci/${project.artifactId}-module</url>
<tag>HEAD</tag>
<connection>scm:git:git://github.com/${gitHubRepo}.git</connection>
<developerConnection>scm:git:ssh://[email protected]/${gitHubRepo}.git</developerConnection>
<url>https://github.com/${gitHubRepo}</url>
<tag>${scmTag}</tag>
</scm>

<properties>
<jenkins.version>2.16</jenkins.version>
<revision>3.0</revision>
<changelist>-SNAPSHOT</changelist>
<jenkins.version>2.280</jenkins.version>
<java.level>8</java.level>
<gitHubRepo>jenkinsci/${project.artifactId}-module</gitHubRepo> <!-- TODO -module → -plugin -->
</properties>

<repositories>
Expand All @@ -50,9 +53,9 @@

<dependencies>
<dependency>
<groupId>io.github.stephenc.crypto</groupId>
<artifactId>self-signed-cert-generator</artifactId>
<version>1.0.0</version>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>bouncycastle-api</artifactId>
<version>2.18</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import hudson.FilePath;
import hudson.Util;
import hudson.model.PageDecorator;
import io.github.stephenc.crypto.sscg.SelfSignedCertificate;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package org.jenkinsci.main.modules.instance_identity;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDSAContentSignerBuilder;
import org.bouncycastle.operator.bc.BcECContentSignerBuilder;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;

final class SelfSignedCertificate {

private final KeyPair keyPair;
private Date firstDate = new Date();
private Date lastDate = new Date(firstDate.getTime() + TimeUnit.DAYS.toMillis(365));
private X500NameBuilder subject = new X500NameBuilder(BCStyle.INSTANCE);
private String hashAlg = "SHA1";

private SelfSignedCertificate(KeyPair keyPair) {
this.keyPair = keyPair;
}

public static SelfSignedCertificate forKeyPair(KeyPair keyPair) {
return new SelfSignedCertificate(keyPair);
}

public SelfSignedCertificate validFrom(Date date) {
this.firstDate = date == null ? new Date() : (Date) (date.clone());
return this;
}

public SelfSignedCertificate validUntil(Date date) {
this.lastDate =
date == null ? new Date(firstDate.getTime() + TimeUnit.DAYS.toMillis(365)) : (Date) (date.clone());
return this;
}

public SelfSignedCertificate cn(String name) {
subject.addRDN(BCStyle.CN, name);
return this;
}

public SelfSignedCertificate c(String name) {
subject.addRDN(BCStyle.C, name);
return this;
}

public SelfSignedCertificate o(String name) {
subject.addRDN(BCStyle.O, name);
return this;
}

public SelfSignedCertificate ou(String name) {
subject.addRDN(BCStyle.OU, name);
return this;
}

public SelfSignedCertificate oid(String oid, String name) {
subject.addRDN(new ASN1ObjectIdentifier(oid), name);
return this;
}

public SelfSignedCertificate sha1() {
hashAlg = "SHA1";
return this;
}

public SelfSignedCertificate sha224() {
hashAlg = "SHA224";
return this;
}

public SelfSignedCertificate sha256() {
hashAlg = "SHA256";
return this;
}

public SelfSignedCertificate sha384() {
hashAlg = "SHA384";
return this;
}

public SelfSignedCertificate sha512() {
hashAlg = "SHA512";
return this;
}

public X509Certificate generate() throws IOException {
try {
SubjectPublicKeyInfo subjectPublicKeyInfo =
SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());

X500Name subject = this.subject.build();

X509v3CertificateBuilder certGen = new X509v3CertificateBuilder(
subject,
BigInteger.ONE,
firstDate,
lastDate,
subject,
subjectPublicKeyInfo
);

JcaX509ExtensionUtils instance = new JcaX509ExtensionUtils();

certGen.addExtension(Extension.subjectKeyIdentifier,
false,
instance.createSubjectKeyIdentifier(subjectPublicKeyInfo)
);

ContentSigner signer;
if (keyPair.getPrivate() instanceof RSAPrivateKey) {
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(hashAlg + "withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(
new RSAKeyParameters(true,privateKey.getModulus(), privateKey.getPrivateExponent()));
} else if (keyPair.getPrivate() instanceof DSAPrivateKey) {
DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
AlgorithmIdentifier sigAlgId =
new DefaultSignatureAlgorithmIdentifierFinder().find(hashAlg + "withDSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
signer = new BcDSAContentSignerBuilder(sigAlgId, digAlgId).build(DSAUtil.generatePrivateKeyParameter(privateKey));
} else if (keyPair.getPrivate() instanceof ECPrivateKey) {
ECPrivateKey privateKey = (ECPrivateKey)keyPair.getPrivate();
AlgorithmIdentifier sigAlgId =
new DefaultSignatureAlgorithmIdentifierFinder().find(hashAlg + "withECDSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
signer = new BcECContentSignerBuilder(sigAlgId, digAlgId).build(ECUtil.generatePrivateKeyParameter(privateKey));
} else {
throw new IOException("Unsupported key type");
}

return (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(certGen.build(signer).getEncoded()));
} catch (OperatorCreationException e) {
throw new IOException("Failed to generate a certificate", e);
} catch (CertificateException e) {
throw new IOException("Failed to generate a certificate", e);
} catch (NoSuchAlgorithmException e) {
throw new IOException("Failed to generate a certificate", e);
} catch (InvalidKeyException e) {
throw new IOException("Failed to generate a certificate", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

// TODO consider switching to BouncyCastle APIs

/**
* Helper class to decode an encode PEM formated strings without any external dependencies
* The supported formats are:
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/index.jelly
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?jelly escape-by-default='true'?>
<div>
Maintains an RSA key pair that can serve as a foundation of authentication when communicating with Jenkins
</div>

0 comments on commit 256a0e5

Please sign in to comment.