Skip to content

Commit

Permalink
Make key store types extensible conveniently
Browse files Browse the repository at this point in the history
  • Loading branch information
Vampire committed Nov 13, 2024
1 parent 3040bb1 commit b467d10
Show file tree
Hide file tree
Showing 38 changed files with 2,025 additions and 1,089 deletions.
45 changes: 23 additions & 22 deletions jsign-cli/src/test/java/net/jsign/JsignCLITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.concurrent.atomic.AtomicBoolean;

import io.netty.handler.codec.http.HttpRequest;
import net.jsign.ks.YubiKeyKeyStore;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.FileUtils;
Expand All @@ -54,7 +55,7 @@ public class JsignCLITest {
private JsignCLI cli;
private File sourceFile = new File("target/test-classes/wineyes.exe");
private File targetFile = new File("target/test-classes/wineyes-signed-with-cli.exe");

private String keystore = "keystore.jks";
private String alias = "test";
private String keypass = "password";
Expand All @@ -64,12 +65,12 @@ public class JsignCLITest {
@Before
public void setUp() throws Exception {
cli = new JsignCLI();

// remove the files signed previously
if (targetFile.exists()) {
assertTrue("Unable to remove the previously signed file", targetFile.delete());
}

assertEquals("Source file CRC32", SOURCE_FILE_CRC32, FileUtils.checksumCRC32(sourceFile));
Thread.sleep(100);
FileUtils.copyFile(sourceFile, targetFile);
Expand Down Expand Up @@ -218,7 +219,7 @@ public void testSigningMultipleFiles() throws Exception {
public void testSigningMultipleFilesWithListFile() throws Exception {
File listFile = new File("target/test-classes/files.txt");
Files.write(listFile.toPath(), Arrays.asList("# first file", '"' + targetFile.getPath() + '"', " ", "# second file", targetFile.getAbsolutePath()));

cli.execute("--name=WinEyes", "--url=http://www.steelblue.com/WinEyes", "--alg=SHA-1", "--keystore=target/test-classes/keystores/" + keystore, "--keypass=" + keypass, "@" + listFile);

assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
Expand Down Expand Up @@ -270,7 +271,7 @@ public void testSigningPowerShell() throws Exception {
File sourceFile = new File("target/test-classes/hello-world.ps1");
File targetFile = new File("target/test-classes/hello-world-signed-with-cli.ps1");
FileUtils.copyFile(sourceFile, targetFile);

cli.execute("--alg=SHA-1", "--replace", "--encoding=ISO-8859-1", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);

PowerShellScript script = new PowerShellScript(targetFile);
Expand All @@ -283,7 +284,7 @@ public void testSigningPowerShellWithDefaultEncoding() throws Exception {
File sourceFile = new File("target/test-classes/hello-world.ps1");
File targetFile = new File("target/test-classes/hello-world-signed-with-cli.ps1");
FileUtils.copyFile(sourceFile, targetFile);

cli.execute("--alg=SHA-1", "--replace", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);

PowerShellScript script = new PowerShellScript(targetFile);
Expand All @@ -296,7 +297,7 @@ public void testSigningMSI() throws Exception {
File sourceFile = new File("target/test-classes/minimal.msi");
File targetFile = new File("target/test-classes/minimal-signed-with-cli.msi");
FileUtils.copyFile(sourceFile, targetFile);

cli.execute("--alg=SHA-1", "--replace", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);

try (MSIFile file = new MSIFile(targetFile)) {
Expand All @@ -307,7 +308,7 @@ public void testSigningMSI() throws Exception {
@Test
public void testSigningPKCS12() throws Exception {
cli.execute("--name=WinEyes", "--url=http://www.steelblue.com/WinEyes", "--alg=SHA-256", "--keystore=target/test-classes/keystores/keystore.p12", "--alias=test", "--storepass=password", "" + targetFile);

assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));

try (PEFile peFile = new PEFile(targetFile)) {
Expand All @@ -329,7 +330,7 @@ public void testSigningJCEKS() throws Exception {
@Test
public void testSigningPVKSPC() throws Exception {
cli.execute("--url=http://www.steelblue.com/WinEyes", "--certfile=target/test-classes/keystores/jsign-test-certificate-full-chain.spc", "--keyfile=target/test-classes/keystores/privatekey-encrypted.pvk", "--storepass=password", "" + targetFile);

assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));

try (PEFile peFile = new PEFile(targetFile)) {
Expand All @@ -340,7 +341,7 @@ public void testSigningPVKSPC() throws Exception {
@Test
public void testSigningPEM() throws Exception {
cli.execute("--certfile=target/test-classes/keystores/jsign-test-certificate.pem", "--keyfile=target/test-classes/keystores/privatekey.pkcs8.pem", "--keypass=password", "" + targetFile);

assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));

try (PEFile peFile = new PEFile(targetFile)) {
Expand All @@ -351,7 +352,7 @@ public void testSigningPEM() throws Exception {
@Test
public void testSigningEncryptedPEM() throws Exception {
cli.execute("--certfile=target/test-classes/keystores/jsign-test-certificate.pem", "--keyfile=target/test-classes/keystores/privatekey-encrypted.pkcs1.pem", "--keypass=password", "" + targetFile);

assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));

try (PEFile peFile = new PEFile(targetFile)) {
Expand All @@ -361,7 +362,7 @@ public void testSigningEncryptedPEM() throws Exception {

@Test
public void testSigningWithYubikey() throws Exception {
Assume.assumeTrue("No Yubikey detected", YubiKey.isPresent());
Assume.assumeTrue("No Yubikey detected", YubiKeyKeyStore.isPresent());

cli.execute("--storetype=YUBIKEY", "--certfile=target/test-classes/keystores/jsign-test-certificate-full-chain.spc", "--storepass=123456", "--alias=X.509 Certificate for Digital Signature", "" + targetFile, "" + targetFile);
}
Expand All @@ -371,7 +372,7 @@ public void testTimestampingAuthenticode() throws Exception {
File targetFile2 = new File("target/test-classes/wineyes-timestamped-with-cli-authenticode.exe");
FileUtils.copyFile(sourceFile, targetFile2);
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "--tsaurl=http://timestamp.sectigo.com", "--tsmode=authenticode", "" + targetFile2);

assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));

try (PEFile peFile = new PEFile(targetFile2)) {
Expand Down Expand Up @@ -404,18 +405,18 @@ public HttpFilters filterRequest(HttpRequest originalRequest) {
}
})
.start();

try {
File targetFile2 = new File("target/test-classes/wineyes-timestamped-with-cli-rfc3161-proxy-unauthenticated.exe");
FileUtils.copyFile(sourceFile, targetFile2);
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass,
"--tsaurl=http://timestamp.sectigo.com", "--tsmode=rfc3161", "--tsretries=1", "--tsretrywait=1",
"--proxyUrl=localhost:" + proxy.getListenAddress().getPort(),
"" + targetFile2);

assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
assertTrue("The proxy wasn't used", proxyUsed.get());

try (PEFile peFile = new PEFile(targetFile2)) {
SignatureAssert.assertSigned(peFile, SHA256);
}
Expand Down Expand Up @@ -457,10 +458,10 @@ public String getRealm() {
"--proxyUser=jsign",
"--proxyPass=jsign",
"" + targetFile2);

assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
assertTrue("The proxy wasn't used", proxyUsed.get());

try (PEFile peFile = new PEFile(targetFile2)) {
SignatureAssert.assertSigned(peFile, SHA256);
}
Expand All @@ -474,11 +475,11 @@ public void testReplaceSignature() throws Exception {
File targetFile2 = new File("target/test-classes/wineyes-re-signed.exe");
FileUtils.copyFile(sourceFile, targetFile2);
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile2);

assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));

cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "--alg=SHA-512", "--replace", "" + targetFile2);

try (PEFile peFile = new PEFile(targetFile2)) {
SignatureAssert.assertSigned(peFile, SHA512);
}
Expand Down Expand Up @@ -514,7 +515,7 @@ public Integer getStatus() {
}

public void checkPermission(Permission perm) { }

public void checkPermission(Permission perm, Object context) { }

public void checkExit(int status) {
Expand Down
14 changes: 8 additions & 6 deletions jsign-core/src/main/java/net/jsign/SignerHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import java.util.Set;
import java.util.logging.Logger;

import net.jsign.ks.AzureTrustedSigningKeyStore;
import net.jsign.ks.JsignKeyStore;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
Expand Down Expand Up @@ -262,7 +264,7 @@ public SignerHelper param(String key, String value) {
if (value == null) {
return this;
}

switch (key) {
case PARAM_COMMAND: return command(value);
case PARAM_KEYSTORE: return keystore(value);
Expand Down Expand Up @@ -329,7 +331,7 @@ private AuthenticodeSigner build() throws SignerException {
} catch (KeyStoreException e) {
throw new SignerException("Failed to load the keystore " + (ksparams.keystore() != null ? ksparams.keystore() : ""), e);
}
KeyStoreType storetype = ksparams.storetype();
JsignKeyStore storetype = ksparams.storetype();
Provider provider = ksparams.provider();

Set<String> aliases = null;
Expand Down Expand Up @@ -420,12 +422,12 @@ private AuthenticodeSigner build() throws SignerException {
}

// enable timestamping with Azure Trusted Signing
if (tsaurl == null && storetype == KeyStoreType.TRUSTEDSIGNING) {
if ((tsaurl == null) && (storetype instanceof AzureTrustedSigningKeyStore)) {
tsaurl = "http://timestamp.acs.microsoft.com/";
tsmode = TimestampingMode.RFC3161.name();
tsretries = 3;
}

// configure the signer
return new AuthenticodeSigner(chain, privateKey)
.withProgramName(name)
Expand All @@ -451,7 +453,7 @@ public void sign(File file) throws SignerException {
if (!file.exists()) {
throw new SignerException("The file " + file + " couldn't be found");
}

try (Signable signable = Signable.of(file, encoding)) {
File detachedSignature = getDetachedSignature(file);
if (detached && detachedSignature.exists()) {
Expand Down Expand Up @@ -655,7 +657,7 @@ private void timestamp(File file) throws SignerException {
SignerId signerId = signerInformation.getSID();
X509CertificateHolder certificate = (X509CertificateHolder) signature.getCertificates().getMatches(signerId).iterator().next();

String digestAlgorithmName = new DefaultAlgorithmNameFinder().getAlgorithmName(signerInformation.getDigestAlgorithmID());
String digestAlgorithmName = new DefaultAlgorithmNameFinder().getAlgorithmName(signerInformation.getDigestAlgorithmID());
String keyAlgorithmName = new DefaultAlgorithmNameFinder().getAlgorithmName(new ASN1ObjectIdentifier(signerInformation.getEncryptionAlgOID()));
String name = digestAlgorithmName + "/" + keyAlgorithmName + " signature from '" + certificate.getSubject() + "'";

Expand Down
35 changes: 18 additions & 17 deletions jsign-core/src/test/java/net/jsign/PESignerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.HashSet;

import net.jsign.ks.YubiKeyKeyStore;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.SystemUtils;
Expand Down Expand Up @@ -66,7 +67,7 @@ private KeyStore getKeyStore() throws Exception {
public void testSign() throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-signed.exe");

FileUtils.copyFile(sourceFile, targetFile);

PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD)
Expand Down Expand Up @@ -96,7 +97,7 @@ public void testSignWithUnknownKeyStoreEntry() {
public void testSigningWithKeyAndChain() throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-signed-key-chain.exe");

FileUtils.copyFile(sourceFile, targetFile);

Certificate[] chain;
Expand Down Expand Up @@ -132,7 +133,7 @@ public void testSigningWithKeyAndChain() throws Exception {

@Test
public void testSigningWithYubikey() throws Exception {
Assume.assumeTrue("No Yubikey detected", YubiKey.isPresent());
Assume.assumeTrue("No Yubikey detected", YubiKeyKeyStore.isPresent());

File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-signed-yubikey.exe");
Expand Down Expand Up @@ -166,7 +167,7 @@ public void testNullChain() throws Exception {
public void testSigningWithMismatchingKeyAndCertificate() throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-signed-mismatching-key-certificate.exe");

FileUtils.copyFile(sourceFile, targetFile);

Certificate[] chain;
Expand Down Expand Up @@ -202,7 +203,7 @@ public void testTimestampRFC3161() throws Exception {
public void testTimestamp(TimestampingMode mode, DigestAlgorithm alg) throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-timestamped-" + mode.name().toLowerCase() + ".exe");

FileUtils.copyFile(sourceFile, targetFile);

PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
Expand Down Expand Up @@ -234,7 +235,7 @@ public void testWithTimestamper() throws Exception {
signer.withDigestAlgorithm(SHA1);
signer.withTimestamping(true);
signer.withTimestamper(new AuthenticodeTimestamper() {

@Override
protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest) throws IOException, TimestampingException {
called.add(true);
Expand All @@ -257,7 +258,7 @@ protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest)
public void testSignTwice() throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-signed-twice.exe");

FileUtils.copyFile(sourceFile, targetFile);

try (PEFile peFile = new PEFile(targetFile)) {
Expand Down Expand Up @@ -286,7 +287,7 @@ public void testSignTwice() throws Exception {
public void testSignThreeTimes() throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-signed-three-times.exe");

FileUtils.copyFile(sourceFile, targetFile);

try (PEFile peFile = new PEFile(targetFile)) {
Expand Down Expand Up @@ -323,7 +324,7 @@ public void testSignThreeTimes() throws Exception {
public void testReplaceSignature() throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-re-signed.exe");

FileUtils.copyFile(sourceFile, targetFile);

try (PEFile peFile = new PEFile(targetFile)) {
Expand Down Expand Up @@ -359,16 +360,16 @@ public void testInvalidRFC3161TimestampingAuthority() throws Exception {
public void testInvalidTimestampingAuthority(TimestampingMode mode) throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-timestamped-unavailable-" + mode.name().toLowerCase() + ".exe");

FileUtils.copyFile(sourceFile, targetFile);

PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
signer.withDigestAlgorithm(SHA1);
signer.withTimestamping(true);
signer.withTimestampingMode(mode);
signer.withTimestampingAuthority("http://www.google.com/" + mode.name().toLowerCase());
signer.withTimestampingRetries(1);

try (PEFile peFile = new PEFile(targetFile)) {
Exception e = assertThrows(TimestampingException.class, () -> signer.sign(peFile));
assertTrue("Missing suppressed IOException", e.getSuppressed() != null && e.getSuppressed().length > 0 && e.getSuppressed()[0].getClass().equals(IOException.class));
Expand All @@ -390,16 +391,16 @@ public void testBrokenRFC3161TimestampingAuthority() throws Exception {
public void testBrokenTimestampingAuthority(TimestampingMode mode) throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-timestamped-broken-" + mode.name().toLowerCase() + ".exe");

FileUtils.copyFile(sourceFile, targetFile);

PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
signer.withDigestAlgorithm(SHA1);
signer.withTimestamping(true);
signer.withTimestampingMode(mode);
signer.withTimestampingAuthority("http://github.com");
signer.withTimestampingRetries(1);

try (PEFile peFile = new PEFile(targetFile)) {
assertThrows(TimestampingException.class, () -> signer.sign(peFile));
}
Expand Down Expand Up @@ -434,7 +435,7 @@ public void testRFC3161TimestampingFailover() throws Exception {
public void testTimestampingFailover(TimestampingMode mode, String validURL) throws Exception {
File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-timestamped-failover-" + mode.name().toLowerCase() + ".exe");

FileUtils.copyFile(sourceFile, targetFile);

PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
Expand Down Expand Up @@ -490,7 +491,7 @@ public void testWithSignatureAlgorithmSHA1withRSA() throws Exception {
@Test
public void testWithSignatureAlgorithmSHA256withRSAandMGF1() throws Exception {
Security.addProvider(new BouncyCastleProvider());

File sourceFile = new File("target/test-classes/wineyes.exe");
File targetFile = new File("target/test-classes/wineyes-signed.exe");

Expand Down
2 changes: 1 addition & 1 deletion jsign-crypto/src/main/java/net/jsign/CertificateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
/**
* @since 5.0
*/
class CertificateUtils {
public class CertificateUtils {

private CertificateUtils() {
}
Expand Down
Loading

0 comments on commit b467d10

Please sign in to comment.