This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
The following commit(s) were added to refs/heads/master by this push:
new 9fc0f5bb6 WSS-717 Encryption with key identifier X509SKI (#438)
9fc0f5bb6 is described below
commit 9fc0f5bb6e76f2482924bf706f78f95e2faeeea6
Author: Robin K. <[email protected]>
AuthorDate: Wed Feb 5 12:06:11 2025 +0100
WSS-717 Encryption with key identifier X509SKI (#438)
* WSS-717 Added support for encryption with key identifier type X509_SKI -
a base64 encoded plain X.509 SKI extension value wrapped in X509Data as a
direct child of a KeyInfo element
* Merge and compatibility adjustments after #418
* Enhanced unit test for X509SKI
* Unused import
* DOMX509SKI: restore "final" for instance variable skiBytes
* Restored the "else" clause in
WSSecEncryptedKey#createEncryptedKeyElement, added a new "else if" clause for
X509SKI, extracted the KeyInfo generation into a new method as it is needed for
X509SKI and the "old" cases
---
.../org/apache/wss4j/common/token/DOMX509Data.java | 10 ++++
.../org/apache/wss4j/common/token/DOMX509SKI.java | 17 +++++++
.../java/org/apache/wss4j/dom/WSConstants.java | 6 +++
.../wss4j/dom/message/WSSecEncryptedKey.java | 57 ++++++++++++++--------
.../apache/wss4j/dom/message/EncryptionTest.java | 46 +++++++++++++++++
5 files changed, 115 insertions(+), 21 deletions(-)
diff --git
a/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509Data.java
b/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509Data.java
index d21f00c7b..93a934d25 100644
---
a/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509Data.java
+++
b/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509Data.java
@@ -58,6 +58,16 @@ public final class DOMX509Data {
element.appendChild(domIssuerSerial.getElement());
}
+ /**
+ * Constructor.
+ */
+ public DOMX509Data(Document doc, DOMX509SKI x509SKI) {
+ element =
+ doc.createElementNS(WSS4JConstants.SIG_NS, "ds:X509Data");
+
+ element.appendChild(x509SKI.getElement());
+ }
+
/**
* Return true if this X509Data element contains a X509IssuerSerial element
*/
diff --git
a/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509SKI.java
b/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509SKI.java
index cf05cf486..b9bfc2476 100644
---
a/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509SKI.java
+++
b/ws-security-common/src/main/java/org/apache/wss4j/common/token/DOMX509SKI.java
@@ -19,10 +19,15 @@
package org.apache.wss4j.common.token;
+import org.apache.wss4j.common.WSS4JConstants;
+import org.apache.wss4j.common.crypto.BouncyCastleUtils;
import org.apache.wss4j.common.util.DOM2Writer;
+import org.w3c.dom.Document;
import org.apache.wss4j.common.util.XMLUtils;
import org.w3c.dom.Element;
+import java.security.cert.X509Certificate;
+
/**
* An X.509 SKI token.
@@ -31,6 +36,18 @@ public final class DOMX509SKI {
private final Element element;
private final byte[] skiBytes;
+ /**
+ * Constructor.
+ */
+ public DOMX509SKI(Document doc, X509Certificate remoteCertificate) {
+ skiBytes =
BouncyCastleUtils.getSubjectKeyIdentifierBytes(remoteCertificate);
+
+ element = doc.createElementNS(WSS4JConstants.SIG_NS, "ds:X509SKI");
+ element.setTextContent(
+ org.apache.xml.security.utils.XMLUtils.encodeToString(skiBytes
+ ));
+ }
+
/**
* Constructor.
*/
diff --git
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/WSConstants.java
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/WSConstants.java
index 34f92ef71..fcaa1dc95 100644
--- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/WSConstants.java
+++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/WSConstants.java
@@ -341,6 +341,12 @@ public final class WSConstants extends WSS4JConstants {
*/
public static final int ISSUER_SERIAL_QUOTE_FORMAT = 15;
+ /**
+ * <code>X509_SKI</code> is used to set a ds:X509Data/ds:KeyValue element
to refer to
+ * the base64 encoded plain value of a X509 V.3 SubjectKeyIdentifier
extension
+ */
+ public static final int X509_SKI = 16;
+
/*
* The following values are bits that can be combined to form a set.
* Be careful when adding new constants.
diff --git
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
index 204c311a4..77e8c80d1 100644
---
a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
+++
b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
@@ -260,10 +260,10 @@ public class WSSecEncryptedKey extends WSSecBase {
}
/**
- * Now we need to setup the EncryptedKey header block:
+ * Now we need to set up the EncryptedKey header block:
* 1) create a EncryptedKey element and set a wsu:Id for it
- * 2) Generate ds:KeyInfo element, this wraps the
wsse:SecurityTokenReference
- * 3) Create and set up the SecurityTokenReference according to the
keyIdentifier parameter
+ * 2) Generate ds:KeyInfo element
+ * 3) Create and set up the ds:KeyInfo child element - this can either be
a SecurityTokenReference or X509Data/X509SKI
* 4) Create the CipherValue element structure and insert the encrypted
session key
*/
protected void createEncryptedKeyElement(X509Certificate remoteCert,
Crypto crypto, KeyAgreementParameters dhSpec)
@@ -276,6 +276,12 @@ public class WSSecEncryptedKey extends WSSecBase {
if (customEKKeyInfoElement != null) {
encryptedKeyElement.appendChild(getDocument().adoptNode(customEKKeyInfoElement));
+ } else if (keyIdentifierType == WSConstants.X509_SKI) {
+ DOMX509SKI x509SKI = new DOMX509SKI(getDocument(), remoteCert);
+ DOMX509Data x509Data = new DOMX509Data(getDocument(), x509SKI);
+
+ Element keyInfoElement =
createKeyInfoElement(x509Data.getElement(), dhSpec);
+ encryptedKeyElement.appendChild(keyInfoElement);
} else {
SecurityTokenReference secToken = new
SecurityTokenReference(getDocument());
if (addWSUNamespace) {
@@ -378,29 +384,38 @@ public class WSSecEncryptedKey extends WSSecBase {
throw new
WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unsupportedKeyId",
new Object[]
{keyIdentifierType});
}
- Element keyInfoElement =
+
+ Element keyInfoElement =
createKeyInfoElement(secToken.getElement(), dhSpec);
+ encryptedKeyElement.appendChild(keyInfoElement);
+ }
+ }
+
+ /**
+ * Method builds and returns a ds:KeyInfo element wrapping the provided
child element.
+ */
+ private Element createKeyInfoElement(Element childElement,
KeyAgreementParameters dhSpec) throws WSSecurityException {
+ Element keyInfoElement =
getDocument().createElementNS(
- WSConstants.SIG_NS, WSConstants.SIG_PREFIX + ":" +
WSConstants.KEYINFO_LN
+ WSConstants.SIG_NS, WSConstants.SIG_PREFIX + ":" +
WSConstants.KEYINFO_LN
);
- keyInfoElement.setAttributeNS(
+ keyInfoElement.setAttributeNS(
WSConstants.XMLNS_NS, "xmlns:" + WSConstants.SIG_PREFIX,
WSConstants.SIG_NS
- );
- if (isKeyAgreementConfigured(keyAgreementMethod)) {
- try {
- AgreementMethodImpl agreementMethod = new
AgreementMethodImpl(getDocument(), dhSpec);
-
agreementMethod.getRecipientKeyInfo().addUnknownElement(secToken.getElement());
- Element agreementMethodElement =
agreementMethod.getElement();
- keyInfoElement.appendChild(agreementMethodElement);
- } catch (XMLSecurityException e) {
- throw new
WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unsupportedKeyId",
- new Object[] {keyIdentifierType});
- }
-
- } else {
- keyInfoElement.appendChild(secToken.getElement());
+ );
+ if (isKeyAgreementConfigured(keyAgreementMethod)) {
+ try {
+ AgreementMethodImpl agreementMethod = new
AgreementMethodImpl(getDocument(), dhSpec);
+
agreementMethod.getRecipientKeyInfo().addUnknownElement(childElement);
+ Element agreementMethodElement = agreementMethod.getElement();
+ keyInfoElement.appendChild(agreementMethodElement);
+ } catch (XMLSecurityException e) {
+ throw new
WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unsupportedKeyId",
+ new Object[]
{keyIdentifierType});
}
- encryptedKeyElement.appendChild(keyInfoElement);
+
+ } else {
+ keyInfoElement.appendChild(childElement);
}
+ return keyInfoElement;
}
/**
diff --git
a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
index cbcf1ada0..6e44a441b 100644
---
a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
+++
b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java
@@ -575,6 +575,52 @@ public class EncryptionTest {
verify(encryptedDoc, encCrypto, keystoreCallbackHandler);
}
+
+ /**
+ * Test that encrypts a WS-Security envelope.
+ * The test uses the X509_SKI key identifier type.
+ */
+ @Test
+ public void testEncryptionX509SKI() throws Exception {
+ Crypto encCrypto = CryptoFactory.getInstance("wss-ecdh.properties");
+
+ Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+ WSSecHeader secHeader = new WSSecHeader(doc);
+ secHeader.insertSecurityHeader();
+
+ WSSecEncrypt builder = new WSSecEncrypt(secHeader);
+ builder.setUserInfo("secp256r1");
+ builder.setKeyEncAlgo(WSConstants.KEYWRAP_AES128);
+ builder.setKeyAgreementMethod(WSConstants.AGREEMENT_METHOD_ECDH_ES);
+ builder.setKeyDerivationMethod(WSConstants.KEYDERIVATION_CONCATKDF);
+ builder.setDigestAlgorithm(WSS4JConstants.SHA256);
+ builder.setKeyIdentifierType(WSConstants.X509_SKI);
+
+ LOG.info("Before Encrypting X509SKI");
+ KeyGenerator keyGen =
KeyUtils.getKeyGenerator(WSConstants.AES_128_GCM);
+ SecretKey symmetricKey = keyGen.generateKey();
+
+ Document encryptedDoc = builder.build(encCrypto, symmetricKey);
+ LOG.info("After Encrypting X509SKI");
+
+ String outputString =
+ XMLUtils.prettyDocumentToString(encryptedDoc);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Encrypted message with X509SKI:");
+ LOG.debug(outputString);
+ }
+
+ assertTrue(outputString.contains("X509Data"));
+ assertTrue(outputString.contains("X509SKI"));
+
+ RequestData data = new RequestData();
+ data.setCallbackHandler(keystoreCallbackHandler);
+ data.setDecCrypto(encCrypto);
+ data.setIgnoredBSPRules(Collections.singletonList(BSPRule.R5426));
+ new WSSecurityEngine().processSecurityHeader(encryptedDoc, data);
+ }
+
/**
* Test that encrypts using EncryptedKeySHA1, where it uses a symmetric
key, rather than a
* generated session key which is then encrypted using a public key.