CommandTest.java
/*
* Copyright 2025 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.IOException;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import net.jsign.asn1.authenticode.AuthenticodeObjectIdentifiers;
import static net.jsign.DigestAlgorithm.*;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public class CommandTest {
@Parameters(name = "File #{index}: {0}")
public static String[] files() {
return new String[]{
"target/test-classes/wineyes.exe",
"target/test-classes/minimal.msi",
"target/test-classes/minimal.msix",
"target/test-classes/minimal.appxbundle",
"target/test-classes/minimal.navx",
"target/test-classes/hello-world.js",
"target/test-classes/hello-world.ps1",
"target/test-classes/hello-world.ps1xml",
"target/test-classes/hello-world.vbs",
"target/test-classes/hello-world.wsf",
"target/test-classes/cat/wineyes.cat",
"target/test-classes/mscab/sample1.cab",
"target/test-classes/mscab/sample2-disk1.cab",
"target/test-classes/mscab/sample2-disk2.cab",
"target/test-classes/mscab/sample3.cab",
"target/test-classes/mscab/sample4.cab",
"target/test-classes/nuget/minimal.1.0.0.nupkg",
};
}
private JsignCLI cli;
private final File sourceFile;
public CommandTest(String sourceFile) {
this.sourceFile = new File(sourceFile);
}
@Before
public void setUp() {
cli = new JsignCLI();
}
private File copy(File sourceFile, String suffix) throws IOException {
String name = sourceFile.getName();
int dot = name.lastIndexOf('.');
String targetName = name.substring(0, dot) + suffix + name.substring(dot);
File targetFile = new File(sourceFile.getParentFile(), targetName);
FileUtils.copyFile(sourceFile, targetFile);
return targetFile;
}
@Test
public void testTag() throws Exception {
File targetFile = copy(sourceFile, "-signed-tagged");
cli.execute("--keystore=target/test-classes/keystores/keystore.jks", "--keypass=password", "" + targetFile);
cli.execute("tag", "--value", "userid:1234-ABCD-5678-EFGH", "" + targetFile);
try (Signable signable = Signable.of(targetFile)) {
SignatureAssert.assertSigned(signable, SHA256);
CMSSignedData signature = signable.getSignatures().get(0);
SignerInformation signerInfo = signature.getSignerInfos().getSigners().iterator().next();
Attribute attribute = signerInfo.getUnsignedAttributes().get(AuthenticodeObjectIdentifiers.JSIGN_UNSIGNED_DATA_OBJID);
assertNotNull("Unsigned attribute not found", attribute);
assertEquals("Unsigned attribute value", "userid:1234-ABCD-5678-EFGH", attribute.getAttrValues().getObjectAt(0).toString());
}
}
@Test
public void testTimestamp() throws Exception {
File targetFile = copy(sourceFile, "-signed-then-timestamped");
cli.execute("--keystore=target/test-classes/keystores/keystore.jks", "--keypass=password", "" + targetFile);
cli.execute("timestamp", "--tsaurl=http://rfc3161.ai.moda", "--tsmode=RFC3161", "" + targetFile);
try (Signable signable = Signable.of(targetFile)) {
SignatureAssert.assertSigned(signable, SHA256);
SignatureAssert.assertTimestamped("Invalid timestamp", signable.getSignatures().get(0));
}
}
@Test
public void testRemove() throws Exception {
File targetFile = copy(sourceFile, "-signed-removed");
cli.execute("--keystore=target/test-classes/keystores/keystore.jks", "--keypass=password", "" + targetFile);
cli.execute("remove", "" + targetFile);
try (Signable signable = Signable.of(targetFile)) {
SignatureAssert.assertNotSigned(signable);
}
}
}