Skip to content

Commit

Permalink
Added an Ant task for signing files
Browse files Browse the repository at this point in the history
  • Loading branch information
ebourg committed Oct 2, 2012
1 parent 1fc9fc2 commit 5aa11b6
Show file tree
Hide file tree
Showing 6 changed files with 454 additions and 1 deletion.
2 changes: 1 addition & 1 deletion TODO.txt
Expand Up @@ -2,5 +2,5 @@ TODO

- Verify signed files
- CLI
- Ant Task
- GUI
- RFC 3161 timestamping for Windows 7 and later
7 changes: 7 additions & 0 deletions pom.xml
Expand Up @@ -40,6 +40,13 @@
<version>1.47</version>
</dependency>

<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.8.4</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
185 changes: 185 additions & 0 deletions src/main/java/net/jsign/PESignerTask.java
@@ -0,0 +1,185 @@
/**
* Copyright 2012 Emmanuel Bourg
*
* 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.
*/

package net.jsign;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.cert.Certificate;

import net.jsign.pe.PEFile;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.util.FileUtils;

/**
* Ant task for signing executable files.
*
* @author Emmanuel Bourg
* @since 1.0
*/
public class PESignerTask extends Task {

/** The file to be signed. */
private File file;

/** The program name embedded in the signature. */
private String name;

/** The program URL embedded in the signature. */
private String url;

/** The URL of the timestamping authority. */
private String tsaurl;

/** The alias of the certificate in the keystore. */
private String alias;

/** The keystore file. */
private File keystore;

/** The password for the keystore. */
private String storepass;

/** The type of the keystore. */
private String storetype = "JKS";

/** The password for the key in the store, if different from the keystore password. */
private String keypass;

public void setFile(File file) {
this.file = file;
}

public void setName(String name) {
this.name = name;
}

public void setUrl(String url) {
this.url = url;
}

public void setTsaurl(String tsaurl) {
this.tsaurl = tsaurl;
}

public void setAlias(String alias) {
this.alias = alias;
}

public void setKeystore(File keystore) {
this.keystore = keystore;
}

public void setStorepass(String storepass) {
this.storepass = storepass;
}

public void setStoretype(String storetype) {
this.storetype = storetype;
}

public void setKeypass(String keypass) {
this.keypass = keypass;
}

@Override
public void execute() throws BuildException {
// some exciting parameter validation...
if (keystore == null) {
throw new BuildException("keystore attribute must be set");
}

KeyStore ks;
try {
ks = KeyStore.getInstance(storetype);
} catch (KeyStoreException e) {
throw new BuildException("keystore type '" + storetype + "' is not supported", e);
}

if (!keystore.exists()) {
throw new BuildException("The keystore " + keystore + " couldn't be found");
}

try {
ks.load(new FileInputStream(keystore), storepass != null ? storepass.toCharArray() : null);
} catch (Exception e) {
throw new BuildException("Unable to load the keystore " + keystore, e);
}

if (alias == null) {
throw new BuildException("alias attribute must be set");
}

Certificate[] chain;
try {
chain = ks.getCertificateChain(alias);
} catch (KeyStoreException e) {
throw new BuildException(e);
}
if (chain == null) {
throw new BuildException("No certificate found under the alias '" + alias + "' in the keystore " + keystore);
}

String password = keypass != null ? keypass : storepass;
PrivateKey privateKey;
try {
privateKey = (PrivateKey) ks.getKey(alias, password != null ? password.toCharArray() : null);
} catch (Exception e) {
throw new BuildException("Failed to retrieve the private key from the keystore", e);
}

if (file == null) {
throw new BuildException("file attribute must be set");
}
if (!file.exists()) {
throw new BuildException("The file " + file + " couldn't be found");
}

PEFile peFile;
try {
peFile = new PEFile(file);
} catch (IOException e) {
throw new BuildException("Couldn't open the executable file " + file, e);
}

// and now the actual work!
PESigner signer = new PESigner(chain, privateKey)
.withProgramName(name)
.withProgramURL(url)
.withTimestamping(tsaurl != null)
.withTimestampingAutority(tsaurl);


try {
log("Adding Authenticode signature to " + FileUtils.getRelativePath(getProject().getBaseDir(), file));
signer.sign(peFile);
} catch (Exception e) {
throw new BuildException("Couldn't sign " + file, e);
} finally {
try {
peFile.close();
} catch (IOException e) {
log("Couldn't close " + file, e, Project.MSG_WARN);
}
}
}
}
4 changes: 4 additions & 0 deletions src/main/resources/net/jsign/antlib.xml
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<antlib>
<taskdef name="signexe" classname="net.jsign.PESignerTask" />
</antlib>

0 comments on commit 5aa11b6

Please sign in to comment.