Skip to content

Commit

Permalink
* remove commons-openpgp dependency and implement the necessary with …
Browse files Browse the repository at this point in the history
…BouncyCastle APIs directly
  • Loading branch information
rednoah committed Nov 7, 2015
1 parent 567ac37 commit 1a95163
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 65 deletions.
1 change: 0 additions & 1 deletion .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<classpathentry kind="lib" path="lib/ivy/jar/bcprov-jdk15on.jar"/>
<classpathentry kind="lib" path="lib/ivy/jar/commons-codec.jar"/>
<classpathentry kind="lib" path="lib/ivy/jar/commons-logging.jar"/>
<classpathentry kind="lib" path="lib/ivy/jar/commons-openpgp.jar"/>
<classpathentry kind="lib" path="lib/ivy/jar/httpclient.jar"/>
<classpathentry kind="lib" path="lib/ivy/jar/httpcore.jar"/>
<classpathentry kind="lib" path="lib/ivy/jar/httpmime.jar"/>
Expand Down
2 changes: 1 addition & 1 deletion examples/helloworld/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<package dir="app" includes="**/*.sh" filemode="755" />
<package dir="app" excludes="**/*.sh" />

<codesign keyid="D545C93D" pubring="gpg/pubring.gpg" secring="gpg/secring.gpg" password="" />
<codesign keyid="D545C93D" secring="gpg/secring.gpg" password="" />
</syno:spk>

<syno:package-source file="dist/spksrc.json">
Expand Down
11 changes: 6 additions & 5 deletions ivy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
<ivy-module version="2.0">
<info organisation="net.filebot" module="ant.spk" />
<dependencies>
<dependency org="org.apache.ant" name="ant" rev="1.9.4" />
<dependency org="org.apache.commons" name="commons-openpgp" rev="1.0-SNAPSHOT" />
<dependency org="org.apache.httpcomponents" name="httpcore" rev="4.4.1" />
<dependency org="org.apache.httpcomponents" name="httpmime" rev="4.4.1" />
<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.4.1" />
<dependency org="org.apache.ant" name="ant" rev="1.9.6" />
<dependency org="org.bouncycastle" name="bcprov-jdk15on" rev="1.53" />
<dependency org="org.bouncycastle" name="bcpg-jdk15on" rev="1.53" />
<dependency org="org.apache.httpcomponents" name="httpcore" rev="4.4.4" />
<dependency org="org.apache.httpcomponents" name="httpmime" rev="4.5.1" />
<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.5.1" />
<dependency org="org.glassfish" name="javax.json" rev="1.0.4" />
</dependencies>
</ivy-module>
9 changes: 0 additions & 9 deletions ivysettings.xml

This file was deleted.

88 changes: 40 additions & 48 deletions src/net/filebot/ant/spk/CodeSignTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

import org.apache.commons.openpgp.ant.OpenPgpSignerTask;
import net.filebot.ant.spk.openpgp.OpenPGPSignature;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
Expand All @@ -19,34 +22,30 @@
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Concat;
import org.apache.tools.ant.taskdefs.Tar.TarFileSet;
import org.apache.tools.ant.types.Resource;
import org.bouncycastle.openpgp.PGPException;

public class CodeSignTask extends Task {

// user properties
private static final int BUFFER_SIZE = 64 * 1024;

String keyId;
File pubring;
File secring;

String password = ""; // empty password by default
char[] password = new char[0]; // empty password by default
String timestamp = "http://timestamp.synology.com/timestamp.php"; // default Synology signature server

public void setKeyId(String keyId) {
this.keyId = keyId;
}

public void setPubring(File pubring) {
this.pubring = pubring;
}

public void setSecring(File secring) {
this.secring = secring;
}

public void setPassword(String password) {
this.password = password;
this.password = password.toCharArray();
}

public void setTimestamp(String timestamp) {
Expand All @@ -67,63 +66,56 @@ public void addConfiguredCat(TarFileSet files) {

@Override
public void execute() {
// temporary files
File allcat = new File("ALLCAT.dat");
File allsig = new File("ALLCAT.dat.asc"); // generated by <signer> default mapper

Concat concat = new Concat();
concat.setProject(getProject());
concat.setDestfile(allcat);
concat.setBinary(true);
concat.setOverwrite(true);

// cat files in case-sensitive alphabetical tar entry path order
TreeMap<String, Resource> sortedCats = new TreeMap<String, Resource>();
cats.forEach(fs -> {
fs.setProject(getProject());
fs.forEach(r -> {
sortedCats.put(getTarEntryName(r.getName(), fs), r);
});
});
byte[] asciiArmoredSignatureFile;

log(String.format("CAT: Prepare package data (%,d files, %,d bytes)", sortedCats.size(), sortedCats.values().stream().mapToLong(r -> r.getSize()).sum()));
sortedCats.values().forEach(concat::add);
concat.execute();

// GPG sign all cat data
// compute PGP signature
log("GPG: sign with key " + keyId);

OpenPgpSignerTask signer = new OpenPgpSignerTask();
signer.setProject(getProject());
signer.setAsciiarmor(true);
signer.setPubring(pubring);
signer.setSecring(secring);
signer.setPassword(password);
signer.setKeyId(keyId);
signer.setArtefact(allcat);
try {
OpenPGPSignature signature = OpenPGPSignature.createSignatureGenerator(keyId, secring, password);

// cat files in case-sensitive alphabetical tar entry path order
byte[] buffer = new byte[BUFFER_SIZE];
int length = 0;
for (Resource r : getTarOrderCatResources()) {
try (InputStream in = r.getInputStream()) {
while ((length = in.read(buffer, 0, buffer.length)) != -1) {
signature.update(buffer, 0, length);
}
}
}

// sign the binary data
signer.execute();
asciiArmoredSignatureFile = signature.generate(true);
} catch (IOException | SignatureException | PGPException e) {
throw new BuildException("Failed to compute PGP signature: " + e.getMessage());
}

// sign the signature
log("SYNO: Submit signature to " + timestamp);

try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpPost httpPost = new HttpPost(timestamp);

HttpEntity pastData = MultipartEntityBuilder.create().addBinaryBody("file", allsig).build();
HttpEntity pastData = MultipartEntityBuilder.create().addBinaryBody("file", asciiArmoredSignatureFile).build();
httpPost.setEntity(pastData);

HttpResponse response = httpClient.execute(httpPost);
Files.copy(response.getEntity().getContent(), token.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new BuildException("Failed to retrieve signature to " + timestamp);
} finally {
allcat.delete();
allsig.delete();
throw new BuildException("Failed to retrieve signature: " + e.getMessage());
}
}

protected Resource[] getTarOrderCatResources() {
TreeMap<String, Resource> sortedCats = new TreeMap<String, Resource>();
cats.forEach(fs -> {
fs.forEach(r -> {
sortedCats.put(getTarEntryName(r.getName(), fs), r);
});
});
return sortedCats.values().toArray(new Resource[0]);
}

protected String getTarEntryName(String vPath, TarFileSet tarFileSet) {
if (vPath.isEmpty() || vPath.startsWith("/"))
throw new IllegalArgumentException("Illegal tar entry: " + vPath);
Expand Down
5 changes: 4 additions & 1 deletion src/net/filebot/ant/spk/PackageTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ public void execute() throws BuildException {
private void prepareSignature(File tempDirectory) {
if (codesign != null) {
// select files that need to be signed
spkFiles.forEach(codesign::addConfiguredCat);
spkFiles.forEach((fs) -> {
fs.setProject(getProject());
codesign.addConfiguredCat(fs);
});

// create signature file
File signatureFile = new File(tempDirectory, SYNO_SIGNATURE);
Expand Down
48 changes: 48 additions & 0 deletions src/net/filebot/ant/spk/openpgp/OpenPGPSecretKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package net.filebot.ant.spk.openpgp;

import java.io.IOException;
import java.io.InputStream;

import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;

public class OpenPGPSecretKey {

private static final long MASK = 0xFFFFFFFFL;

private PGPSecretKey secretKey;
private char[] password;

public OpenPGPSecretKey(String keyId, InputStream secretKeyRing, char[] password) throws IOException {
PGPObjectFactory pgpObjectFactory = new BcPGPObjectFactory(PGPUtil.getDecoderStream(secretKeyRing));

for (Object it = pgpObjectFactory.nextObject(); it != null; it = pgpObjectFactory.nextObject()) {
PGPSecretKeyRing pgpSecretKeyRing = (PGPSecretKeyRing) it;
PGPSecretKey pgpSecretKey = pgpSecretKeyRing.getSecretKey();

if (keyId == null || keyId.isEmpty() || Long.valueOf(keyId, 16) == (pgpSecretKey.getKeyID() & MASK)) {
this.secretKey = pgpSecretKey;
break;
}
}

// sanity check
if (secretKey == null) {
throw new IllegalArgumentException("Secret key " + keyId + " not found");
}

this.password = password;
}

public PGPSecretKey getSecretKey() {
return secretKey;
}

public char[] getPassword() {
return password;
}

}
71 changes: 71 additions & 0 deletions src/net/filebot/ant/spk/openpgp/OpenPGPSignature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package net.filebot.ant.spk.openpgp;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Security;
import java.security.SignatureException;

import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;

public class OpenPGPSignature {

static {
Security.addProvider(new BouncyCastleProvider());
}

private PGPSignatureGenerator signature;

public OpenPGPSignature(OpenPGPSecretKey key) throws PGPException {
PGPDigestCalculatorProvider pgpDigestCalculator = new JcaPGPDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
PBESecretKeyDecryptor pbeSecretKeyDecryptor = new JcePBESecretKeyDecryptorBuilder(pgpDigestCalculator).setProvider(BouncyCastleProvider.PROVIDER_NAME).build(key.getPassword());
JcaPGPContentSignerBuilder pgpContentSigner = new JcaPGPContentSignerBuilder(key.getSecretKey().getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1).setProvider(BouncyCastleProvider.PROVIDER_NAME).setDigestProvider(BouncyCastleProvider.PROVIDER_NAME);

signature = new PGPSignatureGenerator(pgpContentSigner);

PGPPrivateKey privateKey = key.getSecretKey().extractPrivateKey(pbeSecretKeyDecryptor);
signature.init(PGPSignature.BINARY_DOCUMENT, privateKey);
}

public void update(byte[] buffer, int offset, int length) throws SignatureException {
signature.update(buffer, offset, length);
}

public void generate(OutputStream output, boolean asciiArmor) throws IOException, SignatureException, PGPException {
if (asciiArmor) {
output = new ArmoredOutputStream(output);
}
signature.generate().encode(new BCPGOutputStream(output));
}

public byte[] generate(boolean asciiArmor) throws IOException, SignatureException, PGPException {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
generate(out, asciiArmor);
return out.toByteArray();
}

public static OpenPGPSignature createSignatureGenerator(String keyId, File secring, char[] password) throws FileNotFoundException, IOException, PGPException {
try (InputStream secretKeyRing = new FileInputStream(secring)) {
OpenPGPSecretKey key = new OpenPGPSecretKey(keyId, secretKeyRing, password);
OpenPGPSignature signature = new OpenPGPSignature(key);
return signature;
}
}

}

0 comments on commit 1a95163

Please sign in to comment.