Created
October 19, 2011 09:10
-
-
Save cbeams/1297803 to your computer and use it in GitHub Desktop.
artifactory-pgp-plugin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright 2011 the original author or authors. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| import java.io.ByteArrayOutputStream | |
| import java.io.FileInputStream | |
| import java.io.InputStream | |
| import org.artifactory.repo.RepoPath | |
| import org.artifactory.repo.RepoPathFactory | |
| import org.artifactory.resource.ResourceStreamHandle | |
| /** | |
| * Artifactory user plugin that signs all incoming artifacts using the key | |
| * and passphrase specified in $ARTIFACTORY_HOME/etc/pgp/signing.properties | |
| * and deploys the resulting signature in typical fashion as an .asc file | |
| * parallel to the original artifact. | |
| * | |
| * Purpose-built to meet the needs of promoting artifacts to Maven Central, i.e. | |
| * not intended in its current form for more general use. | |
| * | |
| * See http://wiki.jfrog.org/confluence/display/RTF/User+Plugins for background. | |
| * | |
| * @author Chris Beams | |
| */ | |
| storage { | |
| Properties props = new Properties() | |
| props.load(new FileReader( | |
| new File(ctx.artifactoryHome.etcDir, "pgp/signing.properties"))) | |
| File secretKeyFile = | |
| new File(ctx.artifactoryHome.etcDir, props.secretKeyFile) | |
| char[] passphrase = props.passphrase.toCharArray() | |
| afterCreate { item -> | |
| String itemKey = item.repoKey | |
| String itemPath = item.repoPath.path | |
| if (item.isFolder()) { | |
| log.debug("Ignoring creation of new folder: ${itemKey}:${itemPath}") | |
| return | |
| } | |
| else if(itemPath.endsWith(".asc")) { | |
| log.debug("Ignoring deployment of signature file: ${itemKey}:${itemPath}") | |
| return | |
| } | |
| log.debug("Creating signature for: ${itemKey}:${itemPath}") | |
| ResourceStreamHandle content = repositories.getContent(item.repoPath) | |
| ByteArrayOutputStream signatureOut = new ByteArrayOutputStream() | |
| ArmoredOutputStream armoredSignatureOut = new ArmoredOutputStream(signatureOut) | |
| SimplePGPUtil.signFile( | |
| secretKeyFile, passphrase, content.getInputStream(), armoredSignatureOut); | |
| byte[] signatureBytes = signatureOut.toByteArray() | |
| ByteArrayInputStream signatureIn = new ByteArrayInputStream(signatureBytes) | |
| RepoPath signature = RepoPathFactory.create(itemKey, "${itemPath}.asc") | |
| repositories.deploy(signature, signatureIn) | |
| log.info("Created and deployed signature: ${signature.repoKey}:${signature.path}") | |
| } | |
| } | |
| import java.security.Security | |
| @Grapes(@Grab(group='org.bouncycastle', module='bcpg-jdk16', version='1.46')) | |
| import org.bouncycastle.bcpg.ArmoredOutputStream | |
| import org.bouncycastle.bcpg.BCPGOutputStream | |
| import org.bouncycastle.jce.provider.BouncyCastleProvider | |
| import org.bouncycastle.openpgp.PGPPrivateKey | |
| import org.bouncycastle.openpgp.PGPSecretKey | |
| import org.bouncycastle.openpgp.PGPSecretKeyRing | |
| import org.bouncycastle.openpgp.PGPSecretKeyRingCollection | |
| import org.bouncycastle.openpgp.PGPSignature | |
| import org.bouncycastle.openpgp.PGPSignatureGenerator | |
| import org.bouncycastle.openpgp.PGPUtil | |
| /** | |
| * Simple PGP utility that takes a target file and produces a .asc-style signature for it. | |
| * Specifically designed not to depend on the Artifactory public API, such that it may | |
| * easily be tested and modified in a standalone way via copy-and-paste if need be. | |
| * | |
| * @author Chris Beams | |
| */ | |
| class SimplePGPUtil { | |
| /** | |
| * Sign the contents of the given input stream using the given secret key and | |
| * passphrase, writing the resulting signature to the given armored output stream, | |
| * i.e. suitable for .asc file suffix. | |
| * | |
| * @param secretKeyFile PGP secret key file that contains one and only one secret key | |
| * @param passphrase for the secret key | |
| * @param targetFileIn input stream of the file to be signed, closed by this method | |
| * @param signatureFileOut output stream of the signature file, closed by this method | |
| * @throws Exception if anything fails in the signing process | |
| */ | |
| static void signFile(File secretKeyFile, char[] passphrase, InputStream targetFileIn, | |
| ArmoredOutputStream signatureFileOut) throws Exception { | |
| if (secretKeyFile.exists() == false) { | |
| throw new IllegalArgumentException( | |
| "secretKeyFile does not exist: ${secretKeyFile.getPath()}"); | |
| } | |
| FileInputStream secretKeyIn = new FileInputStream(secretKeyFile); | |
| BCPGOutputStream bcpgOut = null; | |
| try { | |
| if (Security.getProvider("BC") == null) { | |
| Security.addProvider(new BouncyCastleProvider()); | |
| } | |
| // as advertised, the .key file must have one and only one secret key within due | |
| // to the opinionated nature of the following call chain. | |
| PGPSecretKey pgpSec = ((PGPSecretKey)((PGPSecretKeyRing)new PGPSecretKeyRingCollection( | |
| PGPUtil.getDecoderStream(secretKeyIn)).getKeyRings().next()).getSecretKeys().next()); | |
| PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(passphrase, "BC"); | |
| PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( | |
| pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC"); | |
| signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); | |
| bcpgOut = new BCPGOutputStream(signatureFileOut); | |
| int ch = 0; | |
| while ((ch = targetFileIn.read()) >= 0) { | |
| signatureGenerator.update((byte)ch); | |
| } | |
| PGPSignature signature = signatureGenerator.generate(); | |
| signature.encode(bcpgOut); | |
| } finally { | |
| secretKeyIn.close(); | |
| targetFileIn.close(); | |
| if (bcpgOut != null) { | |
| bcpgOut.close(); | |
| } | |
| signatureFileOut.close(); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment