Skip to content

Commit

Permalink
Ed25519 and Ed448 keys support
Browse files Browse the repository at this point in the history
  • Loading branch information
ebourg committed May 10, 2024
1 parent 6003001 commit c723528
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ See https://ebourg.github.io/jsign for more information.
* The `remove` command has been added to remove the signature from a signed file
* The JCA provider now works with [apksigner](https://developer.android.com/tools/apksigner) for signing Android applications
* RSA 4096 keys are supported with the `PIV` storetype (for Yubikeys with firmware version 5.7 or higher)
* Certificates using an Ed25519 or Ed448 key are now supported (experimental)
* The APPX/MSIX bundles are now signed with the correct Authenticode UUID
* The error message displayed when the password of a PKCS#12 keystore is missing has been fixed
* The log4j configuration warning displayed when signing a MSI file has been fixed (contributed by Pascal Davoust)
Expand Down
20 changes: 18 additions & 2 deletions jsign-core/src/main/java/net/jsign/AuthenticodeSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.bouncycastle.asn1.ASN1Encodable;
Expand Down Expand Up @@ -300,9 +302,18 @@ private String getSignatureAlgorithm() {
return signatureAlgorithm;
} else if ("EC".equals(privateKey.getAlgorithm())) {
return digestAlgorithm + "withECDSA";
} else {
return digestAlgorithm + "with" + privateKey.getAlgorithm();
} else if ("EdDSA".equals(privateKey.getAlgorithm())) {
X509Certificate certificate = (X509Certificate) chain[0];
PublicKey publicKey = certificate.getPublicKey();
if (publicKey.toString().contains("Ed25519")) {
return "Ed25519";
} else if (publicKey.toString().contains("Ed448")) {
return "Ed448";
}
// return ((EdECKey) publicKey).getParams().getName(); // todo with Java 15+
}

return digestAlgorithm + "with" + privateKey.getAlgorithm();
}

/**
Expand Down Expand Up @@ -574,6 +585,11 @@ protected CMSSignedData addNestedSignature(CMSSignedData primary, CMSSignedData
* @return an AlgorithmIdentifier for the digestAlgorithm and including parameters
*/
protected AlgorithmIdentifier createContentDigestAlgorithmIdentifier(AlgorithmIdentifier signatureAlgorithm) {
if ("1.3.101.112".equals(signatureAlgorithm.getAlgorithm().getId()) // Ed25519
|| "1.3.101.113".equals(signatureAlgorithm.getAlgorithm().getId())) { // Ed448
return new AlgorithmIdentifier(digestAlgorithm.oid, DERNull.INSTANCE);
}

AlgorithmIdentifier ai = new DefaultDigestAlgorithmIdentifierFinder().find(signatureAlgorithm);
if (ai.getParameters() == null) {
// Always include parameters to align with what signtool does
Expand Down
42 changes: 42 additions & 0 deletions jsign-core/src/test/java/net/jsign/PESignerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import java.util.HashSet;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.SystemUtils;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
Expand Down Expand Up @@ -547,6 +549,46 @@ public void testSignWithECKey() throws Exception {
}
}

@Test
public void testSignWithEd25519Key() throws Exception {
Assume.assumeTrue("EdDSA requires Java 15 or higher", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_15));

KeyStore keystore = new KeyStoreBuilder().keystore("target/test-classes/keystores/keystore-ed25519.p12").storepass("password").build();

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

FileUtils.copyFile(sourceFile, targetFile);

AuthenticodeSigner signer = new AuthenticodeSigner(keystore, ALIAS, PRIVATE_KEY_PASSWORD).withTimestamping(false);

try (PEFile peFile = new PEFile(targetFile)) {
signer.sign(peFile);

SignatureAssert.assertSigned(peFile, SHA256);
}
}

@Test
public void testSignWithEd448Key() throws Exception {
Assume.assumeTrue("EdDSA requires Java 15 or higher", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_15));

KeyStore keystore = new KeyStoreBuilder().keystore("target/test-classes/keystores/keystore-ed448.p12").storepass("password").build();

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

FileUtils.copyFile(sourceFile, targetFile);

AuthenticodeSigner signer = new AuthenticodeSigner(keystore, ALIAS, PRIVATE_KEY_PASSWORD).withTimestamping(false);

try (PEFile peFile = new PEFile(targetFile)) {
signer.sign(peFile);

SignatureAssert.assertSigned(peFile, SHA256);
}
}

@Test
public void testContentDigestAlgorithmIdentifier() throws Exception {
// ensure the algorithm identifier has a DER NULL optional parameters field to match the signtool output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ openssl req -new -key privatekey-ec-p384.pkcs1.pem -subj "/CN=Jsign Code Signing
openssl x509 -req -in jsign-test-certificate-ec.csr -CA jsign-code-signing-ca.pem -CAkey jsign-code-signing-ca.key -CAcreateserial \
-out jsign-test-certificate-ec.pem $CERT_OPTS -extfile extensions.cnf -extensions final

openssl req -new -key privatekey-ed25519.pem -subj "/CN=Jsign Code Signing Test Certificate $YEAR (Ed25519)" -out jsign-test-certificate-ed25519.csr
openssl x509 -req -in jsign-test-certificate-ed25519.csr -CA jsign-code-signing-ca.pem -CAkey jsign-code-signing-ca.key -CAcreateserial \
-out jsign-test-certificate-ed25519.pem $CERT_OPTS -extfile extensions.cnf -extensions final

openssl req -new -key privatekey-ed448.pem -subj "/CN=Jsign Code Signing Test Certificate $YEAR (Ed448)" -out jsign-test-certificate-ed448.csr
openssl x509 -req -in jsign-test-certificate-ed448.csr -CA jsign-code-signing-ca.pem -CAkey jsign-code-signing-ca.key -CAcreateserial \
-out jsign-test-certificate-ed448.pem $CERT_OPTS -extfile extensions.cnf -extensions final

# Generate the certificate chains
cat jsign-root-ca.pem jsign-code-signing-ca.pem jsign-test-certificate.pem > jsign-test-certificate-full-chain-reversed.pem
cat jsign-test-certificate.pem jsign-code-signing-ca.pem jsign-root-ca.pem > jsign-test-certificate-full-chain.pem
Expand All @@ -70,6 +78,12 @@ openssl pkcs12 $OPENSSL_OPTS -in jsign-test-certificate.pem -out keys
OPENSSL_OPTS="-export -inkey privatekey-ec-p384.pkcs1.pem -name test -passout pass:password"
openssl pkcs12 $OPENSSL_OPTS -in jsign-test-certificate-ec.pem -out keystore-ec.p12

OPENSSL_OPTS="-export -inkey privatekey-ed25519.pem -name test -passout pass:password"
openssl pkcs12 $OPENSSL_OPTS -in jsign-test-certificate-ed25519.pem -out keystore-ed25519.p12

OPENSSL_OPTS="-export -inkey privatekey-ed448.pem -name test -passout pass:password"
openssl pkcs12 $OPENSSL_OPTS -in jsign-test-certificate-ed448.pem -out keystore-ed448.p12

# Generate the Java keystores
KEYTOOL_OPTS="-importkeystore -srcstoretype pkcs12 -srcstorepass password -srcalias test -deststoretype jks -deststorepass password -destalias test"
keytool $KEYTOOL_OPTS -srckeystore keystore.p12 -destkeystore keystore.jks
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
23:2a:e7:47:43:b0:ae:4d:a7:4b:ea:e1:53:87:64:4a:f6:ca:7a:05
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Jsign Code Signing CA 2024
Validity
Not Before: May 10 13:08:21 2024 GMT
Not After : May 10 13:08:21 2044 GMT
Subject: CN = Jsign Code Signing Test Certificate 2024 (Ed25519)
Subject Public Key Info:
Public Key Algorithm: ED25519
ED25519 Public-Key:
pub:
23:f7:02:37:90:f2:31:ff:7a:a1:24:99:a2:48:77:
91:cc:ba:4c:44:bd:6b:c8:7c:cf:b3:ab:68:1e:2d:
2f:30
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature
X509v3 Extended Key Usage:
Code Signing
X509v3 Subject Key Identifier:
09:4F:86:91:81:F8:C1:BD:C4:B6:FE:C5:66:7E:19:A9:17:44:21:4A
X509v3 Authority Key Identifier:
E2:71:4A:A3:53:E9:58:2A:7B:94:44:3D:3B:AE:8A:01:D4:E5:04:FD
Authority Information Access:
CA Issuers - URI:http://raw.githubusercontent.com/ebourg/jsign/master/jsign-core/src/test/resources/keystores/jsign-code-signing-ca.cer
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
0d:e8:75:5a:94:4c:0c:a1:b0:c6:8b:0d:98:c7:31:3b:5e:a8:
c6:7f:6d:c4:11:bf:27:5c:93:9f:28:9d:97:40:1d:63:83:c2:
bf:ff:f4:e1:fd:b6:43:fd:5d:6e:99:b9:e2:82:aa:c1:3c:b2:
c3:86:c4:bf:d1:ec:dd:23:d5:8e:1d:48:e6:10:d7:46:eb:fc:
a8:20:49:23:9c:1c:0d:83:22:54:92:ed:02:57:f7:22:20:a9:
5d:12:fb:ec:36:6c:85:b4:38:27:e4:b2:d8:26:7c:c1:a3:83:
22:25:58:ac:12:84:b6:7e:c6:17:1c:57:8e:bf:70:15:1b:67:
1d:a6:fe:9d:de:0a:e4:dc:92:f4:8c:c0:6f:a4:ef:2f:96:89:
05:23:c0:20:f9:4a:52:62:8c:cc:b6:8d:fb:f8:0c:5d:4f:11:
85:1a:5b:57:08:98:e8:01:7e:19:9a:c8:f8:6a:0f:b4:4b:d2:
9f:84:a5:bb:ce:c4:5d:1f:96:4d:b8:cb:d2:26:4a:df:66:45:
32:a1:10:d2:e3:c3:9d:57:45:9e:55:b8:d7:5f:a9:56:2a:2f:
e6:df:b4:5f:8a:9c:40:e8:8f:ee:2f:d7:bb:0c:d9:3e:94:dd:
57:fb:aa:55:18:2e:3a:f3:91:a3:42:a3:85:a5:46:a3:88:5f:
ae:ec:79:ad
-----BEGIN CERTIFICATE-----
MIIDATCCAemgAwIBAgIUIyrnR0Owrk2nS+rhU4dkSvbKegUwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaSnNpZ24gQ29kZSBTaWduaW5nIENBIDIwMjQwHhcNMjQw
NTEwMTMwODIxWhcNNDQwNTEwMTMwODIxWjA9MTswOQYDVQQDDDJKc2lnbiBDb2Rl
IFNpZ25pbmcgVGVzdCBDZXJ0aWZpY2F0ZSAyMDI0IChFZDI1NTE5KTAqMAUGAytl
cAMhACP3AjeQ8jH/eqEkmaJId5HMukxEvWvIfM+zq2geLS8wo4IBCTCCAQUwCQYD
VR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYDVR0O
BBYEFAlPhpGB+MG9xLb+xWZ+GakXRCFKMB8GA1UdIwQYMBaAFOJxSqNT6Vgqe5RE
PTuuigHU5QT9MIGVBggrBgEFBQcBAQSBiDCBhTCBggYIKwYBBQUHMAKGdmh0dHA6
Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Vib3VyZy9qc2lnbi9tYXN0ZXIv
anNpZ24tY29yZS9zcmMvdGVzdC9yZXNvdXJjZXMva2V5c3RvcmVzL2pzaWduLWNv
ZGUtc2lnbmluZy1jYS5jZXIwDQYJKoZIhvcNAQELBQADggEBAA3odVqUTAyhsMaL
DZjHMTteqMZ/bcQRvydck58onZdAHWODwr//9OH9tkP9XW6ZueKCqsE8ssOGxL/R
7N0j1Y4dSOYQ10br/KggSSOcHA2DIlSS7QJX9yIgqV0S++w2bIW0OCfkstgmfMGj
gyIlWKwShLZ+xhccV46/cBUbZx2m/p3eCuTckvSMwG+k7y+WiQUjwCD5SlJijMy2
jfv4DF1PEYUaW1cImOgBfhmayPhqD7RL0p+EpbvOxF0flk24y9ImSt9mRTKhENLj
w51XRZ5VuNdfqVYqL+bftF+KnEDoj+4v17sM2T6U3Vf7qlUYLjrzkaNCo4WlRqOI
X67sea0=
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
23:2a:e7:47:43:b0:ae:4d:a7:4b:ea:e1:53:87:64:4a:f6:ca:7a:06
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Jsign Code Signing CA 2024
Validity
Not Before: May 10 13:08:21 2024 GMT
Not After : May 10 13:08:21 2044 GMT
Subject: CN = Jsign Code Signing Test Certificate 2024 (Ed448)
Subject Public Key Info:
Public Key Algorithm: ED448
ED448 Public-Key:
pub:
78:06:56:5e:32:f1:1a:7e:b2:cf:70:b1:15:52:32:
d4:5d:f9:cb:48:ec:08:72:cf:e6:25:7a:19:fa:c4:
85:7d:2d:51:ab:3b:ee:fe:70:c6:a8:96:bc:37:01:
d1:59:72:d2:bb:f1:44:7e:d9:17:65:00
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature
X509v3 Extended Key Usage:
Code Signing
X509v3 Subject Key Identifier:
3A:BD:FC:B1:93:A6:62:8D:23:A7:C5:65:AB:91:8E:76:1A:88:92:77
X509v3 Authority Key Identifier:
E2:71:4A:A3:53:E9:58:2A:7B:94:44:3D:3B:AE:8A:01:D4:E5:04:FD
Authority Information Access:
CA Issuers - URI:http://raw.githubusercontent.com/ebourg/jsign/master/jsign-core/src/test/resources/keystores/jsign-code-signing-ca.cer
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
53:39:73:2c:fa:bb:45:cc:bc:7d:ea:43:f4:c6:59:54:58:fe:
87:4e:c7:c8:9c:cc:a9:55:ac:a0:da:57:88:7c:f5:51:07:f5:
67:21:f5:eb:33:ac:70:7f:a9:9a:d7:d4:eb:0d:91:10:d8:8b:
f8:5a:df:5d:74:3a:d8:df:2f:0e:9e:ac:c6:d5:95:b4:13:35:
34:64:07:c9:fa:36:57:99:49:1b:73:ae:02:63:3b:af:f9:bf:
6d:03:ec:3b:cc:2d:76:70:82:d6:47:5c:75:ae:cd:79:26:7e:
87:57:25:a5:93:23:c0:f9:a0:b7:84:49:27:b6:6d:0b:f2:a6:
aa:be:de:46:ea:d8:0b:6a:81:69:ea:f2:59:75:37:90:d1:86:
5b:f1:4e:50:7c:fb:83:fb:a5:36:1c:ef:d1:dc:99:76:12:cf:
8a:28:9e:13:cf:bb:ae:ab:7e:9e:27:f3:db:d9:17:17:f8:33:
c8:02:d6:6e:54:98:94:69:0c:bd:73:05:50:7d:fb:61:57:f9:
db:5e:2b:de:02:1c:a3:a1:70:96:29:60:0c:5a:92:7b:24:e9:
cf:01:76:17:2d:de:63:e2:84:0d:ce:50:39:f2:8a:f3:b7:1e:
8e:62:b2:94:d5:32:0d:81:15:ce:01:e4:8a:07:ec:64:a6:e4:
c0:5e:01:52
-----BEGIN CERTIFICATE-----
MIIDGDCCAgCgAwIBAgIUIyrnR0Owrk2nS+rhU4dkSvbKegYwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaSnNpZ24gQ29kZSBTaWduaW5nIENBIDIwMjQwHhcNMjQw
NTEwMTMwODIxWhcNNDQwNTEwMTMwODIxWjA7MTkwNwYDVQQDDDBKc2lnbiBDb2Rl
IFNpZ25pbmcgVGVzdCBDZXJ0aWZpY2F0ZSAyMDI0IChFZDQ0OCkwQzAFBgMrZXED
OgB4BlZeMvEafrLPcLEVUjLUXfnLSOwIcs/mJXoZ+sSFfS1Rqzvu/nDGqJa8NwHR
WXLSu/FEftkXZQCjggEJMIIBBTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIHgDATBgNV
HSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUOr38sZOmYo0jp8Vlq5GOdhqIkncw
HwYDVR0jBBgwFoAU4nFKo1PpWCp7lEQ9O66KAdTlBP0wgZUGCCsGAQUFBwEBBIGI
MIGFMIGCBggrBgEFBQcwAoZ2aHR0cDovL3Jhdy5naXRodWJ1c2VyY29udGVudC5j
b20vZWJvdXJnL2pzaWduL21hc3Rlci9qc2lnbi1jb3JlL3NyYy90ZXN0L3Jlc291
cmNlcy9rZXlzdG9yZXMvanNpZ24tY29kZS1zaWduaW5nLWNhLmNlcjANBgkqhkiG
9w0BAQsFAAOCAQEAUzlzLPq7Rcy8fepD9MZZVFj+h07HyJzMqVWsoNpXiHz1UQf1
ZyH16zOscH+pmtfU6w2RENiL+FrfXXQ62N8vDp6sxtWVtBM1NGQHyfo2V5lJG3Ou
AmM7r/m/bQPsO8wtdnCC1kdcda7NeSZ+h1clpZMjwPmgt4RJJ7ZtC/Kmqr7eRurY
C2qBaeryWXU3kNGGW/FOUHz7g/ulNhzv0dyZdhLPiiieE8+7rqt+nifz29kXF/gz
yALWblSYlGkMvXMFUH37YVf5214r3gIco6FwlilgDFqSeyTpzwF2Fy3eY+KEDc5Q
OfKK87cejmKylNUyDYEVzgHkigfsZKbkwF4BUg==
-----END CERTIFICATE-----
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIPct5Gzf9mxFpSHqEqgdRfq9bc2UAddOZSKcFfT5px7Z
-----END PRIVATE KEY-----
4 changes: 4 additions & 0 deletions jsign-core/src/test/resources/keystores/privatekey-ed448.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOawzggKYvvx3TrGySJ3cBEpaj+I8Mq/3HV2U+6aTKDll
GbhxK885+vSC5scbnsiqEWNjUwy84JoVPg==
-----END PRIVATE KEY-----

0 comments on commit c723528

Please sign in to comment.