Skip to content

Commit b467d10

Browse files
committed
Make key store types extensible conveniently
1 parent 3040bb1 commit b467d10

38 files changed

+2025
-1089
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);
@@ -218,7 +219,7 @@ public void testSigningMultipleFiles() throws Exception {
218219
public void testSigningMultipleFilesWithListFile() throws Exception {
219220
File listFile = new File("target/test-classes/files.txt");
220221
Files.write(listFile.toPath(), Arrays.asList("# first file", '"' + targetFile.getPath() + '"', " ", "# second file", targetFile.getAbsolutePath()));
221-
222+
222223
cli.execute("--name=WinEyes", "--url=http://www.steelblue.com/WinEyes", "--alg=SHA-1", "--keystore=target/test-classes/keystores/" + keystore, "--keypass=" + keypass, "@" + listFile);
223224

224225
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
@@ -270,7 +271,7 @@ public void testSigningPowerShell() throws Exception {
270271
File sourceFile = new File("target/test-classes/hello-world.ps1");
271272
File targetFile = new File("target/test-classes/hello-world-signed-with-cli.ps1");
272273
FileUtils.copyFile(sourceFile, targetFile);
273-
274+
274275
cli.execute("--alg=SHA-1", "--replace", "--encoding=ISO-8859-1", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);
275276

276277
PowerShellScript script = new PowerShellScript(targetFile);
@@ -283,7 +284,7 @@ public void testSigningPowerShellWithDefaultEncoding() throws Exception {
283284
File sourceFile = new File("target/test-classes/hello-world.ps1");
284285
File targetFile = new File("target/test-classes/hello-world-signed-with-cli.ps1");
285286
FileUtils.copyFile(sourceFile, targetFile);
286-
287+
287288
cli.execute("--alg=SHA-1", "--replace", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);
288289

289290
PowerShellScript script = new PowerShellScript(targetFile);
@@ -296,7 +297,7 @@ public void testSigningMSI() throws Exception {
296297
File sourceFile = new File("target/test-classes/minimal.msi");
297298
File targetFile = new File("target/test-classes/minimal-signed-with-cli.msi");
298299
FileUtils.copyFile(sourceFile, targetFile);
299-
300+
300301
cli.execute("--alg=SHA-1", "--replace", "--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile);
301302

302303
try (MSIFile file = new MSIFile(targetFile)) {
@@ -307,7 +308,7 @@ public void testSigningMSI() throws Exception {
307308
@Test
308309
public void testSigningPKCS12() throws Exception {
309310
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);
310-
311+
311312
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
312313

313314
try (PEFile peFile = new PEFile(targetFile)) {
@@ -329,7 +330,7 @@ public void testSigningJCEKS() throws Exception {
329330
@Test
330331
public void testSigningPVKSPC() throws Exception {
331332
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);
332-
333+
333334
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
334335

335336
try (PEFile peFile = new PEFile(targetFile)) {
@@ -340,7 +341,7 @@ public void testSigningPVKSPC() throws Exception {
340341
@Test
341342
public void testSigningPEM() throws Exception {
342343
cli.execute("--certfile=target/test-classes/keystores/jsign-test-certificate.pem", "--keyfile=target/test-classes/keystores/privatekey.pkcs8.pem", "--keypass=password", "" + targetFile);
343-
344+
344345
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
345346

346347
try (PEFile peFile = new PEFile(targetFile)) {
@@ -351,7 +352,7 @@ public void testSigningPEM() throws Exception {
351352
@Test
352353
public void testSigningEncryptedPEM() throws Exception {
353354
cli.execute("--certfile=target/test-classes/keystores/jsign-test-certificate.pem", "--keyfile=target/test-classes/keystores/privatekey-encrypted.pkcs1.pem", "--keypass=password", "" + targetFile);
354-
355+
355356
assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile));
356357

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

362363
@Test
363364
public void testSigningWithYubikey() throws Exception {
364-
Assume.assumeTrue("No Yubikey detected", YubiKey.isPresent());
365+
Assume.assumeTrue("No Yubikey detected", YubiKeyKeyStore.isPresent());
365366

366367
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);
367368
}
@@ -371,7 +372,7 @@ public void testTimestampingAuthenticode() throws Exception {
371372
File targetFile2 = new File("target/test-classes/wineyes-timestamped-with-cli-authenticode.exe");
372373
FileUtils.copyFile(sourceFile, targetFile2);
373374
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "--tsaurl=http://timestamp.sectigo.com", "--tsmode=authenticode", "" + targetFile2);
374-
375+
375376
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
376377

377378
try (PEFile peFile = new PEFile(targetFile2)) {
@@ -404,18 +405,18 @@ public HttpFilters filterRequest(HttpRequest originalRequest) {
404405
}
405406
})
406407
.start();
407-
408+
408409
try {
409410
File targetFile2 = new File("target/test-classes/wineyes-timestamped-with-cli-rfc3161-proxy-unauthenticated.exe");
410411
FileUtils.copyFile(sourceFile, targetFile2);
411412
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass,
412413
"--tsaurl=http://timestamp.sectigo.com", "--tsmode=rfc3161", "--tsretries=1", "--tsretrywait=1",
413414
"--proxyUrl=localhost:" + proxy.getListenAddress().getPort(),
414415
"" + targetFile2);
415-
416+
416417
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
417418
assertTrue("The proxy wasn't used", proxyUsed.get());
418-
419+
419420
try (PEFile peFile = new PEFile(targetFile2)) {
420421
SignatureAssert.assertSigned(peFile, SHA256);
421422
}
@@ -457,10 +458,10 @@ public String getRealm() {
457458
"--proxyUser=jsign",
458459
"--proxyPass=jsign",
459460
"" + targetFile2);
460-
461+
461462
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
462463
assertTrue("The proxy wasn't used", proxyUsed.get());
463-
464+
464465
try (PEFile peFile = new PEFile(targetFile2)) {
465466
SignatureAssert.assertSigned(peFile, SHA256);
466467
}
@@ -474,11 +475,11 @@ public void testReplaceSignature() throws Exception {
474475
File targetFile2 = new File("target/test-classes/wineyes-re-signed.exe");
475476
FileUtils.copyFile(sourceFile, targetFile2);
476477
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "" + targetFile2);
477-
478+
478479
assertTrue("The file " + targetFile2 + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile2));
479-
480+
480481
cli.execute("--keystore=target/test-classes/keystores/" + keystore, "--alias=" + alias, "--keypass=" + keypass, "--alg=SHA-512", "--replace", "" + targetFile2);
481-
482+
482483
try (PEFile peFile = new PEFile(targetFile2)) {
483484
SignatureAssert.assertSigned(peFile, SHA512);
484485
}
@@ -514,7 +515,7 @@ public Integer getStatus() {
514515
}
515516

516517
public void checkPermission(Permission perm) { }
517-
518+
518519
public void checkPermission(Permission perm, Object context) { }
519520

520521
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)
@@ -96,7 +97,7 @@ public void testSignWithUnknownKeyStoreEntry() {
9697
public void testSigningWithKeyAndChain() throws Exception {
9798
File sourceFile = new File("target/test-classes/wineyes.exe");
9899
File targetFile = new File("target/test-classes/wineyes-signed-key-chain.exe");
99-
100+
100101
FileUtils.copyFile(sourceFile, targetFile);
101102

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

133134
@Test
134135
public void testSigningWithYubikey() throws Exception {
135-
Assume.assumeTrue("No Yubikey detected", YubiKey.isPresent());
136+
Assume.assumeTrue("No Yubikey detected", YubiKeyKeyStore.isPresent());
136137

137138
File sourceFile = new File("target/test-classes/wineyes.exe");
138139
File targetFile = new File("target/test-classes/wineyes-signed-yubikey.exe");
@@ -166,7 +167,7 @@ public void testNullChain() throws Exception {
166167
public void testSigningWithMismatchingKeyAndCertificate() throws Exception {
167168
File sourceFile = new File("target/test-classes/wineyes.exe");
168169
File targetFile = new File("target/test-classes/wineyes-signed-mismatching-key-certificate.exe");
169-
170+
170171
FileUtils.copyFile(sourceFile, targetFile);
171172

172173
Certificate[] chain;
@@ -202,7 +203,7 @@ public void testTimestampRFC3161() throws Exception {
202203
public void testTimestamp(TimestampingMode mode, DigestAlgorithm alg) throws Exception {
203204
File sourceFile = new File("target/test-classes/wineyes.exe");
204205
File targetFile = new File("target/test-classes/wineyes-timestamped-" + mode.name().toLowerCase() + ".exe");
205-
206+
206207
FileUtils.copyFile(sourceFile, targetFile);
207208

208209
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
@@ -234,7 +235,7 @@ public void testWithTimestamper() throws Exception {
234235
signer.withDigestAlgorithm(SHA1);
235236
signer.withTimestamping(true);
236237
signer.withTimestamper(new AuthenticodeTimestamper() {
237-
238+
238239
@Override
239240
protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest) throws IOException, TimestampingException {
240241
called.add(true);
@@ -257,7 +258,7 @@ protected CMSSignedData timestamp(DigestAlgorithm algo, byte[] encryptedDigest)
257258
public void testSignTwice() throws Exception {
258259
File sourceFile = new File("target/test-classes/wineyes.exe");
259260
File targetFile = new File("target/test-classes/wineyes-signed-twice.exe");
260-
261+
261262
FileUtils.copyFile(sourceFile, targetFile);
262263

263264
try (PEFile peFile = new PEFile(targetFile)) {
@@ -286,7 +287,7 @@ public void testSignTwice() throws Exception {
286287
public void testSignThreeTimes() throws Exception {
287288
File sourceFile = new File("target/test-classes/wineyes.exe");
288289
File targetFile = new File("target/test-classes/wineyes-signed-three-times.exe");
289-
290+
290291
FileUtils.copyFile(sourceFile, targetFile);
291292

292293
try (PEFile peFile = new PEFile(targetFile)) {
@@ -323,7 +324,7 @@ public void testSignThreeTimes() throws Exception {
323324
public void testReplaceSignature() throws Exception {
324325
File sourceFile = new File("target/test-classes/wineyes.exe");
325326
File targetFile = new File("target/test-classes/wineyes-re-signed.exe");
326-
327+
327328
FileUtils.copyFile(sourceFile, targetFile);
328329

329330
try (PEFile peFile = new PEFile(targetFile)) {
@@ -359,16 +360,16 @@ public void testInvalidRFC3161TimestampingAuthority() throws Exception {
359360
public void testInvalidTimestampingAuthority(TimestampingMode mode) throws Exception {
360361
File sourceFile = new File("target/test-classes/wineyes.exe");
361362
File targetFile = new File("target/test-classes/wineyes-timestamped-unavailable-" + mode.name().toLowerCase() + ".exe");
362-
363+
363364
FileUtils.copyFile(sourceFile, targetFile);
364-
365+
365366
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
366367
signer.withDigestAlgorithm(SHA1);
367368
signer.withTimestamping(true);
368369
signer.withTimestampingMode(mode);
369370
signer.withTimestampingAuthority("http://www.google.com/" + mode.name().toLowerCase());
370371
signer.withTimestampingRetries(1);
371-
372+
372373
try (PEFile peFile = new PEFile(targetFile)) {
373374
Exception e = assertThrows(TimestampingException.class, () -> signer.sign(peFile));
374375
assertTrue("Missing suppressed IOException", e.getSuppressed() != null && e.getSuppressed().length > 0 && e.getSuppressed()[0].getClass().equals(IOException.class));
@@ -390,16 +391,16 @@ public void testBrokenRFC3161TimestampingAuthority() throws Exception {
390391
public void testBrokenTimestampingAuthority(TimestampingMode mode) throws Exception {
391392
File sourceFile = new File("target/test-classes/wineyes.exe");
392393
File targetFile = new File("target/test-classes/wineyes-timestamped-broken-" + mode.name().toLowerCase() + ".exe");
393-
394+
394395
FileUtils.copyFile(sourceFile, targetFile);
395-
396+
396397
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
397398
signer.withDigestAlgorithm(SHA1);
398399
signer.withTimestamping(true);
399400
signer.withTimestampingMode(mode);
400401
signer.withTimestampingAuthority("http://github.com");
401402
signer.withTimestampingRetries(1);
402-
403+
403404
try (PEFile peFile = new PEFile(targetFile)) {
404405
assertThrows(TimestampingException.class, () -> signer.sign(peFile));
405406
}
@@ -434,7 +435,7 @@ public void testRFC3161TimestampingFailover() throws Exception {
434435
public void testTimestampingFailover(TimestampingMode mode, String validURL) throws Exception {
435436
File sourceFile = new File("target/test-classes/wineyes.exe");
436437
File targetFile = new File("target/test-classes/wineyes-timestamped-failover-" + mode.name().toLowerCase() + ".exe");
437-
438+
438439
FileUtils.copyFile(sourceFile, targetFile);
439440

440441
PESigner signer = new PESigner(getKeyStore(), ALIAS, PRIVATE_KEY_PASSWORD);
@@ -490,7 +491,7 @@ public void testWithSignatureAlgorithmSHA1withRSA() throws Exception {
490491
@Test
491492
public void testWithSignatureAlgorithmSHA256withRSAandMGF1() throws Exception {
492493
Security.addProvider(new BouncyCastleProvider());
493-
494+
494495
File sourceFile = new File("target/test-classes/wineyes.exe");
495496
File targetFile = new File("target/test-classes/wineyes-signed.exe");
496497

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)