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 <jrihtars...@gmail.com>
AuthorDate: Tue Jan 16 08:10:42 2024 +0100

    Enable EdDSA signature (#260)
    
    * Enable EdDSA signature
    
    * PR comments
    
    ---------
    
    Co-authored-by: RIHTARSIC Joze <joze.rihtar...@ext.ec.europa.eu>
---
 .../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
+}

Reply via email to