This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 3_0_x-fixes
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
The following commit(s) were added to refs/heads/3_0_x-fixes by this push:
new d6c605d81 Enable EdDSA signature (#260)
d6c605d81 is described below
commit d6c605d818059c7f2c04fdbd4f87b53dfb046914
Author: jrihtarsic <[email protected]>
AuthorDate: Tue Jan 16 08:10:42 2024 +0100
Enable EdDSA signature (#260)
* Enable EdDSA signature
* PR comments
---------
Co-authored-by: RIHTARSIC Joze <[email protected]>
---
.../org/apache/wss4j/common/WSS4JConstants.java | 7 +-
.../org/apache/wss4j/common/crypto/DERDecoder.java | 2 +
.../src/test/resources/keys/wss-eddsa.p12 | Bin 0 -> 1687 bytes
.../src/test/resources/wss-eddsa.properties | 4 +
.../apache/wss4j/dom/message/WSSecSignature.java | 53 ++++++++++
.../wss4j/dom/message/SignatureCertTest.java | 113 ++++++++++++++++++++-
6 files changed, 174 insertions(+), 5 deletions(-)
diff --git
a/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java
b/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java
index 66e3e3d20..af54a2090 100644
---
a/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java
+++
b/ws-security-common/src/main/java/org/apache/wss4j/common/WSS4JConstants.java
@@ -158,6 +158,11 @@ public class WSS4JConstants {
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256";
public static final String ECDSA_SHA512 =
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512";
+ // see RFC 9231 for these algorithm definitions
+ public static final String ED25519 =
+ "http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519";
+ public static final String ED448 =
+ "http://www.w3.org/2021/04/xmldsig-more#eddsa-ed448";
public static final String MGF_SHA1 =
"http://www.w3.org/2009/xmlenc11#mgf1sha1";
public static final String MGF_SHA224 =
"http://www.w3.org/2009/xmlenc11#mgf1sha224";
@@ -273,4 +278,4 @@ public class WSS4JConstants {
URI_SOAP11_ENV,
URI_SOAP12_ENV,
};
-}
\ No newline at end of file
+}
diff --git
a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/DERDecoder.java
b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/DERDecoder.java
index b08031ff3..78a89267b 100644
---
a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/DERDecoder.java
+++
b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/DERDecoder.java
@@ -45,6 +45,8 @@ public class DERDecoder {
public static final byte TYPE_OCTET_STRING = 0x04;
/** DER type identifier for a sequence value */
public static final byte TYPE_SEQUENCE = 0x30;
+ /** DER type identifier for ASN.1 "OBJECT IDENTIFIER" value. */
+ public static final byte TYPE_OBJECT_IDENTIFIER = 0x06;
private byte[] arr;
private int pos;
diff --git a/ws-security-common/src/test/resources/keys/wss-eddsa.p12
b/ws-security-common/src/test/resources/keys/wss-eddsa.p12
new file mode 100644
index 000000000..3e02ef16e
Binary files /dev/null and
b/ws-security-common/src/test/resources/keys/wss-eddsa.p12 differ
diff --git a/ws-security-common/src/test/resources/wss-eddsa.properties
b/ws-security-common/src/test/resources/wss-eddsa.properties
new file mode 100644
index 000000000..5f97a4a2f
--- /dev/null
+++ b/ws-security-common/src/test/resources/wss-eddsa.properties
@@ -0,0 +1,4 @@
+org.apache.wss4j.crypto.provider=org.apache.wss4j.common.crypto.Merlin
+org.apache.wss4j.crypto.merlin.keystore.type=PKCS12
+org.apache.wss4j.crypto.merlin.keystore.password=security
+org.apache.wss4j.crypto.merlin.keystore.file=keys/wss-eddsa.p12
diff --git
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
index b296ff901..74fbd1b4d 100644
---
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
+++
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
@@ -21,6 +21,7 @@ package org.apache.wss4j.dom.message;
import java.security.NoSuchProviderException;
import java.security.Provider;
+import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -46,6 +47,7 @@ import org.apache.wss4j.common.WSEncryptionPart;
import org.apache.wss4j.common.WSS4JConstants;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.crypto.DERDecoder;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.token.BinarySecurity;
import org.apache.wss4j.common.token.DOMX509Data;
@@ -887,6 +889,12 @@ public class WSSecSignature extends WSSecSignatureBase {
sigAlgo = WSConstants.RSA;
} else if (pubKeyAlgo.equalsIgnoreCase("EC")) {
sigAlgo = WSConstants.ECDSA_SHA256;
+ } else if (pubKeyAlgo.equalsIgnoreCase("Ed25519")) {
+ sigAlgo = WSConstants.ED25519;
+ } else if (pubKeyAlgo.equalsIgnoreCase("ED448")) {
+ sigAlgo = WSConstants.ED448;
+ } else if (pubKeyAlgo.equalsIgnoreCase("EdDSA")) {
+ sigAlgo =
getSigAlgorithmForEdDSAKey(certs[0].getPublicKey());
} else {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE,
@@ -898,6 +906,51 @@ public class WSSecSignature extends WSSecSignatureBase {
return certs;
}
+ /**
+ * The method returns EdDSA signature algorithm URI for public key type
(Ed25519 or Ed448).
+ *
+ * @param publicKey the public key to get the algorithm from
+ * @return the signature algorithm URI (ED25519 or ED448) for the EdDSA
public key
+ * @throws WSSecurityException if the algorithm cannot be determined
+ */
+ private static String getSigAlgorithmForEdDSAKey(PublicKey publicKey)
throws WSSecurityException {
+
+ if (!"x.509".equalsIgnoreCase(publicKey.getFormat())) {
+ throw new
WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unknownAlgorithm",
+ new Object[]{"Unknown cert format!"});
+ }
+
+ DERDecoder decoder = new DERDecoder(publicKey.getEncoded());
+ // find TYPE_OBJECT_IDENTIFIER (OID) for the public key algorithm
+ decoder.expect(decoder.TYPE_SEQUENCE);
+ decoder.getLength();
+ decoder.expect(decoder.TYPE_SEQUENCE);
+ decoder.getLength();
+ decoder.expect(decoder.TYPE_OBJECT_IDENTIFIER);
+ int size = decoder.getLength();
+ if (size != 3) {
+ LOG.debug("Invalid ECDSA Public key OID byte size: [{}]", size);
+ throw new
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY,
"invalidCert");
+ }
+
+ // The first two nodes 1.3 of the OID are encoded onto a single byte.
The first node is multiplied by 40
+ // and the result is added to the value of the second node 3 which
gives 43 or 0x2B.
+ decoder.expect(43);
+ // The second byte is expected 101 from the OID 1.3.101 (EdDSA)
+ decoder.expect(101);
+ // The third byte defines algorithm 112 is for Ed25519 and 113 is for
Ed448
+ byte algDef = decoder.getBytes(1)[0];
+ switch (algDef) {
+ case 112:
+ return WSConstants.ED25519;
+ case 113:
+ return WSConstants.ED448;
+ default:
+ throw new
WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY,
"unknownAlgorithm",
+ new Object[]{"Invalid ECDSA Public key OID!"});
+ }
+ }
+
public boolean isIncludeSignatureToken() {
return includeSignatureToken;
}
diff --git
a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java
b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java
index 985424272..81c1777c3 100644
---
a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java
+++
b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/SignatureCertTest.java
@@ -36,16 +36,17 @@ import org.apache.wss4j.dom.handler.RequestData;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import org.apache.wss4j.dom.handler.WSHandlerResult;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
+import javax.security.auth.x500.X500Principal;
+import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Properties;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.*;
/**
* This is a test for WSS-40. Essentially it just tests that a message is
signed using a
@@ -81,11 +82,18 @@ public class SignatureCertTest {
private WSSecurityEngine secEngine = new WSSecurityEngine();
private Crypto crypto;
private Crypto cryptoCA;
+ private boolean isJDK16up;
public SignatureCertTest() throws Exception {
WSSConfig.init();
crypto = CryptoFactory.getInstance("wss40.properties");
cryptoCA = CryptoFactory.getInstance("wss40CA.properties");
+ try {
+ int javaVersion = Integer.getInteger("java.specification.version",
0);
+ isJDK16up = javaVersion >= 16;
+ } catch (NumberFormatException ex) {
+ LOG.warn("Error in retrieving the java version: [{}]",
ex.getMessage());
+ }
}
/**
@@ -340,6 +348,103 @@ public class SignatureCertTest {
}
}
+ /**
+ * The Ed25519 KeyValue test.
+ */
+ @Test
+ public void testED25519SignatureDirectReference() throws Exception {
+ try {
+ // not needed after JDK 16
+ if (!isJDK16up) {
+ Security.addProvider(new
org.bouncycastle.jce.provider.BouncyCastleProvider());
+ }
+
+ Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+ WSSecHeader secHeader = new WSSecHeader(doc);
+ secHeader.insertSecurityHeader();
+
+ Crypto ed_crypto =
CryptoFactory.getInstance("wss-eddsa.properties");
+
+ WSSecSignature builder = new WSSecSignature(secHeader);
+ builder.setUserInfo("ed25519", "security");
+ builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
+ Document signedDoc = builder.build(ed_crypto);
+ // test the algorithm attribute
+ String outputString =
+ XMLUtils.prettyDocumentToString(signedDoc);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(outputString);
+ }
+
+
assertTrue(outputString.contains("Algorithm=\"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519\""));
+
+ final WSHandlerResult results = verify(signedDoc, ed_crypto);
+
+ WSSecurityEngineResult actionResult =
+ results.getActionResults().get(WSConstants.SIGN).get(0);
+ assertNotNull(actionResult);
+
+ java.security.Principal principal =
+ (java.security.Principal)
actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
+ assertTrue(principal instanceof X500Principal);
+ X500Principal x500Principal = (X500Principal) principal;
+ assertEquals(new X500Principal("CN=ED25519,O=EDELIVERY,C=EU"),
x500Principal);
+
+ } finally {
+ if (!isJDK16up) {
+ Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
+ }
+ }
+ }
+
+ /**
+ * Successful ECKeyValue test.
+ */
+ @Test
+ public void testED448KeyValue() throws Exception {
+ try {
+ // not needed after JDK 16
+ if (!isJDK16up) {
+ Security.addProvider(new
org.bouncycastle.jce.provider.BouncyCastleProvider());
+ }
+
+ Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+ WSSecHeader secHeader = new WSSecHeader(doc);
+ secHeader.insertSecurityHeader();
+
+ Crypto ed_crypto =
CryptoFactory.getInstance("wss-eddsa.properties");
+
+ WSSecSignature builder = new WSSecSignature(secHeader);
+ builder.setUserInfo("ed448", "security");
+ builder.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER);
+ Document signedDoc = builder.build(ed_crypto);
+
+ String outputString =
+ XMLUtils.prettyDocumentToString(signedDoc);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(outputString);
+ }
+
+
assertTrue(outputString.contains("Algorithm=\"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed448\""));
+
+ final WSHandlerResult results = verify(signedDoc, ed_crypto);
+
+ WSSecurityEngineResult actionResult =
+ results.getActionResults().get(WSConstants.SIGN).get(0);
+ assertNotNull(actionResult);
+
+ java.security.Principal principal =
+ (java.security.Principal)
actionResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
+ assertTrue(principal instanceof X500Principal);
+ X500Principal x500Principal = (X500Principal) principal;
+ assertEquals(new X500Principal("CN=ED448, O=EDELIVERY, C=EU"),
x500Principal);
+ } finally {
+ if (!isJDK16up) {
+ Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
+ }
+ }
+ }
+
/**
* Verifies the soap envelope
* <p/>
@@ -361,4 +466,4 @@ public class SignatureCertTest {
}
-}
\ No newline at end of file
+}