Skip to content

Commit ecbf15c

Browse files
committed
Make key store types extensible conveniently
1 parent 5cd5cfc commit ecbf15c

38 files changed

+2022
-1086
lines changed

jsign-cli/src/test/java/net/jsign/JsignCLITest.java

+23-22
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.concurrent.atomic.AtomicBoolean;
2929

3030
import io.netty.handler.codec.http.HttpRequest;
31+
import net.jsign.ks.YubiKeyKeyStore;
3132
import org.apache.commons.cli.ParseException;
3233
import org.apache.commons.io.ByteOrderMark;
3334
import org.apache.commons.io.FileUtils;
@@ -54,7 +55,7 @@ public class JsignCLITest {
5455
private JsignCLI cli;
5556
private File sourceFile = new File("target/test-classes/wineyes.exe");
5657
private File targetFile = new File("target/test-classes/wineyes-signed-with-cli.exe");
57-
58+
5859
private String keystore = "keystore.jks";
5960
private String alias = "test";
6061
private String keypass = "password";
@@ -64,12 +65,12 @@ public class JsignCLITest {
6465
@Before
6566
public void setUp() throws Exception {
6667
cli = new JsignCLI();
67-
68+
6869
// remove the files signed previously
6970
if (targetFile.exists()) {
7071
assertTrue("Unable to remove the previously signed file", targetFile.delete());
7172
}
72-
73+
7374
assertEquals("Source file CRC32", SOURCE_FILE_CRC32, FileUtils.checksumCRC32(sourceFile));
7475
Thread.sleep(100);
7576
FileUtils.copyFile(sourceFile, targetFile);
@@ -226,7 +227,7 @@ public void testSigningMultipleFiles() throws Exception {
226227
public void testSigningMultipleFilesWithListFile() throws Exception {
227228
File listFile = new File("target/test-classes/files.txt");
228229
Files.write(listFile.toPath(), Arrays.asList("# first file", '"' + targetFile.getPath() + '"', " ", "# second file", targetFile.getAbsolutePath()));
229-
230+
230231
cli.execute("--name=WinEyes", "--url=http://www.steelblue.com/WinEyes", "--alg=SHA-1", "--keystore=target/test-classes/keystores/" + keystore, "--keypass=" + keypass, "@" + listFile);
231232

232233
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
@@ -278,7 +279,7 @@ public void testSigningPowerShell() throws Exception {
278279
File sourceFile = new File("target/test-classes/hello-world.ps1");
279280
File targetFile = new File("target/test-classes/hello-world-signed-with-cli.ps1");
280281
FileUtils.copyFile(sourceFile, targetFile);
281-
282+
282283
cli.execute("--alg=SHA-1", "--replace", "--encoding=ISO-8859-1", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);
283284

284285
PowerShellScript script = new PowerShellScript(targetFile);
@@ -291,7 +292,7 @@ public void testSigningPowerShellWithDefaultEncoding() throws Exception {
291292
File sourceFile = new File("target/test-classes/hello-world.ps1");
292293
File targetFile = new File("target/test-classes/hello-world-signed-with-cli.ps1");
293294
FileUtils.copyFile(sourceFile, targetFile);
294-
295+
295296
cli.execute("--alg=SHA-1", "--replace", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);
296297

297298
PowerShellScript script = new PowerShellScript(targetFile);
@@ -304,7 +305,7 @@ public void testSigningMSI() throws Exception {
304305
File sourceFile = new File("target/test-classes/minimal.msi");
305306
File targetFile = new File("target/test-classes/minimal-signed-with-cli.msi");
306307
FileUtils.copyFile(sourceFile, targetFile);
307-
308+
308309
cli.execute("--alg=SHA-1", "--replace", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);
309310

310311
try (MSIFile file = new MSIFile(targetFile)) {
@@ -315,7 +316,7 @@ public void testSigningMSI() throws Exception {
315316
@Test
316317
public void testSigningPKCS12() throws Exception {
317318
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);
318-
319+
319320
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
320321

321322
try (PEFile peFile = new PEFile(targetFile)) {
@@ -337,7 +338,7 @@ public void testSigningJCEKS() throws Exception {
337338
@Test
338339
public void testSigningPVKSPC() throws Exception {
339340
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);
340-
341+
341342
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
342343

343344
try (PEFile peFile = new PEFile(targetFile)) {
@@ -348,7 +349,7 @@ public void testSigningPVKSPC() throws Exception {
348349
@Test
349350
public void testSigningPEM() throws Exception {
350351
cli.execute("--certfile=target/test-classes/keystores/jsign-test-certificate.pem", "--keyfile=target/test-classes/keystores/privatekey.pkcs8.pem", "--keypass=password", "" + targetFile);
351-
352+
352353
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
353354

354355
try (PEFile peFile = new PEFile(targetFile)) {
@@ -359,7 +360,7 @@ public void testSigningPEM() throws Exception {
359360
@Test
360361
public void testSigningEncryptedPEM() throws Exception {
361362
cli.execute("--certfile=target/test-classes/keystores/jsign-test-certificate.pem", "--keyfile=target/test-classes/keystores/privatekey-encrypted.pkcs1.pem", "--keypass=password", "" + targetFile);
362-
363+
363364
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
364365

365366
try (PEFile peFile = new PEFile(targetFile)) {
@@ -369,7 +370,7 @@ public void testSigningEncryptedPEM() throws Exception {
369370

370371
@Test
371372
public void testSigningWithYubikey() throws Exception {
372-
Assume.assumeTrue("No Yubikey detected", YubiKey.isPresent());
373+
Assume.assumeTrue("No Yubikey detected", YubiKeyKeyStore.isPresent());
373374

374375
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);
375376
}
@@ -379,7 +380,7 @@ public void testTimestampingAuthenticode() throws Exception {
379380
File targetFile2 = new File("target/test-classes/wineyes-timestamped-with-cli-authenticode.exe");
380381
FileUtils.copyFile(sourceFile, targetFile2);
381382
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "--tsaurl=http://timestamp.sectigo.com", "--tsmode=authenticode", "" + targetFile2);
382-
383+
383384
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
384385

385386
try (PEFile peFile = new PEFile(targetFile2)) {
@@ -412,18 +413,18 @@ public HttpFilters filterRequest(HttpRequest originalRequest) {
412413
}
413414
})
414415
.start();
415-
416+
416417
try {
417418
File targetFile2 = new File("target/test-classes/wineyes-timestamped-with-cli-rfc3161-proxy-unauthenticated.exe");
418419
FileUtils.copyFile(sourceFile, targetFile2);
419420
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass,
420421
"--tsaurl=http://timestamp.sectigo.com", "--tsmode=rfc3161", "--tsretries=1", "--tsretrywait=1",
421422
"--proxyUrl=localhost:" + proxy.getListenAddress().getPort(),
422423
"" + targetFile2);
423-
424+
424425
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
425426
assertTrue("The proxy wasn't used", proxyUsed.get());
426-
427+
427428
try (PEFile peFile = new PEFile(targetFile2)) {
428429
SignatureAssert.assertSigned(peFile, SHA256);
429430
}
@@ -465,10 +466,10 @@ public String getRealm() {
465466
"--proxyUser=jsign",
466467
"--proxyPass=jsign",
467468
"" + targetFile2);
468-
469+
469470
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
470471
assertTrue("The proxy wasn't used", proxyUsed.get());
471-
472+
472473
try (PEFile peFile = new PEFile(targetFile2)) {
473474
SignatureAssert.assertSigned(peFile, SHA256);
474475
}
@@ -482,11 +483,11 @@ public void testReplaceSignature() throws Exception {
482483
File targetFile2 = new File("target/test-classes/wineyes-re-signed.exe");
483484
FileUtils.copyFile(sourceFile, targetFile2);
484485
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile2);
485-
486+
486487
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
487-
488+
488489
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "--alg=SHA-512", "--replace", "" + targetFile2);
489-
490+
490491
try (PEFile peFile = new PEFile(targetFile2)) {
491492
SignatureAssert.assertSigned(peFile, SHA512);
492493
}
@@ -525,7 +526,7 @@ public Integer getStatus() {
525526
}
526527

527528
public void checkPermission(Permission perm) { }
528-
529+
529530
public void checkPermission(Permission perm, Object context) { }
530531

531532
public void checkExit(int status) {

jsign-core/src/main/java/net/jsign/SignerHelper.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import java.util.Set;
4646
import java.util.logging.Logger;
4747

48+
import net.jsign.ks.AzureTrustedSigningKeyStore;
49+
import net.jsign.ks.JsignKeyStore;
4850
import org.bouncycastle.asn1.ASN1Encodable;
4951
import org.bouncycastle.asn1.ASN1InputStream;
5052
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -262,7 +264,7 @@ public SignerHelper param(String key, String value) {
262264
if (value == null) {
263265
return this;
264266
}
265-
267+
266268
switch (key) {
267269
case PARAM_COMMAND: return command(value);
268270
case PARAM_KEYSTORE: return keystore(value);
@@ -329,7 +331,7 @@ private AuthenticodeSigner build() throws SignerException {
329331
} catch (KeyStoreException e) {
330332
throw new SignerException("Failed to load the keystore " + (ksparams.keystore() != null ? ksparams.keystore() : ""), e);
331333
}
332-
KeyStoreType storetype = ksparams.storetype();
334+
JsignKeyStore storetype = ksparams.storetype();
333335
Provider provider = ksparams.provider();
334336

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

422424
// enable timestamping with Azure Trusted Signing
423-
if (tsaurl == null && storetype == KeyStoreType.TRUSTEDSIGNING) {
425+
if ((tsaurl == null) && (storetype instanceof AzureTrustedSigningKeyStore)) {
424426
tsaurl = "http://timestamp.acs.microsoft.com/";
425427
tsmode = TimestampingMode.RFC3161.name();
426428
tsretries = 3;
427429
}
428-
430+
429431
// configure the signer
430432
return new AuthenticodeSigner(chain, privateKey)
431433
.withProgramName(name)
@@ -451,7 +453,7 @@ public void sign(File file) throws SignerException {
451453
if (!file.exists()) {
452454
throw new SignerException("The file " + file + " couldn't be found");
453455
}
454-
456+
455457
try (Signable signable = Signable.of(file, encoding)) {
456458
File detachedSignature = getDetachedSignature(file);
457459
if (detached && detachedSignature.exists()) {
@@ -655,7 +657,7 @@ private void timestamp(File file) throws SignerException {
655657
SignerId signerId = signerInformation.getSID();
656658
X509CertificateHolder certificate = (X509CertificateHolder) signature.getCertificates().getMatches(signerId).iterator().next();
657659

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

jsign-core/src/test/java/net/jsign/PESignerTest.java

+18-17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Collection;
2828
import java.util.HashSet;
2929

30+
import net.jsign.ks.YubiKeyKeyStore;
3031
import org.apache.commons.io.FileUtils;
3132
import org.apache.commons.lang3.JavaVersion;
3233
import org.apache.commons.lang3.SystemUtils;
@@ -66,7 +67,7 @@ private KeyStore getKeyStore() throws Exception {
6667
public void testSign() throws Exception {
6768
File sourceFile = new File("target/test-classes/wineyes.exe");
6869
File targetFile = new File("target/test-classes/wineyes-signed.exe");
69-
70+
7071
FileUtils.copyFile(sourceFile, targetFile);
7172

7273
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD)
@@ -100,7 +101,7 @@ public void testSignWithUnknownKeyStoreEntry() throws Exception {
100101
public void testSigningWithKeyAndChain() throws Exception {
101102
File sourceFile = new File("target/test-classes/wineyes.exe");
102103
File targetFile = new File("target/test-classes/wineyes-signed-key-chain.exe");
103-
104+
104105
FileUtils.copyFile(sourceFile, targetFile);
105106

106107
Certificate[] chain;
@@ -136,7 +137,7 @@ public void testSigningWithKeyAndChain() throws Exception {
136137

137138
@Test
138139
public void testSigningWithYubikey() throws Exception {
139-
Assume.assumeTrue("No Yubikey detected", YubiKey.isPresent());
140+
Assume.assumeTrue("No Yubikey detected", YubiKeyKeyStore.isPresent());
140141

141142
File sourceFile = new File("target/test-classes/wineyes.exe");
142143
File targetFile = new File("target/test-classes/wineyes-signed-yubikey.exe");
@@ -170,7 +171,7 @@ public void testNullChain() throws Exception {
170171
public void testSigningWithMismatchingKeyAndCertificate() throws Exception {
171172
File sourceFile = new File("target/test-classes/wineyes.exe");
172173
File targetFile = new File("target/test-classes/wineyes-signed-mismatching-key-certificate.exe");
173-
174+
174175
FileUtils.copyFile(sourceFile, targetFile);
175176

176177
Certificate[] chain;
@@ -208,7 +209,7 @@ public void testTimestampRFC3161() throws Exception {
208209
public void testTimestamp(TimestampingMode mode, DigestAlgorithm alg) throws Exception {
209210
File sourceFile = new File("target/test-classes/wineyes.exe");
210211
File targetFile = new File("target/test-classes/wineyes-timestamped-" + mode.name().toLowerCase() + ".exe");
211-
212+
212213
FileUtils.copyFile(sourceFile, targetFile);
213214

214215
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
@@ -240,7 +241,7 @@ public void testWithTimestamper() throws Exception {
240241
signer.withDigestAlgorithm(SHA1);
241242
signer.withTimestamping(true);
242243
signer.withTimestamper(new AuthenticodeTimestamper() {
243-
244+
244245
@Override
245246
protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest) throws IOException, TimestampingException {
246247
called.add(true);
@@ -263,7 +264,7 @@ protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest)
263264
public void testSignTwice() throws Exception {
264265
File sourceFile = new File("target/test-classes/wineyes.exe");
265266
File targetFile = new File("target/test-classes/wineyes-signed-twice.exe");
266-
267+
267268
FileUtils.copyFile(sourceFile, targetFile);
268269

269270
try (PEFile peFile = new PEFile(targetFile)) {
@@ -292,7 +293,7 @@ public void testSignTwice() throws Exception {
292293
public void testSignThreeTimes() throws Exception {
293294
File sourceFile = new File("target/test-classes/wineyes.exe");
294295
File targetFile = new File("target/test-classes/wineyes-signed-three-times.exe");
295-
296+
296297
FileUtils.copyFile(sourceFile, targetFile);
297298

298299
try (PEFile peFile = new PEFile(targetFile)) {
@@ -329,7 +330,7 @@ public void testSignThreeTimes() throws Exception {
329330
public void testReplaceSignature() throws Exception {
330331
File sourceFile = new File("target/test-classes/wineyes.exe");
331332
File targetFile = new File("target/test-classes/wineyes-re-signed.exe");
332-
333+
333334
FileUtils.copyFile(sourceFile, targetFile);
334335

335336
try (PEFile peFile = new PEFile(targetFile)) {
@@ -365,16 +366,16 @@ public void testInvalidRFC3161TimestampingAuthority() throws Exception {
365366
public void testInvalidTimestampingAuthority(TimestampingMode mode) throws Exception {
366367
File sourceFile = new File("target/test-classes/wineyes.exe");
367368
File targetFile = new File("target/test-classes/wineyes-timestamped-unavailable-" + mode.name().toLowerCase() + ".exe");
368-
369+
369370
FileUtils.copyFile(sourceFile, targetFile);
370-
371+
371372
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
372373
signer.withDigestAlgorithm(SHA1);
373374
signer.withTimestamping(true);
374375
signer.withTimestampingMode(mode);
375376
signer.withTimestampingAuthority("http://www.google.com/" + mode.name().toLowerCase());
376377
signer.withTimestampingRetries(1);
377-
378+
378379
try (PEFile peFile = new PEFile(targetFile)) {
379380
signer.sign(peFile);
380381
fail("TimestampingException not thrown");
@@ -399,16 +400,16 @@ public void testBrokenRFC3161TimestampingAuthority() throws Exception {
399400
public void testBrokenTimestampingAuthority(TimestampingMode mode) throws Exception {
400401
File sourceFile = new File("target/test-classes/wineyes.exe");
401402
File targetFile = new File("target/test-classes/wineyes-timestamped-broken-" + mode.name().toLowerCase() + ".exe");
402-
403+
403404
FileUtils.copyFile(sourceFile, targetFile);
404-
405+
405406
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
406407
signer.withDigestAlgorithm(SHA1);
407408
signer.withTimestamping(true);
408409
signer.withTimestampingMode(mode);
409410
signer.withTimestampingAuthority("http://github.com");
410411
signer.withTimestampingRetries(1);
411-
412+
412413
try (PEFile peFile = new PEFile(targetFile)) {
413414
signer.sign(peFile);
414415
fail("TimestampingException not thrown");
@@ -446,7 +447,7 @@ public void testRFC3161TimestampingFailover() throws Exception {
446447
public void testTimestampingFailover(TimestampingMode mode, String validURL) throws Exception {
447448
File sourceFile = new File("target/test-classes/wineyes.exe");
448449
File targetFile = new File("target/test-classes/wineyes-timestamped-failover-" + mode.name().toLowerCase() + ".exe");
449-
450+
450451
FileUtils.copyFile(sourceFile, targetFile);
451452

452453
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
@@ -502,7 +503,7 @@ public void testWithSignatureAlgorithmSHA1withRSA() throws Exception {
502503
@Test
503504
public void testWithSignatureAlgorithmSHA256withRSAandMGF1() throws Exception {
504505
Security.addProvider(new BouncyCastleProvider());
505-
506+
506507
File sourceFile = new File("target/test-classes/wineyes.exe");
507508
File targetFile = new File("target/test-classes/wineyes-signed.exe");
508509

jsign-crypto/src/main/java/net/jsign/CertificateUtils.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
/**
4646
* @since 5.0
4747
*/
48-
class CertificateUtils {
48+
public class CertificateUtils {
4949

5050
private CertificateUtils() {
5151
}

0 commit comments

Comments
 (0)