diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index fb8d447c8..cbd67652d 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -39,6 +39,10 @@ exports org.apache.xml.security.c14n.implementations; exports org.apache.xml.security.configuration; exports org.apache.xml.security.encryption; + exports org.apache.xml.security.encryption.keys; + exports org.apache.xml.security.encryption.keys.content; + exports org.apache.xml.security.encryption.keys.content.derivedKey; + exports org.apache.xml.security.encryption.params; exports org.apache.xml.security.exceptions; exports org.apache.xml.security.keys; exports org.apache.xml.security.keys.content; diff --git a/src/main/java/org/apache/xml/security/algorithms/MessageDigestAlgorithm.java b/src/main/java/org/apache/xml/security/algorithms/MessageDigestAlgorithm.java index 884a8abcf..6ff822b70 100644 --- a/src/main/java/org/apache/xml/security/algorithms/MessageDigestAlgorithm.java +++ b/src/main/java/org/apache/xml/security/algorithms/MessageDigestAlgorithm.java @@ -99,7 +99,7 @@ public static MessageDigestAlgorithm getInstance( return new MessageDigestAlgorithm(doc, algorithmURI); } - private static MessageDigest getDigestInstance(String algorithmURI) throws XMLSignatureException { + public static MessageDigest getDigestInstance(String algorithmURI) throws XMLSignatureException { String algorithmID = JCEMapper.translateURItoJCEID(algorithmURI); if (algorithmID == null) { diff --git a/src/main/java/org/apache/xml/security/encryption/AgreementMethod.java b/src/main/java/org/apache/xml/security/encryption/AgreementMethod.java index 532f1a465..0339b3a2c 100644 --- a/src/main/java/org/apache/xml/security/encryption/AgreementMethod.java +++ b/src/main/java/org/apache/xml/security/encryption/AgreementMethod.java @@ -18,9 +18,12 @@ */ package org.apache.xml.security.encryption; +import java.security.PublicKey; import java.util.Iterator; -import org.apache.xml.security.keys.KeyInfo; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.encryption.keys.OriginatorKeyInfo; +import org.apache.xml.security.encryption.keys.RecipientKeyInfo; import org.w3c.dom.Element; /** @@ -88,6 +91,22 @@ public interface AgreementMethod { */ void setKANonce(byte[] kanonce); + + /** + * Returns KeyDerivationMethod information used in the AgreementMethod. + * @return The KeyDerivationMethod information regarding the AgreementMethod. + */ + KeyDerivationMethod getKeyDerivationMethod() throws XMLSecurityException; + + /** + * This method is used to set the KeyDerivationMethod when the AgreementMethod is being + * used to derive a key. The KeyDerivationMethod is declared as + * but is used in ECDH_ES + * + * @param keyDerivationMethod + */ + void setKeyDerivationMethod(KeyDerivationMethod keyDerivationMethod); + /** * Returns additional information regarding the AgreementMethod. * @return additional information regarding the AgreementMethod. @@ -114,35 +133,42 @@ public interface AgreementMethod { * * */ - void revoveAgreementMethodInformation(Element info); + void removeAgreementMethodInformation(Element info); /** * Returns information relating to the originator's shared secret. * * @return information relating to the originator's shared secret. */ - KeyInfo getOriginatorKeyInfo(); + OriginatorKeyInfo getOriginatorKeyInfo() throws XMLSecurityException; /** * Sets the information relating to the originator's shared secret. * * @param keyInfo information relating to the originator's shared secret. */ - void setOriginatorKeyInfo(KeyInfo keyInfo); + void setOriginatorKeyInfo(OriginatorKeyInfo keyInfo); + + /** + * Sets the originator's PublicKey to generate the secret + * + * @param publicKey originator's PublicKey + */ + void setOriginatorPublicKey(PublicKey publicKey); /** * Returns information relating to the recipient's shared secret. * * @return information relating to the recipient's shared secret. */ - KeyInfo getRecipientKeyInfo(); + RecipientKeyInfo getRecipientKeyInfo() throws XMLSecurityException; /** * Sets the information relating to the recipient's shared secret. * * @param keyInfo information relating to the recipient's shared secret. */ - void setRecipientKeyInfo(KeyInfo keyInfo); + void setRecipientKeyInfo(RecipientKeyInfo keyInfo); /** * Returns the algorithm URI of this CryptographicMethod. diff --git a/src/main/java/org/apache/xml/security/encryption/KeyDerivationMethod.java b/src/main/java/org/apache/xml/security/encryption/KeyDerivationMethod.java new file mode 100644 index 000000000..b180043c5 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/KeyDerivationMethod.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption; + +/** + * The key derivation is to generate new cryptographic key material from existing key material such as the shared + * secret and any other (private or public) information. The purpose of the key derivation is an extension of a given + * but limited set of original key materials and to limit the use (exposure) of such key material. + * + * The Schema for KeyDerivationMethod is as follows: + *
+ * 
+ * 
+ *   
+ *     
+ *   
+ *   
+ * 
+ * 
+ */ +public interface KeyDerivationMethod { + + /** + * Returns the algorithm URI of this KeyDerivationMethod. + * + * @return the algorithm URI of this KeyDerivationMethod + */ + String getAlgorithm(); +} diff --git a/src/main/java/org/apache/xml/security/encryption/XMLCipher.java b/src/main/java/org/apache/xml/security/encryption/XMLCipher.java index ff7426941..75075f904 100644 --- a/src/main/java/org/apache/xml/security/encryption/XMLCipher.java +++ b/src/main/java/org/apache/xml/security/encryption/XMLCipher.java @@ -27,12 +27,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; +import java.security.*; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.MGF1ParameterSpec; import java.util.ArrayList; @@ -58,8 +53,11 @@ import org.apache.xml.security.algorithms.MessageDigestAlgorithm; import org.apache.xml.security.c14n.Canonicalizer; import org.apache.xml.security.c14n.InvalidCanonicalizerException; +import org.apache.xml.security.encryption.keys.KeyInfoEnc; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; import org.apache.xml.security.exceptions.XMLSecurityException; import org.apache.xml.security.keys.KeyInfo; +import org.apache.xml.security.encryption.keys.content.AgreementMethodImpl; import org.apache.xml.security.keys.keyresolver.KeyResolverException; import org.apache.xml.security.keys.keyresolver.KeyResolverSpi; import org.apache.xml.security.keys.keyresolver.implementations.EncryptedKeyResolver; @@ -67,10 +65,7 @@ import org.apache.xml.security.stax.ext.XMLSecurityConstants; import org.apache.xml.security.transforms.InvalidTransformException; import org.apache.xml.security.transforms.TransformationException; -import org.apache.xml.security.utils.Constants; -import org.apache.xml.security.utils.ElementProxy; -import org.apache.xml.security.utils.EncryptionConstants; -import org.apache.xml.security.utils.XMLUtils; +import org.apache.xml.security.utils.*; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -148,6 +143,12 @@ public final class XMLCipher { public static final String DIFFIE_HELLMAN = EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH; + /** + * DIFFIE_HELLMAN ES Cipher for Elliptic curve and X keys + */ + public static final String DIFFIE_HELLMAN_EC = + EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES; + /** Triple DES EDE (192 bit key) in CBC mode KEYWRAP*/ public static final String TRIPLEDES_KeyWrap = EncryptionConstants.ALGO_ID_KEYWRAP_TRIPLEDES; @@ -1299,7 +1300,7 @@ public EncryptedKey loadEncryptedKey(Element element) throws XMLEncryptionExcept * @throws XMLEncryptionException */ public EncryptedKey encryptKey(Document doc, Key key) throws XMLEncryptionException { - return encryptKey(doc, key, null, null); + return encryptKey(doc, key, (String) null, null); } /** @@ -1341,7 +1342,19 @@ public EncryptedKey encryptKey( byte[] oaepParams, SecureRandom random ) throws XMLEncryptionException { - LOG.log(Level.DEBUG, "Encrypting key ..."); + + OAEPParameterSpec oaepParameters = + XMLCipherUtil.constructOAEPParameters(algorithm, digestAlg, mgfAlgorithm, oaepParams); + return encryptKey(doc, key, oaepParameters, random); + } + + public EncryptedKey encryptKey( + Document doc, + Key key, + AlgorithmParameterSpec params, + SecureRandom random + ) throws XMLEncryptionException { + LOG.log(Level.DEBUG, "Encrypting key using algorithm specs [{0}] ...", params); if (null == key) { throw new XMLEncryptionException("empty", "Key unexpectedly null..."); @@ -1364,26 +1377,33 @@ public EncryptedKey encryptKey( } else { c = contextCipher; } - // Now perform the encryption + AlgorithmParameterSpec cipherSpec = null; + Key wrapKey = this.key; + if (params instanceof OAEPParameterSpec) { + cipherSpec = params; + } else if (params instanceof KeyAgreementParameters) { + KeyAgreementParameters keyAgreementParameter = (KeyAgreementParameters) params; + validateAndUpdateKeyAgreementParameterKeys(keyAgreementParameter); + // Generate a key using the key Agreement Parameters for the wrap algorithm + wrapKey = KeyUtils.aesWrapKeyWithDHGeneratedKey(keyAgreementParameter); + } else if (params != null) { + throw new XMLEncryptionException("encryption.UnsupportedAlgorithmParameterSpec", params.getClass().getName()); + } + + // Now perform the encryption try { - // Should internally generate an IV - // todo - allow user to set an IV - OAEPParameterSpec oaepParameters = - constructOAEPParameters( - algorithm, digestAlg, mgfAlgorithm, oaepParams - ); if (random != null) { - if (oaepParameters == null) { - c.init(Cipher.WRAP_MODE, this.key, random); + if (cipherSpec == null) { + c.init(Cipher.WRAP_MODE, wrapKey, random); } else { - c.init(Cipher.WRAP_MODE, this.key, oaepParameters, random); + c.init(Cipher.WRAP_MODE, wrapKey, cipherSpec, random); } } else { - if (oaepParameters == null) { - c.init(Cipher.WRAP_MODE, this.key); + if (cipherSpec == null) { + c.init(Cipher.WRAP_MODE, wrapKey); } else { - c.init(Cipher.WRAP_MODE, this.key, oaepParameters); + c.init(Cipher.WRAP_MODE, wrapKey, cipherSpec); } } encryptedBytes = c.wrap(key); @@ -1401,9 +1421,24 @@ public EncryptedKey encryptKey( try { EncryptionMethod method = factory.newEncryptionMethod(new URI(algorithm).toString()); method.setDigestAlgorithm(digestAlg); - method.setMGFAlgorithm(mgfAlgorithm); - method.setOAEPparams(oaepParams); ek.setEncryptionMethod(method); + if (params instanceof OAEPParameterSpec) { + OAEPParameterSpec oaepSpec = (OAEPParameterSpec) params; + String mgf1Uri = XMLCipherUtil.getMgf1URIForParameter((MGF1ParameterSpec) oaepSpec.getMGFParameters()); + method.setMGFAlgorithm(mgf1Uri); + if (PSource.PSpecified.DEFAULT != oaepSpec.getPSource() && oaepSpec.getPSource() instanceof PSource.PSpecified) { + byte[] pSourceParams = ((PSource.PSpecified) oaepSpec.getPSource()).getValue(); + method.setOAEPparams(pSourceParams); + } + } else if (params instanceof KeyAgreementParameters) { + KeyAgreementParameters keyAgreementParameter = (KeyAgreementParameters) params; + AgreementMethodImpl agreementMethod = new AgreementMethodImpl(contextDocument, keyAgreementParameter); + + KeyInfoEnc keyInfo = new KeyInfoEnc(contextDocument); + keyInfo.add(agreementMethod); + ek.setKeyInfo(keyInfo); + } + } catch (URISyntaxException ex) { throw new XMLEncryptionException(ex); } @@ -1440,7 +1475,8 @@ public Key decryptKey(EncryptedKey encryptedKey, String algorithm) try { String keyWrapAlg = encryptedKey.getEncryptionMethod().getAlgorithm(); String keyType = JCEMapper.getJCEKeyAlgorithmFromURI(keyWrapAlg); - if ("RSA".equals(keyType) || "EC".equals(keyType)) { + if ( !(ki instanceof KeyInfoEnc && ((KeyInfoEnc)ki).containsAgreementMethod()) + && ("RSA".equals(keyType) || "EC".equals(keyType))) { key = ki.getPrivateKey(); } else { key = ki.getSecretKey(); @@ -1478,17 +1514,23 @@ public Key decryptKey(EncryptedKey encryptedKey, String algorithm) Key ret; + AlgorithmParameterSpec params; try { - EncryptionMethod encMethod = encryptedKey.getEncryptionMethod(); - OAEPParameterSpec oaepParameters = - constructOAEPParameters( - encMethod.getAlgorithm(), encMethod.getDigestAlgorithm(), - encMethod.getMGFAlgorithm(), encMethod.getOAEPparams() - ); - if (oaepParameters == null) { + params = getAlgorithmParameters(encryptedKey); + } catch (XMLSecurityException e) { + throw new XMLEncryptionException(e); + } + + try { + + if (params == null) { c.init(Cipher.UNWRAP_MODE, key); - } else { - c.init(Cipher.UNWRAP_MODE, key, oaepParameters); + } else if (params instanceof OAEPParameterSpec) { + c.init(Cipher.UNWRAP_MODE, key, params); + } + if (params instanceof KeyAgreementParameters) { + Key wrapKey = KeyUtils.aesWrapKeyWithDHGeneratedKey((KeyAgreementParameters) params); + c.init(Cipher.UNWRAP_MODE, wrapKey); } ret = c.unwrap(encryptedBytes, jceKeyAlgorithm, Cipher.SECRET_KEY); } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { @@ -1500,42 +1542,77 @@ public Key decryptKey(EncryptedKey encryptedKey, String algorithm) } /** - * Construct an OAEPParameterSpec object from the given parameters + * Method validates and updates if needed the KeyAgreementParameterSpec with the required keys. + * + * @param keyAgreementParameter KeyAgreementParameterSpec to be validated and updated + * with the required keys if needed */ - private OAEPParameterSpec constructOAEPParameters( - String encryptionAlgorithm, - String digestAlgorithm, - String mgfAlgorithm, - byte[] oaepParams - ) { - if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithm) - || XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithm)) { - - String jceDigestAlgorithm = "SHA-1"; - if (digestAlgorithm != null) { - jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgorithm); + public void validateAndUpdateKeyAgreementParameterKeys(KeyAgreementParameters keyAgreementParameter) throws XMLEncryptionException { + if (keyAgreementParameter == null) { + return; + } + // check if the recipient's public key is set in keyAgreementParameter, if not, use the recipient's public key + // specified in XMLCipher instance init method. + if (keyAgreementParameter.getRecipientPublicKey() == null && this.key != null) { + if (this.key instanceof PublicKey) { + LOG.log(Level.DEBUG, "Recipient's public key is not set in keyAgreementParameter, " + + "use the recipient's public key specified in XMLCipher instance init method."); + keyAgreementParameter.setRecipientPublicKey((PublicKey) this.key); + } else { + throw new XMLEncryptionException("algorithms.WrongKeyForThisOperation", + this.key.getClass().getName(), "java.security.PublicKey"); } + } - PSource.PSpecified pSource = PSource.PSpecified.DEFAULT; - if (oaepParams != null) { - pSource = new PSource.PSpecified(oaepParams); - } + // check if the originator's private key is still null + if (keyAgreementParameter.getRecipientPublicKey() == null) { + // recipient's public key is mandatory for key agreement algorithm. + throw new XMLEncryptionException("encryption.nokey"); + } - MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1"); - if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithm)) { - if (EncryptionConstants.MGF1_SHA224.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-224"); - } else if (EncryptionConstants.MGF1_SHA256.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-256"); - } else if (EncryptionConstants.MGF1_SHA384.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-384"); - } else if (EncryptionConstants.MGF1_SHA512.equals(mgfAlgorithm)) { - mgfParameterSpec = new MGF1ParameterSpec("SHA-512"); - } - } - return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource); + if (keyAgreementParameter.getOriginatorPrivateKey() == null) { + LOG.log(Level.DEBUG, "Originator's private key is not set in keyAgreementParameter, " + + "generate an ephemeral key for the originator's private key."); + KeyPair originatorKeyPair = KeyUtils.generateEphemeralDHKeyPair( + keyAgreementParameter.getRecipientPublicKey(), null); + keyAgreementParameter.setOriginatorKeyPair(originatorKeyPair); } + } + /** + * Method resolves the EncryptedKey using the EncryptedKeyResolver + * + * @param encryptedKey + * @return + * @throws XMLEncryptionException + */ + private AlgorithmParameterSpec getAlgorithmParameters(EncryptedKey encryptedKey) throws XMLSecurityException { + if (encryptedKey == null + || encryptedKey.getEncryptionMethod() == null + || encryptedKey.getEncryptionMethod().getAlgorithm() == null) { + LOG.log(Level.DEBUG,"EncryptedKey key algorithm is null"); + return null; + } + + EncryptionMethod encMethod = encryptedKey.getEncryptionMethod(); + String encryptionAlgorithm = encMethod.getAlgorithm(); + + if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithm) + || XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithm)) { + LOG.log(Level.DEBUG,"EncryptedKey key algorithm is RSA OAEP"); + return XMLCipherUtil.constructOAEPParameters( + encryptionAlgorithm, encMethod.getDigestAlgorithm(), + encMethod.getMGFAlgorithm(), encMethod.getOAEPparams()); + } + + KeyInfoEnc keyInfo = encryptedKey.getKeyInfo() instanceof KeyInfoEnc ? (KeyInfoEnc) encryptedKey.getKeyInfo(): null; + if (keyInfo != null && keyInfo.containsAgreementMethod()) { + // resolve the agreement method + LOG.log(Level.DEBUG,"EncryptedKey key is using Key agreement data"); + AgreementMethod agreementMethod = keyInfo.itemAgreementMethod(0); + return XMLCipherUtil.constructRecipientKeyAgreementParameters(encryptionAlgorithm, + agreementMethod, (PrivateKey) this.key); + } return null; } @@ -2030,7 +2107,7 @@ private class Factory { * @return a new AgreementMethod */ AgreementMethod newAgreementMethod(String algorithm) { - return new AgreementMethodImpl(algorithm); + return new AgreementMethodImpl(contextDocument, algorithm); } /** @@ -2338,7 +2415,7 @@ EncryptedKey newEncryptedKey(Element element) throws XMLEncryptionException { */ KeyInfo newKeyInfo(Element element) throws XMLEncryptionException { try { - KeyInfo ki = new KeyInfo(element, null); + KeyInfoEnc ki = new KeyInfoEnc(element, null); ki.setSecureValidation(secureValidation); if (internalKeyResolvers != null) { int size = internalKeyResolvers.size(); @@ -2510,88 +2587,7 @@ Element toElement(ReferenceList referenceList) { return ((ReferenceListImpl) referenceList).toElement(); } - private class AgreementMethodImpl implements AgreementMethod { - private byte[] kaNonce; - private List agreementMethodInformation; - private KeyInfo originatorKeyInfo; - private KeyInfo recipientKeyInfo; - private String algorithmURI; - /** - * @param algorithm - */ - public AgreementMethodImpl(String algorithm) { - agreementMethodInformation = new LinkedList<>(); - URI tmpAlgorithm = null; - try { - tmpAlgorithm = new URI(algorithm); - } catch (URISyntaxException ex) { - throw (IllegalArgumentException) - new IllegalArgumentException().initCause(ex); - } - algorithmURI = tmpAlgorithm.toString(); - } - - /** {@inheritDoc} */ - @Override - public byte[] getKANonce() { - return kaNonce; - } - - /** {@inheritDoc} */ - @Override - public void setKANonce(byte[] kanonce) { - kaNonce = kanonce; - } - - /** {@inheritDoc} */ - @Override - public Iterator getAgreementMethodInformation() { - return agreementMethodInformation.iterator(); - } - - /** {@inheritDoc} */ - @Override - public void addAgreementMethodInformation(Element info) { - agreementMethodInformation.add(info); - } - - /** {@inheritDoc} */ - @Override - public void revoveAgreementMethodInformation(Element info) { - agreementMethodInformation.remove(info); - } - - /** {@inheritDoc} */ - @Override - public KeyInfo getOriginatorKeyInfo() { - return originatorKeyInfo; - } - - /** {@inheritDoc} */ - @Override - public void setOriginatorKeyInfo(KeyInfo keyInfo) { - originatorKeyInfo = keyInfo; - } - - /** {@inheritDoc} */ - @Override - public KeyInfo getRecipientKeyInfo() { - return recipientKeyInfo; - } - - /** {@inheritDoc} */ - @Override - public void setRecipientKeyInfo(KeyInfo keyInfo) { - recipientKeyInfo = keyInfo; - } - - /** {@inheritDoc} */ - @Override - public String getAlgorithm() { - return algorithmURI; - } - } private class CipherDataImpl implements CipherData { private static final String valueMessage = diff --git a/src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java b/src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java index 86d6873ca..13e48538b 100644 --- a/src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java +++ b/src/main/java/org/apache/xml/security/encryption/XMLCipherUtil.java @@ -21,13 +21,26 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.security.AccessController; +import java.security.PrivateKey; import java.security.PrivilegedAction; +import java.security.PublicKey; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; +import org.apache.xml.security.algorithms.JCEMapper; +import org.apache.xml.security.encryption.params.ConcatKDFParams; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; +import org.apache.xml.security.encryption.params.KeyDerivationParameters; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.encryption.keys.content.derivedKey.ConcatKDFParamsImpl; +import org.apache.xml.security.encryption.keys.content.derivedKey.KeyDerivationMethodImpl; import org.apache.xml.security.utils.EncryptionConstants; +import org.apache.xml.security.utils.KeyUtils; public final class XMLCipherUtil { @@ -81,4 +94,212 @@ private static AlgorithmParameterSpec constructBlockCipherParametersForGCMAlgori LOG.log(Level.DEBUG, "Successfully created GCMParameterSpec"); return gcmSpec; } + + /** + * Method buildOAEPParameters from given parameters and returns OAEPParameterSpec. If encryptionAlgorithmURI is + * not RSA_OAEP or RSA_OAEP_11, null is returned. + * + * @param encryptionAlgorithmURI the encryption algorithm URI (RSA_OAEP or RSA_OAEP_11) + * @param digestAlgorithmURI the digest algorithm URI + * @param mgfAlgorithmURI the MGF algorithm URI if encryptionAlgorithmURI is RSA_OAEP_11, otherwise parameter is ignored + * @param oaepParams the OAEP parameters bytes + * @return OAEPParameterSpec or null if encryptionAlgorithmURI is not RSA_OAEP or RSA_OAEP_11 + */ + public static OAEPParameterSpec constructOAEPParameters( + String encryptionAlgorithmURI, + String digestAlgorithmURI, + String mgfAlgorithmURI, + byte[] oaepParams + ) { + if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithmURI) + || XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) { + + String jceDigestAlgorithm = "SHA-1"; + if (digestAlgorithmURI != null) { + jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgorithmURI); + } + + PSource.PSpecified pSource = oaepParams == null ? + PSource.PSpecified.DEFAULT : new PSource.PSpecified(oaepParams); + + MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1"); + if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithmURI)) { + mgfParameterSpec = constructMGF1Parameter(mgfAlgorithmURI); + } + return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource); + } + return null; + } + + /** + * Create MGF1ParameterSpec for the given algorithm URI + * + * @param mgh1AlgorithmURI the algorithm URI. If null or empty, SHA-1 is used as default MGF1 digest algorithm. + * @return the MGF1ParameterSpec for the given algorithm URI + */ + public static MGF1ParameterSpec constructMGF1Parameter(String mgh1AlgorithmURI) { + LOG.log(Level.DEBUG, "Creating MGF1ParameterSpec for [{0}]", mgh1AlgorithmURI); + if (mgh1AlgorithmURI == null || mgh1AlgorithmURI.isEmpty()) { + LOG.log(Level.WARNING,"MGF1 algorithm URI is null or empty. Using SHA-1 as default."); + return new MGF1ParameterSpec("SHA-1"); + } + + switch (mgh1AlgorithmURI) { + case EncryptionConstants.MGF1_SHA1: + return new MGF1ParameterSpec("SHA-1"); + case EncryptionConstants.MGF1_SHA224: + return new MGF1ParameterSpec("SHA-224"); + case EncryptionConstants.MGF1_SHA256: + return new MGF1ParameterSpec("SHA-256"); + case EncryptionConstants.MGF1_SHA384: + return new MGF1ParameterSpec("SHA-384"); + case EncryptionConstants.MGF1_SHA512: + return new MGF1ParameterSpec("SHA-512"); + default: + LOG.log(Level.WARNING, "Unsupported MGF algorithm: [{0}] Using SHA-1 as default.", mgh1AlgorithmURI); + return new MGF1ParameterSpec("SHA-1"); + } + } + + /** + * Get the MGF1 algorithm URI for the given MGF1ParameterSpec + * + * @param parameterSpec the MGF1ParameterSpec + * @return the MGF1 algorithm URI for the given MGF1ParameterSpec + */ + public static String getMgf1URIForParameter(MGF1ParameterSpec parameterSpec) { + String digestAlgorithm = parameterSpec.getDigestAlgorithm(); + LOG.log(Level.DEBUG, "Get MGF1 URI for digest algorithm [{0}]", digestAlgorithm); + switch (digestAlgorithm) { + case "SHA-1": + return EncryptionConstants.MGF1_SHA1; + case "SHA-224": + return EncryptionConstants.MGF1_SHA224; + case "SHA-256": + return EncryptionConstants.MGF1_SHA256; + case "SHA-384": + return EncryptionConstants.MGF1_SHA384; + case "SHA-512": + return EncryptionConstants.MGF1_SHA512; + default: + LOG.log(Level.WARNING, "Unknown hash algorithm: [{0}] for MGF1", digestAlgorithm); + return EncryptionConstants.MGF1_SHA1; + } + } + + + /** + * Construct an KeyAgreementParameterSpec object from the given parameters + * + * @param keyWrapAlgoURI key wrap algorithm + * @param agreementMethod agreement method + * @param keyAgreementPrivateKey private key to derive the shared secret in case of Diffie-Hellman key agreements + */ + public static KeyAgreementParameters constructRecipientKeyAgreementParameters(String keyWrapAlgoURI, + AgreementMethod agreementMethod, + PrivateKey keyAgreementPrivateKey + ) throws XMLSecurityException { + String agreementAlgorithmURI = agreementMethod.getAlgorithm(); + int keyLength = KeyUtils.getAESKeyBitSizeForWrapAlgorithm(keyWrapAlgoURI); + + KeyDerivationMethod keyDerivationMethod = agreementMethod.getKeyDerivationMethod(); + if (keyDerivationMethod == null) { + throw new XMLEncryptionException("Key Derivation Algorithm is not specified"); + } + KeyDerivationParameters kdp = constructKeyDerivationParameter(keyDerivationMethod, keyLength); + + return constructAgreementParameters( + agreementAlgorithmURI, KeyAgreementParameters.ActorType.RECIPIENT, kdp, + keyAgreementPrivateKey, agreementMethod.getOriginatorKeyInfo().getPublicKey()); + } + + /** + * Construct an KeyAgreementParameterSpec object from the given parameters + * + * @param agreementAlgorithmURI agreement algorithm + * @param keyDerivationParameter key derivation parameters (e.g. ConcatKDFParams for ConcatKDF key derivation) + * @param keyAgreementPrivateKey private key to derive the shared secret in case of Diffie-Hellman key agreements + * @param keyAgreementPublicKey public key to derive the shared secret in case of Diffie-Hellman key agreements + */ + public static KeyAgreementParameters constructAgreementParameters(String agreementAlgorithmURI, + KeyAgreementParameters.ActorType actorType, + KeyDerivationParameters keyDerivationParameter, + PrivateKey keyAgreementPrivateKey, + PublicKey keyAgreementPublicKey) { + KeyAgreementParameters ecdhKeyAgreementParameters = new KeyAgreementParameters( + actorType, + agreementAlgorithmURI, keyDerivationParameter); + if (actorType == KeyAgreementParameters.ActorType.RECIPIENT ) { + ecdhKeyAgreementParameters.setRecipientPrivateKey(keyAgreementPrivateKey); + ecdhKeyAgreementParameters.setOriginatorPublicKey(keyAgreementPublicKey); + } else { + ecdhKeyAgreementParameters.setOriginatorPrivateKey(keyAgreementPrivateKey); + ecdhKeyAgreementParameters.setRecipientPublicKey(keyAgreementPublicKey); + } + + return ecdhKeyAgreementParameters; + } + + /** + * Construct a KeyDerivationParameter object from the given keyDerivationMethod and keyBitLength + * + * @param keyDerivationMethod element to parse + * @param keyBitLength expected derived key length + * @return KeyDerivationParameter object + * @throws XMLSecurityException if the keyDerivationMethod is not supported + */ + public static KeyDerivationParameters constructKeyDerivationParameter(KeyDerivationMethod keyDerivationMethod, int keyBitLength) throws XMLSecurityException { + String keyDerivationAlgorithm = keyDerivationMethod.getAlgorithm(); + if (!EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(keyDerivationAlgorithm)) { + throw new XMLEncryptionException("unknownAlgorithm", keyDerivationAlgorithm); + } + ConcatKDFParamsImpl concatKDFParams = ((KeyDerivationMethodImpl) keyDerivationMethod).getConcatKDFParams(); + + return constructConcatKeyDerivationParameter(keyBitLength, concatKDFParams.getDigestMethod(), concatKDFParams.getAlgorithmId(), + concatKDFParams.getPartyUInfo(), concatKDFParams.getPartyVInfo(), + concatKDFParams.getSuppPubInfo(),concatKDFParams.getSuppPrivInfo()); + + } + + + /** + * Construct a ConcatKeyDerivationParameter object from the key length and digest method. + * + * @param keyBitLength expected derived key length + * @param digestMethod digest method + * @return ConcatKeyDerivationParameter object + */ + public static ConcatKDFParams constructConcatKeyDerivationParameter(int keyBitLength, + String digestMethod){ + return constructConcatKeyDerivationParameter(keyBitLength, digestMethod, null, null, null, null, null); + } + + /** + * Construct a ConcatKeyDerivationParameter object from the given parameters + * + * @param keyBitLength expected derived key length + * @param digestMethod digest method + * @param algorithmId algorithm id + * @param partyUInfo partyUInfo + * @param partyVInfo partyVInfo + * @param suppPubInfo suppPubInfo + * @param suppPrivInfo suppPrivInfo + * @return ConcatKeyDerivationParameter object + */ + public static ConcatKDFParams constructConcatKeyDerivationParameter(int keyBitLength, + String digestMethod, + String algorithmId, + String partyUInfo, + String partyVInfo, + String suppPubInfo, + String suppPrivInfo) { + + ConcatKDFParams kdp = new ConcatKDFParams(keyBitLength, digestMethod); + kdp.setAlgorithmID(algorithmId); + kdp.setPartyUInfo(partyUInfo); + kdp.setPartyVInfo(partyVInfo); + kdp.setSuppPubInfo(suppPubInfo); + kdp.setSuppPrivInfo(suppPrivInfo); + return kdp; + } } diff --git a/src/main/java/org/apache/xml/security/encryption/keys/KeyInfoEnc.java b/src/main/java/org/apache/xml/security/encryption/keys/KeyInfoEnc.java new file mode 100644 index 000000000..bb0d81f28 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/KeyInfoEnc.java @@ -0,0 +1,112 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys; + +import org.apache.xml.security.encryption.AgreementMethod; +import org.apache.xml.security.encryption.keys.content.AgreementMethodImpl; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.keys.KeyInfo; +import org.apache.xml.security.utils.ElementProxy; +import org.apache.xml.security.utils.EncryptionConstants; +import org.apache.xml.security.utils.I18n; +import org.apache.xml.security.utils.XMLUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + +/** + * This class is the extension of the {@link org.apache.xml.security.keys.KeyInfo} class. + * The {@link org.apache.xml.security.keys.KeyInfo} implements XML structures defined in XML Signature standards, + * and this class extends it for handling XML Element types defined by the XML encryption standards, + * such as AgreementMethod. + */ +public class KeyInfoEnc extends KeyInfo { + + private static final Logger LOG = System.getLogger(KeyInfoEnc.class.getName()); + + /** + * @see KeyInfo + */ + public KeyInfoEnc(Document doc) { + super(doc); + } + + /** + * @see KeyInfo + */ + public KeyInfoEnc(Element element, String baseURI) throws XMLSecurityException { + super(element, baseURI); + } + + /** + * Method add AgreementMethod to the KeyInfo + * + * @param agreementMethod the AgreementMethod to be added. The AgreementMethod must extend + * class {@link ElementProxy} + */ + public void add(AgreementMethod agreementMethod) { + + if (agreementMethod instanceof ElementProxy) { + LOG.log(Level.DEBUG, "Adding agreementMethod with algorithm {0}", agreementMethod.getAlgorithm()); + appendSelf((ElementProxy) agreementMethod); + addReturnToSelf(); + } else { + Object[] exArgs = {EncryptionConstants._TAG_AGREEMENTMETHOD, agreementMethod.getClass().getName()}; + throw new IllegalArgumentException(I18n.translate("KeyValue.IllegalArgument", exArgs)); + } + } + + /** + * Method lengthAgreementMethod + * + * @return the number of the AgreementMethod tags + */ + public int lengthAgreementMethod() { + return this.length(EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_AGREEMENTMETHOD); + } + + /** + * Method itemAgreementMethod + * + * @param i index of the AgreementMethod element + * @return the i(th) AgreementMethod proxy element or null if the index is too big + * @throws XMLSecurityException if the element with AgreementMethod exists but with wrong namespace + */ + public AgreementMethod itemAgreementMethod(int i) throws XMLSecurityException { + Element e = XMLUtils.selectXencNode( + getFirstChild(), EncryptionConstants._TAG_AGREEMENTMETHOD, i); + + if (e == null) { + LOG.log(Level.WARNING, "No AgreementMethod element at position [{0}]", i); + return null; + } + return new AgreementMethodImpl(e); + } + + /** + * Method containsAgreementMethod returns true if the KeyInfo contains a AgreementMethod node + * + * @return true if the KeyInfo contains a AgreementMethod node else false + */ + public boolean containsAgreementMethod() { + return this.lengthAgreementMethod() > 0; + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/keys/OriginatorKeyInfo.java b/src/main/java/org/apache/xml/security/encryption/keys/OriginatorKeyInfo.java new file mode 100644 index 000000000..73ce89bc2 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/OriginatorKeyInfo.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys; + +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.utils.EncryptionConstants; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + + +/** + * The OriginatorKeyInfo element is used in the ds:KeyInfo with AgreementMethod elements. + * @see KeyAgreement. The OriginatorKeyInfo + * element extends ds:KeyInfo with namespace xenc (http://www.w3.org/2001/04/xmlenc#) and is used to convey + * the originator's public key. + */ +public class OriginatorKeyInfo extends KeyInfoEnc { + + public OriginatorKeyInfo(Document doc) { + super(doc); + } + + public OriginatorKeyInfo(Element element, String baseURI) throws XMLSecurityException { + super(element, baseURI); + } + + + /** + * {@inheritDoc} + */ + @Override + public String getBaseNamespace() { + return EncryptionConstants.EncryptionSpecNS; + } + + /** + * {@inheritDoc} + */ + @Override + public String getBaseLocalName() { + return EncryptionConstants._TAG_ORIGINATORKEYINFO; + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/keys/RecipientKeyInfo.java b/src/main/java/org/apache/xml/security/encryption/keys/RecipientKeyInfo.java new file mode 100644 index 000000000..37bf17d1f --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/RecipientKeyInfo.java @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys; + +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.utils.EncryptionConstants; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * The RecipientKeyInfo element is used in the ds:KeyInfo with AgreementMethod elements. + * @see KeyAgreement. The RecipientKeyInfo + * element extends ds:KeyInfo with namespace xenc (http://www.w3.org/2001/04/xmlenc#) and is used to describe + * the recipient's key. + */ +public class RecipientKeyInfo extends KeyInfoEnc { + + public RecipientKeyInfo(Document doc) { + super(doc); + } + + public RecipientKeyInfo(Element element, String baseURI) throws XMLSecurityException { + super(element, baseURI); + } + + /** + * {@inheritDoc} + */ + @Override + public String getBaseNamespace() { + return EncryptionConstants.EncryptionSpecNS; + } + + /** + * {@inheritDoc} + */ + @Override + public String getBaseLocalName() { + return EncryptionConstants._TAG_RECIPIENTKEYINFO; + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/keys/content/AgreementMethodImpl.java b/src/main/java/org/apache/xml/security/encryption/keys/content/AgreementMethodImpl.java new file mode 100644 index 000000000..30bf29eb4 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/content/AgreementMethodImpl.java @@ -0,0 +1,326 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys.content; + +import org.apache.xml.security.encryption.AgreementMethod; +import org.apache.xml.security.encryption.KeyDerivationMethod; +import org.apache.xml.security.encryption.XMLEncryptionException; +import org.apache.xml.security.encryption.params.ConcatKDFParams; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; +import org.apache.xml.security.encryption.params.KeyDerivationParameters; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.encryption.keys.OriginatorKeyInfo; +import org.apache.xml.security.encryption.keys.RecipientKeyInfo; +import org.apache.xml.security.keys.content.KeyInfoContent; +import org.apache.xml.security.encryption.keys.content.derivedKey.ConcatKDFParamsImpl; +import org.apache.xml.security.encryption.keys.content.derivedKey.KeyDerivationMethodImpl; +import org.apache.xml.security.utils.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.PublicKey; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + + +/** + * The implementation of the AgreementMethod interface. The element contains a information about + * the key agreement algorithm for deriving the encryption key. + * + */ +public class AgreementMethodImpl extends EncryptionElementProxy implements KeyInfoContent, AgreementMethod { + protected static final Logger LOG = System.getLogger(AgreementMethodImpl.class.getName()); + + private byte[] kaNonce; + private List agreementMethodInformation; + private KeyDerivationMethod keyDerivationMethod; + private OriginatorKeyInfo originatorKeyInfo; + private RecipientKeyInfo recipientKeyInfo; + private String algorithmURI; + + /** + * Constructor AgreementMethodImpl for generating AgreementMethod from scratch based on {@link KeyAgreementParameters}. + * The constructor generates {@link KeyDerivationMethod} if given and {@link OriginatorKeyInfo} based on originator + * public key for ECDH-ES key agreement. It generates a placeholder element for RecipientKeyInfo. The recipient key info value + * must be set later. + * + * @param doc the {@link Document} in which AgreementMethod will be placed + * @param keyAgreementParameter the {@link KeyAgreementParameters} from which AgreementMethod will be generated + * @throws XMLEncryptionException if the Key derivation algorithm is not supported or invalid parameters are given. + */ + public AgreementMethodImpl(Document doc, KeyAgreementParameters keyAgreementParameter) throws XMLEncryptionException { + this(doc, keyAgreementParameter.getKeyAgreementAlgorithm()); + + if (keyAgreementParameter.getKeyDerivationParameter() != null) { + KeyDerivationMethod keyDerivationMethod = createKeyDerivationMethod(keyAgreementParameter); + setKeyDerivationMethod(keyDerivationMethod); + } + // if ephemeral static key agreement then add originator public key automatically + if (EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES.equals(keyAgreementParameter.getKeyAgreementAlgorithm())) { + setOriginatorPublicKey(keyAgreementParameter.getOriginatorPublicKey()); + } + // set recipient key info holder + RecipientKeyInfo recipientKeyInfo = new RecipientKeyInfo(getDocument()); + setRecipientKeyInfo(recipientKeyInfo); + } + + /** + * Constructor AgreementMethodImpl for generating AgreementMethod from scratch based on algorithm URI. The constructor + * builds a placeholder element for {@link KeyDerivationMethod}, {@link OriginatorKeyInfo} and {@link RecipientKeyInfo}. The values for these elements + * must be set later. + * + * @param algorithm the algorithm URI for the key agreement algorithm + */ + public AgreementMethodImpl(Document doc, String algorithm) { + super(doc); + + agreementMethodInformation = new LinkedList<>(); + URI tmpAlgorithm; + try { + tmpAlgorithm = new URI(algorithm); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException("Algorithm [" + algorithm + "] is not URI ", ex); + } + algorithmURI = tmpAlgorithm.toString(); + + setLocalAttribute(Constants._ATT_ALGORITHM, algorithmURI); + } + + /** + * Constructor AgreementMethodImpl based on XML {@link Element}. + * + * @param element the XML {@link Element} containing AgreementMethod information + * @throws XMLSecurityException if the AgreementMethod element has invalid XML structure + */ + public AgreementMethodImpl(Element element) throws XMLSecurityException { + super(element, EncryptionConstants.EncryptionSpecNS); + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] getKANonce() { + return kaNonce; + } + + /** + * {@inheritDoc} + */ + @Override + public void setKANonce(byte[] kanonce) { + kaNonce = kanonce; + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator getAgreementMethodInformation() { + return agreementMethodInformation.iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addAgreementMethodInformation(Element info) { + agreementMethodInformation.add(info); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeAgreementMethodInformation(Element info) { + agreementMethodInformation.remove(info); + } + + /** + * {@inheritDoc} + */ + @Override + public KeyDerivationMethod getKeyDerivationMethod() throws XMLSecurityException { + + if (keyDerivationMethod != null) { + return keyDerivationMethod; + } + + Element keyDerivationMethodElement = + XMLUtils.selectXenc11Node(getElement().getFirstChild(), EncryptionConstants._TAG_KEYDERIVATIONMETHOD, 0); + + if (keyDerivationMethodElement == null) { + return null; + } + keyDerivationMethod = new KeyDerivationMethodImpl(keyDerivationMethodElement, baseURI); + + + return keyDerivationMethod; + } + + /** + * {@inheritDoc} + */ + @Override + public void setKeyDerivationMethod(KeyDerivationMethod keyDerivationMethod) { + this.keyDerivationMethod = keyDerivationMethod; + if (keyDerivationMethod instanceof ElementProxy) { + appendSelf((ElementProxy) keyDerivationMethod); + addReturnToSelf(); + } else { + LOG.log(Level.WARNING, "KeyDerivationMethod [{0}] is set but is not an instance of ElementProxy. " + + "The DOM node is lost upon serialization.", keyDerivationMethod); + } + } + + /** + * {@inheritDoc} + */ + @Override + public OriginatorKeyInfo getOriginatorKeyInfo() throws XMLSecurityException { + if (originatorKeyInfo != null) { + return originatorKeyInfo; + } + + Element originatorKeyInfoElement = + XMLUtils.selectXencNode(getElement().getFirstChild(), EncryptionConstants._TAG_ORIGINATORKEYINFO, 0); + + if (originatorKeyInfoElement == null) { + return null; + } + originatorKeyInfo = new OriginatorKeyInfo(originatorKeyInfoElement, baseURI); + + return originatorKeyInfo; + } + + /** + * {@inheritDoc} + */ + @Override + public void setOriginatorKeyInfo(OriginatorKeyInfo keyInfo) { + originatorKeyInfo = keyInfo; + appendSelf(keyInfo); + addReturnToSelf(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setOriginatorPublicKey(PublicKey publicKey) { + + OriginatorKeyInfo originatorKeyInfo = new OriginatorKeyInfo(getDocument()); + originatorKeyInfo.add(publicKey); + setOriginatorKeyInfo(originatorKeyInfo); + } + + /** + * {@inheritDoc} + */ + @Override + public RecipientKeyInfo getRecipientKeyInfo() throws XMLSecurityException { + if (recipientKeyInfo != null) { + return recipientKeyInfo; + } + + Element recipientKeyInfoElement = + XMLUtils.selectXencNode(getElement().getFirstChild(), EncryptionConstants._TAG_RECIPIENTKEYINFO, 0); + + if (recipientKeyInfoElement == null) { + return null; + } + recipientKeyInfo = new RecipientKeyInfo(recipientKeyInfoElement, baseURI); + + return recipientKeyInfo; + } + + /** + * {@inheritDoc} + */ + @Override + public void setRecipientKeyInfo(RecipientKeyInfo keyInfo) { + + recipientKeyInfo = keyInfo; + appendSelf(keyInfo); + addReturnToSelf(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getAlgorithm() { + if (algorithmURI == null) { + algorithmURI = getLocalAttribute(Constants._ATT_ALGORITHM); + } + return algorithmURI; + } + + @Override + public String getBaseLocalName() { + return EncryptionConstants._TAG_AGREEMENTMETHOD; + } + + /** + * Method createKeyDerivationMethod creates a {@link KeyDerivationMethod} based on {@link KeyAgreementParameters}. + * The method supports only {@link ConcatKDFParams} for now. + * @see ConcatKDF + * + * @param keyAgreementParameter the {@link KeyAgreementParameters} from which {@link KeyDerivationMethod} will be generated. + * @return the {@link KeyDerivationMethod} based on {@link KeyAgreementParameters} + * @throws XMLEncryptionException if Key derivation algorithm is not supported + */ + private KeyDerivationMethod createKeyDerivationMethod(KeyAgreementParameters keyAgreementParameter) throws XMLEncryptionException { + KeyDerivationParameters kdfParameters = keyAgreementParameter.getKeyDerivationParameter(); + + KeyDerivationMethodImpl keyDerivationMethod = new KeyDerivationMethodImpl(getDocument()); + keyDerivationMethod.setAlgorithm(kdfParameters.getAlgorithm()); + + if (kdfParameters instanceof ConcatKDFParams + && EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(kdfParameters.getAlgorithm())) { + ConcatKDFParamsImpl concatKDFParams = getConcatKDFParams((ConcatKDFParams)kdfParameters); + keyDerivationMethod.setConcatKDFParams(concatKDFParams); + } else { + throw new XMLEncryptionException("Unsupported Key Derivation Algorithm"); + } + return keyDerivationMethod; + } + + /** + * Method getConcatKDFParams creates a {@link ConcatKDFParamsImpl} based on {@link ConcatKDFParams}. + * @param kdfParameters the {@link ConcatKDFParams} from which {@link ConcatKDFParamsImpl} will be generated. + * @return the {@link ConcatKDFParamsImpl} + */ + private ConcatKDFParamsImpl getConcatKDFParams(ConcatKDFParams kdfParameters) { + ConcatKDFParamsImpl concatKDFParams = new ConcatKDFParamsImpl(getDocument()); + concatKDFParams.setDigestMethod(kdfParameters.getDigestAlgorithm()); + // set parameters + concatKDFParams.setAlgorithmId(kdfParameters.getAlgorithmID()); + concatKDFParams.setPartyUInfo(kdfParameters.getPartyUInfo()); + concatKDFParams.setPartyVInfo(kdfParameters.getPartyVInfo()); + concatKDFParams.setSuppPubInfo(kdfParameters.getSuppPubInfo()); + concatKDFParams.setSuppPrivInfo(kdfParameters.getSuppPrivInfo()); + return concatKDFParams; + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/ConcatKDF.java b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/ConcatKDF.java new file mode 100644 index 000000000..cda807fd0 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/ConcatKDF.java @@ -0,0 +1,240 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys.content.derivedKey; + +import org.apache.xml.security.algorithms.MessageDigestAlgorithm; +import org.apache.xml.security.encryption.XMLEncryptionException; +import org.apache.xml.security.exceptions.XMLSecurityException; + +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; + +/** + * Key DerivationAlgorithm implementation, defined in Section 5.8.1 of NIST SP 800-56A [SP800-56A], and is equivalent + * to the KDF3 function defined in ANSI X9.44-2007 [ANSI-X9-44-2007] when the contents of the OtherInfo parameter + * is structured as in NIST SP 800-56A. + *

+ * Identifier of the key derivation algorithm: http://www.w3.org/2009/xmlenc11#ConcatKDF + */ +public class ConcatKDF implements DerivationAlgorithm { + + private static final System.Logger LOG = System.getLogger(ConcatKDF.class.getName()); + private final String algorithmURI; + + /** + * Constructor ConcatKDF with digest algorithmURI parameter such as http://www.w3.org/2001/04/xmlenc#sha256, + * http://www.w3.org/2001/04/xmlenc#sha512, etc. + */ + public ConcatKDF(String algorithmURI) { + this.algorithmURI = algorithmURI; + } + + /** + * Default Constructor which sets the default digest algorithmURI parameter: http://www.w3.org/2001/04/xmlenc#sha256, + */ + public ConcatKDF() { + this(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256); + } + + /** + * Key DerivationAlgorithm implementation as defined in Section 5.8.1 of NIST SP 800-56A [SP800-56A] + *

    + *
  • reps = ⎡ keydatalen / hashlen⎤.
  • + *
  • If reps > (2>32 −1), then ABORT: output an error indicator and stop.
  • + *
  • Initialize a 32-bit, big-endian bit string counter as 0000000116.
  • + *
  • If counter || Z || OtherInfo is more than max_hash_inputlen bits long, then ABORT: output an error indicator and stop. + *
  • For i = 1 to reps by 1, do the following:
      + *
    • Compute Hashi = H(counter || Z || OtherInfo).
    • + *
    • Increment counter (modulo 232), treating it as an unsigned 32-bit integer.
    • + *
  • + *
  • Let Hhash be set to Hashreps if (keydatalen / hashlen) is an integer; otherwise, let Hhash be set to the + * (keydatalen mod hashlen) leftmost bits of Hashreps.
  • + *
  • Set DerivedKeyingMaterial = Hash1 || Hash2 || ... || Hashreps-1 || Hhash
  • + *
+ * + * @param secret The "shared" secret to use for key derivation (e.g. the secret key) + * @param otherInfo as specified in [SP800-56A] the optional attributes: AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo and SuppPrivInfo attributes are concatenated to form a bit string “OtherInfo” that is used with the key derivation function. + * @param offset the offset parameter is ignored by this implementation. + * @param keyLength The length of the key to derive + * @return The derived key + * @throws XMLEncryptionException if the key length is too long to be derived with the given algorithm + */ + @Override + public byte[] deriveKey(byte[] secret, byte[] otherInfo, int offset, long keyLength) throws XMLSecurityException { + + MessageDigest digest = MessageDigestAlgorithm.getDigestInstance(algorithmURI); + + long genKeyLength = offset+keyLength; + + int iDigestLength = digest.getDigestLength(); + if (genKeyLength / iDigestLength > (long) Integer.MAX_VALUE) { + LOG.log(Level.ERROR, "Key size is to long to be derived with hash algorithm [{0}]", algorithmURI); + throw new XMLEncryptionException("errorInKeyDerivation"); + } + int toGenerateSize = (int) genKeyLength; + + digest.reset(); + ByteBuffer indexBuffer = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN); + + ByteBuffer result = ByteBuffer.allocate(toGenerateSize); + + int counter = 1; + while (toGenerateSize > 0) { + indexBuffer.position(0); + indexBuffer.putInt(counter++); + indexBuffer.position(0); + digest.update(indexBuffer); + digest.update(secret); + if (otherInfo != null && otherInfo.length > 0) { + digest.update(otherInfo); + } + result.put(digest.digest(), 0, Math.min(toGenerateSize, iDigestLength)); + toGenerateSize -= iDigestLength; + } + if (offset > 0) { + result.position(offset); + return result.slice().array(); + } + return result.array(); + } + + /** + * Method concatenate the bitstrings in following order {@code algID || partyUInfo || partyVInfo || suppPubInfo || suppPrivInfo}. + * to crate otherInfo as key derivation function input. + * If named parameters are null the value is ignored. + * Method parses the bitstring firs {{@code @See} https://www.w3.org/TR/xmlenc-core1/#sec-ConcatKDF} and then concatenates them to a byte array. + * + * @param sharedSecret The "shared" secret to use for key derivation (e.g. the secret key) + * @param algID A bit string that indicates how the derived keying material will be parsed and for which + * algorithm(s) the derived secret keying material will be used. + * @param partyUInfo A bit string containing public information that is required by the + * application using this KDF to be contributed by party U to the key derivation + * process. At a minimum, PartyUInfo shall include IDU, the identifier of party U. See + * the notes below.. + * @param partyVInfo A bit string containing public information that is required by the + * application using this KDF to be contributed by party V to the key derivation + * process. At a minimum, PartyVInfo shall include IDV, the identifier of party V. See + * the notes below. + * @param suppPubInfo bit string containing additional, mutually-known public information. + * @param suppPrivInfo The suppPrivInfo A bit string containing additional, mutually-known public Information. + * @param keyLength The length of the key to derive + * @return The resulting other info. + */ + public byte[] deriveKey(final byte[] sharedSecret, + final String algID, + final String partyUInfo, + final String partyVInfo, + final String suppPubInfo, + final String suppPrivInfo, + final long keyLength) + throws XMLSecurityException { + + final byte[] otherInfo = concatParameters(algID, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo); + + return deriveKey(sharedSecret, otherInfo, keyLength); + } + + + /** + * Simple method to concatenate non-padded bitstream ConcatKDF parameters. + * If parameters are null the value is ignored. + * + * @param parameters the parameters to concatenate + * @return the concatenated parameters as byte array + */ + private static byte[] concatParameters(final String... parameters) throws XMLEncryptionException { + + List byteParams = new ArrayList<>(); + for (String parameter : parameters) { + byte[] bytes = parseBitString(parameter); + byteParams.add(bytes); + } + // get bytearrays size + int iSize = byteParams.stream().map(ConcatKDF::getSize).reduce(0, Integer::sum); + + ByteBuffer buffer = ByteBuffer + .allocate(iSize); + byteParams.forEach(buffer::put); + return buffer.array(); + } + + /** + * The method validates the bitstring parameter structure and returns byte array of the parameter. + *

+ * The bitstring is divided into octets using big-endian encoding. Parameter starts with two characters (hex number) + * defining the number of padding bits followed by hex-string. The length of the bitstring is not a multiple of 8 + * then add padding bits (value 0) as necessary to the last octet to make it a multiple of 8. + * + * @param kdfParameter the parameter to parse + * @return the parsed parameter as byte array + */ + private static byte[] parseBitString(final String kdfParameter) throws XMLEncryptionException { + // ignore empty parameters + if (kdfParameter == null || kdfParameter.isEmpty()) { + return new byte[0]; + } + String kdfP = kdfParameter.trim(); + int paramLen = kdfP.length(); + // bit string must have two chars following by first byte defining the number of padding bits + if (paramLen < 4) { + LOG.log(Level.ERROR, "ConcatKDF parameter is to short"); + throw new XMLEncryptionException( "KeyDerivation.TooShortParameter", kdfParameter); + } + if (paramLen % 2 != 0) { + LOG.log(Level.ERROR, "Invalid length of ConcatKDF parameter [{0}]!", kdfP); + throw new XMLEncryptionException( "KeyDerivation.InvalidParameter", kdfParameter); + } + int iPadding; + String strPadding = kdfP.substring(0, 2); + try { + iPadding = Integer.parseInt(strPadding, 16); + } catch (NumberFormatException e) { + LOG.log(Level.ERROR, "Invalid padding number: [{0}]! Number is not Hexadecimal!", strPadding); + throw new XMLEncryptionException(e, "KeyDerivation.InvalidParameter", new Object[]{kdfParameter}); + } + + if (iPadding != 0) { + LOG.log(Level.ERROR, "Padded ConcatKDF parameters are not supported"); + throw new XMLEncryptionException( "KeyDerivation.NotSupportedParameter", kdfParameter); + } + // skip first two chars + kdfP = kdfP.substring(2); + paramLen = kdfP.length(); + byte[] data = new byte[paramLen / 2]; + + for (int i = 0; i < paramLen; i += 2) { + data[i / 2] = (byte) ((Character.digit(kdfP.charAt(i), 16) << 4) + + Character.digit(kdfP.charAt(i + 1), 16)); + } + return data; + } + + /** + * Method returns the size of the array or 0 if the array is null. + * @param array the array to get the size from + * @return the size of the array or 0 if the array is null. + */ + private static int getSize(byte[] array) { + return array == null ? 0 : array.length; + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/ConcatKDFParamsImpl.java b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/ConcatKDFParamsImpl.java new file mode 100644 index 000000000..fcc7b4e15 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/ConcatKDFParamsImpl.java @@ -0,0 +1,163 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys.content.derivedKey; + +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.utils.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.StringJoiner; + +/** + * Class ConcatKDFParamsImpl is an DOM representation of the ConcatKDFParams. + */ +public class ConcatKDFParamsImpl extends Encryption11ElementProxy { + + + /** + * Constructor ConcatKDFParamsImpl creates a new ConcatKDFParamsImpl instance. + * + * @param doc the Document in which to create the DOM tree + */ + public ConcatKDFParamsImpl(Document doc) { + super(doc); + } + + /** + * Constructor ConcatKDFParamsImpl from existing XML element + * @param element the element to use as source + * @param baseURI the URI of the resource where the XML instance was stored + * @throws XMLSecurityException + */ + public ConcatKDFParamsImpl(Element element, String baseURI) throws XMLSecurityException { + super(element, baseURI); + } + + /** + * Sets the Algorithm attribute + * + * @param algorithm ID + */ + public void setAlgorithmId(String algorithm) { + if (algorithm != null) { + setLocalAttribute(EncryptionConstants._ATT_ALGORITHM_ID, algorithm); + } + } + + public String getAlgorithmId() { + return getLocalAttribute(EncryptionConstants._ATT_ALGORITHM_ID); + } + + /** + * Sets the PartyUInfo attribute + * + * @param partyUInfo + */ + public void setPartyUInfo(String partyUInfo) { + if (partyUInfo != null) { + setLocalAttribute(EncryptionConstants._ATT_PARTYUINFO, partyUInfo); + } + } + + public String getPartyUInfo() { + return getLocalAttribute(EncryptionConstants._ATT_PARTYUINFO); + } + + /** + * Sets the PartyVInfo attribute + * + * @param partyVInfo + */ + public void setPartyVInfo(String partyVInfo) { + if (partyVInfo != null) { + setLocalAttribute(EncryptionConstants._ATT_PARTYVINFO, partyVInfo); + } + } + + public String getPartyVInfo() { + return getLocalAttribute(EncryptionConstants._ATT_PARTYVINFO); + } + + /** + * Sets the SuppPubInfo attribute + * + * @param suppPubInfo + */ + public void setSuppPubInfo(String suppPubInfo) { + if (suppPubInfo != null) { + setLocalAttribute(EncryptionConstants._ATT_SUPPPUBINFO, suppPubInfo); + } + } + + public String getSuppPubInfo() { + return getLocalAttribute(EncryptionConstants._ATT_SUPPPUBINFO); + } + + /** + * Sets the SuppPrivInfo attribute + * + * @param suppPrivInfo + */ + + public void setSuppPrivInfo(String suppPrivInfo) { + if (suppPrivInfo != null) { + setLocalAttribute(EncryptionConstants._ATT_SUPPPRIVINFO, suppPrivInfo); + } + } + + public String getSuppPrivInfo() { + return getLocalAttribute(EncryptionConstants._ATT_SUPPPRIVINFO); + } + + public void setDigestMethod(String digestMethod) { + if (digestMethod != null) { + Element digestElement = + XMLUtils.createElementInSignatureSpace(getDocument(), Constants._TAG_DIGESTMETHOD); + digestElement.setAttributeNS(null, "Algorithm", digestMethod); + digestElement.setAttributeNS( + Constants.NamespaceSpecNS, + "xmlns:" + ElementProxy.getDefaultPrefix(Constants.SignatureSpecNS), + Constants.SignatureSpecNS + ); + appendSelf(digestElement); + } + } + + public String getDigestMethod() { + Element digestElement = + XMLUtils.selectDsNode(getElement().getFirstChild(), Constants._TAG_DIGESTMETHOD, 0); + if (digestElement != null) { + return digestElement.getAttributeNS(null, "Algorithm"); + } + return null; + } + + @Override + public String getBaseLocalName() { + return EncryptionConstants._TAG_CONCATKDFPARAMS; + } + + @Override + public String toString() { + return new StringJoiner(", ", ConcatKDFParamsImpl.class.getSimpleName() + "[", "]") + .add("baseURI='" + baseURI + "'") + .toString(); + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/DerivationAlgorithm.java b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/DerivationAlgorithm.java new file mode 100644 index 000000000..3f24b4452 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/DerivationAlgorithm.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys.content.derivedKey; + +import org.apache.xml.security.exceptions.XMLSecurityException; + +/** + * Interface is supported by classes to implement key derivation algorithms. + */ +public interface DerivationAlgorithm { + + /** + * Derives a key from the given secret and other info. The initial derived key is size of + * offset + keyLength. + * + * @param secret The "shared" secret to use for key derivation (e.g. the secret key) + * @param otherInfo as specified in [SP800-56A] the optional attributes: AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo and SuppPrivInfo attributes are concatenated to form a bit string “OtherInfo” that is used with the key derivation function. + * @param offset the starting position in derived keying material of size: offset + keyLength + * @param keyLength The length of the key to derive + * @return The derived key + * @throws XMLSecurityException if something goes wrong during the key derivation + */ + byte[] deriveKey(byte[] secret, byte[] otherInfo, int offset, + long keyLength) throws XMLSecurityException; + + + /** + * Derives a key from the given secret and other info. + * @param secret The "shared" secret to use for key derivation (e.g. the secret key) + * @param otherInfo as specified in [SP800-56A] the optional attributes: AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo and SuppPrivInfo attributes are concatenated to form a bit string “OtherInfo” that is used with the key derivation function. + * @param keyLength The length of the key to derive + * @return The derived key + * @throws XMLSecurityException if something goes wrong during the key derivation + */ + default byte[] deriveKey(byte[] secret, byte[] otherInfo, + long keyLength) throws XMLSecurityException { + return deriveKey(secret, otherInfo, 0, keyLength); + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/KeyDerivationMethodImpl.java b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/KeyDerivationMethodImpl.java new file mode 100644 index 000000000..8f59e8db8 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/KeyDerivationMethodImpl.java @@ -0,0 +1,108 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.keys.content.derivedKey; + +import org.apache.xml.security.encryption.KeyDerivationMethod; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.utils.Encryption11ElementProxy; +import org.apache.xml.security.utils.EncryptionConstants; +import org.apache.xml.security.utils.XMLUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.StringJoiner; + +/** + * Class KeyDerivationMethodImpl is an DOM implementation of the KeyDerivationMethod + * + */ +public class KeyDerivationMethodImpl extends Encryption11ElementProxy implements KeyDerivationMethod { + private ConcatKDFParamsImpl concatKDFParams; + + /** + * Constructor KeyDerivationMethodImpl creates a new KeyDerivationMethodImpl instance. + * + * @param doc the Document in which to create the DOM tree + */ + public KeyDerivationMethodImpl(Document doc) { + super(doc); + } + + /** + * Constructor KeyDerivationMethodImpl from existing XML element + * + * @param element the element to use as source + * @param baseURI the URI of the resource where the XML instance was stored + * @throws XMLSecurityException if a parsing error occurs + */ + public KeyDerivationMethodImpl(Element element, String baseURI) throws XMLSecurityException { + super(element, baseURI); + } + + /** + * Sets the Algorithm attribute + * + * @param algorithm ID + */ + public void setAlgorithm(String algorithm) { + if (algorithm != null) { + setLocalIdAttribute(EncryptionConstants._ATT_ALGORITHM, algorithm); + } + } + + @Override + public String getAlgorithm() { + return getLocalAttribute(EncryptionConstants._ATT_ALGORITHM); + } + + public ConcatKDFParamsImpl getConcatKDFParams() throws XMLSecurityException { + + if (concatKDFParams != null) { + return concatKDFParams; + } + + Element concatKDFParamsElement = + XMLUtils.selectXenc11Node(getElement().getFirstChild(), EncryptionConstants._TAG_CONCATKDFPARAMS, 0); + + if (concatKDFParamsElement == null) { + return null; + } + concatKDFParams = new ConcatKDFParamsImpl(concatKDFParamsElement, getBaseURI()); + + return concatKDFParams; + } + + public void setConcatKDFParams(ConcatKDFParamsImpl concatKDFParams) { + this.concatKDFParams = concatKDFParams; + appendSelf(concatKDFParams); + addReturnToSelf(); + } + + @Override + public String getBaseLocalName() { + return EncryptionConstants._TAG_KEYDERIVATIONMETHOD; + } + + @Override + public String toString() { + return new StringJoiner(", ", KeyDerivationMethodImpl.class.getSimpleName() + "[", "]") + .add("concatKDFParams=" + concatKDFParams) + .toString(); + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/params/ConcatKDFParams.java b/src/main/java/org/apache/xml/security/encryption/params/ConcatKDFParams.java new file mode 100644 index 000000000..891c5ff57 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/params/ConcatKDFParams.java @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.params; + +import org.apache.xml.security.algorithms.MessageDigestAlgorithm; +import org.apache.xml.security.utils.EncryptionConstants; + +/** + * Class ConcatKeyDerivationParameter is used to specify parameters for the ConcatKDF key derivation algorithm. + * @see XML Encryption Syntax and Processing Version 1.1, 5.8.1 + * The ConcatKDF Key Derivation Algorithm + */ +public class ConcatKDFParams extends KeyDerivationParameters { + + private String digestAlgorithm; + private String algorithmID; + private String partyUInfo; + private String partyVInfo; + private String suppPubInfo; + private String suppPrivInfo; + + /** + * Constructor ConcatKeyDerivationParameter with specified digest algorithm + * + * @param keyBitLength the length of the derived key in bits + * @param digestAlgorithm the digest algorithm to use + */ + public ConcatKDFParams(int keyBitLength, String digestAlgorithm) { + super(EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF, keyBitLength); + this.digestAlgorithm = digestAlgorithm; + } + + /** + * Method return the digest algorithm. In case of algorithm is not set, the "default" + * algorithm SHA256 digest algorithm is returned. + * + * @return the digest algorithm + */ + public String getDigestAlgorithm() { + return digestAlgorithm == null ? MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256 : digestAlgorithm; + } + + public void setDigestAlgorithm(String digestAlgorithm) { + this.digestAlgorithm = digestAlgorithm; + } + + public String getAlgorithmID() { + return algorithmID; + } + + public void setAlgorithmID(String algorithmID) { + this.algorithmID = algorithmID; + } + + public String getPartyUInfo() { + return partyUInfo; + } + + public void setPartyUInfo(String partyUInfo) { + this.partyUInfo = partyUInfo; + } + + public String getPartyVInfo() { + return partyVInfo; + } + + public void setPartyVInfo(String partyVInfo) { + this.partyVInfo = partyVInfo; + } + + public String getSuppPubInfo() { + return suppPubInfo; + } + + public void setSuppPubInfo(String suppPubInfo) { + this.suppPubInfo = suppPubInfo; + } + + public String getSuppPrivInfo() { + return suppPrivInfo; + } + + public void setSuppPrivInfo(String suppPrivInfo) { + this.suppPrivInfo = suppPrivInfo; + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/params/KeyAgreementParameters.java b/src/main/java/org/apache/xml/security/encryption/params/KeyAgreementParameters.java new file mode 100644 index 000000000..70e9f4ac3 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/params/KeyAgreementParameters.java @@ -0,0 +1,126 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.params; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.AlgorithmParameterSpec; + +/** + * This class is used to pass parameters to the KeyAgreement algorithm. + */ +public class KeyAgreementParameters implements AlgorithmParameterSpec { + /** + * This enum defines the actor type of the KeyAgreement algorithm. The actor type defines which public and which + * private key is expected to be present for the KeyAgreement algorithm to define derived key. + *

    + *
  • ORIGINATOR: The key agreement is used by originator of the message. Therefore, the originator private key and + * the recipient public key are expected to be present.
  • + *
  • RECIPIENT: The key agreement is used by recipient of the message. Therefore, the recipient private key and + * the originator public key are expected to be present.
  • + *
+ */ + public enum ActorType { + ORIGINATOR, + RECIPIENT + } + + private final KeyDerivationParameters KeyDerivationParameter; + private final ActorType actorType; + private final String keyAgreementAlgorithm; + + private PublicKey originatorPublicKey; + private PrivateKey originatorPrivateKey; + private PublicKey recipientPublicKey; + private PrivateKey recipientPrivateKey; + + + public KeyAgreementParameters(ActorType actorType, String keyAgreementAlgorithm, KeyDerivationParameters keyDerivationParameter) { + this.actorType = actorType; + this.KeyDerivationParameter = keyDerivationParameter; + this.keyAgreementAlgorithm = keyAgreementAlgorithm; + } + + public KeyDerivationParameters getKeyDerivationParameter() { + return KeyDerivationParameter; + } + + public String getKeyAgreementAlgorithm() { + return keyAgreementAlgorithm; + } + + public void setOriginatorKeyPair(KeyPair originatorKeyPair) { + this.originatorPublicKey = originatorKeyPair.getPublic(); + this.originatorPrivateKey = originatorKeyPair.getPrivate(); + } + + public PublicKey getOriginatorPublicKey() { + return originatorPublicKey; + } + + public void setOriginatorPublicKey(PublicKey originatorPublicKey) { + this.originatorPublicKey = originatorPublicKey; + } + + public PrivateKey getOriginatorPrivateKey() { + return originatorPrivateKey; + } + + public void setOriginatorPrivateKey(PrivateKey originatorPrivateKey) { + if (actorType != ActorType.ORIGINATOR) { + throw new IllegalStateException("Cannot set originator private key when actor type is not ORIGINATOR"); + } + this.originatorPrivateKey = originatorPrivateKey; + } + + public PublicKey getRecipientPublicKey() { + return recipientPublicKey; + } + + public void setRecipientPublicKey(PublicKey recipientPublicKey) { + if (actorType != ActorType.ORIGINATOR) { + throw new IllegalStateException("Cannot set recipient public key when actor type is not ORIGINATOR"); + } + this.recipientPublicKey = recipientPublicKey; + } + + public PrivateKey getRecipientPrivateKey() { + return recipientPrivateKey; + } + + public void setRecipientPrivateKey(PrivateKey recipientPrivateKey) { + if (actorType != ActorType.RECIPIENT) { + throw new IllegalStateException("Cannot set recipient private key when actor type is not RECIPIENT"); + } + this.recipientPrivateKey = recipientPrivateKey; + } + + public ActorType getActorType() { + return actorType; + } + + public PublicKey getAgreementPublicKey() { + return actorType == ActorType.ORIGINATOR ? recipientPublicKey : originatorPublicKey; + } + + public PrivateKey getAgreementPrivateKey() { + return actorType == ActorType.ORIGINATOR ? originatorPrivateKey : recipientPrivateKey; + } +} diff --git a/src/main/java/org/apache/xml/security/encryption/params/KeyDerivationParameters.java b/src/main/java/org/apache/xml/security/encryption/params/KeyDerivationParameters.java new file mode 100644 index 000000000..e22dbff82 --- /dev/null +++ b/src/main/java/org/apache/xml/security/encryption/params/KeyDerivationParameters.java @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.encryption.params; + +/** + * Abstract key derivation class contains the basic parameters used for the key derivation. + * The class should be extended to provide algorithm specific parameters. + */ +public abstract class KeyDerivationParameters { + private final String algorithm; + private final int keyBitLength; + + public KeyDerivationParameters(String algorithm, int keyLength) { + this.algorithm = algorithm; + this.keyBitLength = keyLength; + } + + public String getAlgorithm() { + return algorithm; + } + + public int getKeyBitLength() { + return keyBitLength; + } +} diff --git a/src/main/java/org/apache/xml/security/exceptions/DERDecodingException.java b/src/main/java/org/apache/xml/security/exceptions/DERDecodingException.java new file mode 100644 index 000000000..1054664dd --- /dev/null +++ b/src/main/java/org/apache/xml/security/exceptions/DERDecodingException.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.exceptions; + +/** + * This Exception is thrown if decoding of ASN.1 (DER) data fails. + * + */ +public class DERDecodingException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Constructor DERDecodingException + * + * @param message the message to display when this exception is thrown + */ + public DERDecodingException(String message) { + super(message); + } + + /** + * Constructor DERDecodingException + * + * @param message the message to display when this exception is thrown + * @param cause the cause of this exception + */ + public DERDecodingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/apache/xml/security/keys/KeyInfo.java b/src/main/java/org/apache/xml/security/keys/KeyInfo.java index f30db6ea1..d1e90dec1 100644 --- a/src/main/java/org/apache/xml/security/keys/KeyInfo.java +++ b/src/main/java/org/apache/xml/security/keys/KeyInfo.java @@ -33,15 +33,7 @@ import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.encryption.XMLEncryptionException; import org.apache.xml.security.exceptions.XMLSecurityException; -import org.apache.xml.security.keys.content.DEREncodedKeyValue; -import org.apache.xml.security.keys.content.KeyInfoReference; -import org.apache.xml.security.keys.content.KeyName; -import org.apache.xml.security.keys.content.KeyValue; -import org.apache.xml.security.keys.content.MgmtData; -import org.apache.xml.security.keys.content.PGPData; -import org.apache.xml.security.keys.content.RetrievalMethod; -import org.apache.xml.security.keys.content.SPKIData; -import org.apache.xml.security.keys.content.X509Data; +import org.apache.xml.security.keys.content.*; import org.apache.xml.security.keys.content.keyvalues.DSAKeyValue; import org.apache.xml.security.keys.content.keyvalues.RSAKeyValue; import org.apache.xml.security.keys.keyresolver.KeyResolver; @@ -49,11 +41,7 @@ import org.apache.xml.security.keys.keyresolver.KeyResolverSpi; import org.apache.xml.security.keys.storage.StorageResolver; import org.apache.xml.security.transforms.Transforms; -import org.apache.xml.security.utils.Constants; -import org.apache.xml.security.utils.ElementProxy; -import org.apache.xml.security.utils.EncryptionConstants; -import org.apache.xml.security.utils.SignatureElementProxy; -import org.apache.xml.security.utils.XMLUtils; +import org.apache.xml.security.utils.*; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -90,7 +78,7 @@ * contains the corresponding type. * */ -public class KeyInfo extends SignatureElementProxy { +public class KeyInfo extends ElementProxy { private static final Logger LOG = System.getLogger(KeyInfo.class.getName()); @@ -233,12 +221,24 @@ public void add(RSAKeyValue rsakeyvalue) { } /** - * Method add + * Method adds public key encoded as KeyValue. If public key type is not supported by KeyValue, then + * DEREncodedKeyValue is used. If public key type is not supported by DEREncodedKeyValue, then + * IllegalArgumentException is thrown. * - * @param pk + * @param pk public key to be added to KeyInfo */ - public void add(PublicKey pk) { - this.add(new KeyValue(getDocument(), pk)); + public void add(PublicKey pk) { + + if (KeyValue.isSupportedKeyType(pk)) { + this.add(new KeyValue(getDocument(), pk)); + return; + } + + try { + this.add(new DEREncodedKeyValue(getDocument(), pk)); + } catch (XMLSecurityException ex) { + throw new IllegalArgumentException(ex); + } } /** @@ -813,6 +813,7 @@ public boolean containsKeyInfoReference() { return this.lengthKeyInfoReference() > 0; } + /** * This method returns the public key. * @@ -1229,4 +1230,10 @@ public void addStorageResolver(StorageResolver storageResolver) { public String getBaseLocalName() { return Constants._TAG_KEYINFO; } + + /** {@inheritDoc} */ + @Override + public String getBaseNamespace() { + return Constants.SignatureSpecNS; + } } diff --git a/src/main/java/org/apache/xml/security/keys/content/DEREncodedKeyValue.java b/src/main/java/org/apache/xml/security/keys/content/DEREncodedKeyValue.java index f1e8faf06..a5c402578 100644 --- a/src/main/java/org/apache/xml/security/keys/content/DEREncodedKeyValue.java +++ b/src/main/java/org/apache/xml/security/keys/content/DEREncodedKeyValue.java @@ -37,7 +37,10 @@ public class DEREncodedKeyValue extends Signature11ElementProxy implements KeyInfoContent { /** JCA algorithm key types supported by this implementation. */ - private static final String[] supportedKeyTypes = { "RSA", "DSA", "EC"}; + private static final String[] supportedKeyTypes = { "RSA", "DSA", "EC", + "DiffieHellman", "DH", "XDH", "X25519", "X448", + "EdDSA", "Ed25519", "Ed448", + "RSASSA-PSS"}; /** * Constructor DEREncodedKeyValue @@ -140,5 +143,4 @@ protected byte[] getEncodedDER(PublicKey publicKey) throws XMLSecurityException throw new XMLSecurityException(e, "DEREncodedKeyValue.UnsupportedPublicKey", exArgs); } } - } diff --git a/src/main/java/org/apache/xml/security/keys/content/KeyValue.java b/src/main/java/org/apache/xml/security/keys/content/KeyValue.java index ab8b79b99..98d741875 100644 --- a/src/main/java/org/apache/xml/security/keys/content/KeyValue.java +++ b/src/main/java/org/apache/xml/security/keys/content/KeyValue.java @@ -37,7 +37,6 @@ * (section 6.4). The KeyValue element may include externally defined public * keys values represented as PCDATA or element types from an external * namespace. - * */ public class KeyValue extends SignatureElementProxy implements KeyInfoContent { @@ -116,6 +115,20 @@ public KeyValue(Document doc, PublicKey pk) { } } + /** + * Verifies that the XML KeyValue encoding is supported for the given key type. If the + * encoding is supported, it returns true else false. + * + * @return true if the public key has a KeyValue encoding, false otherwise. + */ + public static boolean isSupportedKeyType(PublicKey publicKey) { + + return publicKey instanceof java.security.interfaces.DSAPublicKey + || publicKey instanceof java.security.interfaces.RSAPublicKey + || publicKey instanceof java.security.interfaces.ECPublicKey; + + } + /** * Constructor KeyValue * diff --git a/src/main/java/org/apache/xml/security/resource/xmlsecurity_de.properties b/src/main/java/org/apache/xml/security/resource/xmlsecurity_de.properties index 03a236956..68d2aced1 100644 --- a/src/main/java/org/apache/xml/security/resource/xmlsecurity_de.properties +++ b/src/main/java/org/apache/xml/security/resource/xmlsecurity_de.properties @@ -66,6 +66,7 @@ encryption.RSAPKCS15.blockTruncated = Block abgeschnitten encryption.RSAPKCS15.noDataInBlock = Im Block sind keine Daten enthalten encryption.RSAPKCS15.unknownBlockType = Unbekannter Block Typ encryption.nokey = Es ist kein verschl\u00fcsselungs Schl\u00fcssel geladen und es konnte kein Schl\u00fcssel mit Hilfe der "key resolvers" gefunden werden. +encryption.UnsupportedAlgorithmParameterSpec=Nicht unterst\u00fctzter Algorithmusparametertyp: {0} endorsed.jdk1.4.0 = Leider scheint niemand unsere Installations-Anleitung zu lesen, deshalb m\u00fcssen wir es \u00fcber die Exception machen\: Du hast den "endorsing" Mechanismus vom JDK 1.4 nicht richtig angewandt. Schaue unter nach wie man das Problem l\u00f6st. errorMessages.InvalidDigestValueException = Ung\u00fcltige Signatur\: Referen-Validierung fehlgeschlagen. errorMessages.InvalidSignatureValueException = Ung\u00fcltige Signatur\: Core Validierung fehlgeschlagen. @@ -108,6 +109,9 @@ KeyStore.alreadyRegistered = Klasse {0} bereits registriert f\u00fcr {1} KeyStore.register = {1} type class register error in class {0} KeyStore.registerStore.register = Registrierungsfehler f\u00fcr Typ {0} KeyValue.IllegalArgument = Kann kein {0} aus {1} erzeugen +KeyDerivation.TooShortParameter = Parameter zu kurz {0} +KeyDerivation.InvalidParameter = Ung\u00fcltige Parameter {0} +KeyDerivation.NotSupportedParameter = Nicht unterst\u00fctzter Parameterwert {0} namespacePrefixAlreadyUsedByOtherURI = Namespace {0} wird bereits von einer anderen URI {1} gebraucht notYetInitialized = Das Modul {0} ist noch nicht initialisiert prefix.AlreadyAssigned = Sie binden den Prefix {0} an den Namespace {1} aber er ist bereits an {2} zugewiesen @@ -196,4 +200,4 @@ stax.signature.keyNameMissing = KeyName nicht konfiguriert. stax.keyNotFoundForName = Kein Schl\u00fcssel für Schl\u00fcsselname konfiguriert: {0} stax.keyTypeNotSupported = Key vom Typ {0} nicht f\u00fcr einen Key-Namenssuche unterst\u00fctzt stax.idsetbutnotgenerated = An Id attribute is specified, but Id generation is disabled -stax.idgenerationdisablewithmultipleparts = Id generation must not be disabled when multiple parts need signing \ No newline at end of file +stax.idgenerationdisablewithmultipleparts = Id generation must not be disabled when multiple parts need signing diff --git a/src/main/java/org/apache/xml/security/resource/xmlsecurity_en.properties b/src/main/java/org/apache/xml/security/resource/xmlsecurity_en.properties index 054372004..f1b36a1be 100644 --- a/src/main/java/org/apache/xml/security/resource/xmlsecurity_en.properties +++ b/src/main/java/org/apache/xml/security/resource/xmlsecurity_en.properties @@ -66,6 +66,7 @@ encryption.RSAPKCS15.blockTruncated = block truncated encryption.RSAPKCS15.noDataInBlock = no data in block encryption.RSAPKCS15.unknownBlockType = unknown block type encryption.nokey = No Key Encryption Key loaded and cannot determine using key resolvers +encryption.UnsupportedAlgorithmParameterSpec=Unsupported Algorithm parameter type: {0} endorsed.jdk1.4.0 = Since it seems that nobody reads our installation notes, we must do it in the exception messages. Hope you read them. You did NOT use the endorsed mechanism from JDK 1.4 properly; look at how to solve this problem. errorMessages.InvalidDigestValueException = INVALID signature -- check reference resolution. errorMessages.InvalidSignatureValueException = INVALID signature -- core validation failed. @@ -108,6 +109,9 @@ KeyStore.alreadyRegistered = {0} Class has already been registered for {1} KeyStore.register = {1} type class register error in class {0} KeyStore.registerStore.register = Registration error for type {0} KeyValue.IllegalArgument = Cannot create a {0} from {1} +KeyDerivation.TooShortParameter = Key derivation parameter {0} is too short +KeyDerivation.InvalidParameter = Key derivation parameter {0} is illegal +KeyDerivation.NotSupportedParameter = Key derivation parameter {0} is not supported namespacePrefixAlreadyUsedByOtherURI = Namespace prefix {0} already used by other URI {1} notYetInitialized = The module {0} is not yet initialized prefix.AlreadyAssigned = You want to assign {0} as prefix for namespace {1} but it is already assigned for {2} diff --git a/src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java b/src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java new file mode 100644 index 000000000..d6a2c13a0 --- /dev/null +++ b/src/main/java/org/apache/xml/security/utils/DERDecoderUtils.java @@ -0,0 +1,262 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.utils; + +import org.apache.xml.security.exceptions.DERDecodingException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.PublicKey; + +/** + * Provides the means to navigate through a DER-encoded byte array, to help + * in decoding the contents. + *

+ * It maintains a "current position" in the array that advances with each + * operation, providing a simple means to handle the type-length-value + * encoding of DER. For example + *

+ *   decoder.expect(TYPE);
+ *   int length = decoder.getLength();
+ *   byte[] value = decoder.getBytes(len);
+ * 
+ */ +public class DERDecoderUtils { + private static final System.Logger LOG = System.getLogger(DERDecoderUtils.class.getName()); + + /** + * DER type identifier for a bit string value + */ + public static final byte TYPE_BIT_STRING = 0x03; + /** + * DER type identifier for a octet string value + */ + 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; + + /** + * Simple method parses an ASN.1 encoded byte array. The encoding uses "DER", a BER/1 subset, that means a triple { typeId, length, data }. + * with the following structure: + *

+ *

+     *  PublicKeyInfo ::= SEQUENCE {
+     *      algorithm   AlgorithmIdentifier,
+     *      PublicKey   BIT STRING
+     *  }
+     * 
+ *

+ * Where AlgorithmIdentifier is formatted as: + *

+     *  AlgorithmIdentifier ::= SEQUENCE {
+     *      algorithm   OBJECT IDENTIFIER,
+     *      parameters  ANY DEFINED BY algorithm OPTIONAL
+     *  }
+     *
+ * @param derEncodedIS the DER-encoded input stream to decode. + * @throws DERDecodingException in case of decoding error or if given InputStream is null or empty. + * @throws IOException if an I/O error occurs. + */ + public static byte[] getAlgorithmIdBytes(InputStream derEncodedIS) throws DERDecodingException, IOException { + if (derEncodedIS == null || derEncodedIS.available() <= 0) { + throw new DERDecodingException("DER decoding error: Null data"); + } + + validateType(derEncodedIS.read(), TYPE_SEQUENCE); + readLength(derEncodedIS); + validateType(derEncodedIS.read(), TYPE_SEQUENCE); + readLength(derEncodedIS); + + return readObjectIdentifier(derEncodedIS); + } + + /** + * Read the next object identifier from the given DER-encoded input stream. + *

+ * @param derEncodedIS the DER-encoded input stream to decode. + * @return the object identifier as a byte array. + * @throws DERDecodingException if parse error occurs. + */ + public static byte[] readObjectIdentifier(InputStream derEncodedIS) throws DERDecodingException { + try { + validateType(derEncodedIS.read(), TYPE_OBJECT_IDENTIFIER); + int length = readLength(derEncodedIS); + LOG.log(System.Logger.Level.DEBUG, "DER decoding algorithm id bytes"); + return derEncodedIS.readNBytes(length); + } catch (IOException ex) { + throw new DERDecodingException("Error occurred while reading the input stream.", ex); + } + } + + /** + * The method extracts the algorithm OID from the public key and returns it as "dot encoded" OID string. + * + * @param publicKey the public key for which method returns algorithm ID. + * @return String representing the algorithm ID. + * @throws DERDecodingException if the algorithm ID cannot be determined. + */ + public static String getAlgorithmIdFromPublicKey(PublicKey publicKey) throws DERDecodingException { + String keyFormat = publicKey.getFormat(); + if (!("X.509".equalsIgnoreCase(keyFormat) + || "X509".equalsIgnoreCase(keyFormat))) { + throw new DERDecodingException("Unknown key format [" + keyFormat + + "]! Support for X.509-encoded public keys only!"); + } + try (InputStream inputStream = new ByteArrayInputStream(publicKey.getEncoded())) { + byte[] keyAlgOidBytes = getAlgorithmIdBytes(inputStream); + String alg = decodeOID(keyAlgOidBytes); + if (alg.equals(KeyUtils.KeyAlgorithmType.EC.getOid())) { + keyAlgOidBytes = readObjectIdentifier(inputStream); + alg = decodeOID(keyAlgOidBytes); + } + return alg; + } catch (IOException ex) { + throw new DERDecodingException("Error reading public key", ex); + } + } + + private static void validateType(int iType, byte expectedType) throws DERDecodingException { + validateType((byte) (iType & 0xFF), expectedType); + } + + private static void validateType(byte type, byte expectedType) throws DERDecodingException { + if (type != expectedType) { + throw new DERDecodingException("DER decoding error: Expected type [" + expectedType + "] but got [" + type + "]"); + } + } + + /** + * Get the DER length at the current position. + *

+ * DER length is encoded as + *

    + *
  • If the first byte is 0x00 to 0x7F, it describes the actual length. + *
  • If the first byte is 0x80 + n with 0The length value 0x80, used only in constructed types, is + * defined as "indefinite length". + *
+ * + * @return the length, -1 for indefinite length. + * @throws DERDecodingException if the current position is at the end of the array or there is + * an incomplete length specification. + * @throws IOException if an I/O error occurs. + */ + public static int readLength(InputStream derEncodedIs) throws DERDecodingException, IOException { + if (derEncodedIs.available() <= 0) { + throw new DERDecodingException("Invalid DER format"); + } + + int value = derEncodedIs.read(); + + if ((value & 0x080) == 0x00) { // short form, 1 byte size + return value; + } + // number of bytes used to encode length + int byteCount = value & 0x07f; + //byteCount == 0 indicates indefinite length encoded data. + if (byteCount == 0) { + return -1; + } + + // byteCount > 4 not able to handle more than 4Gb of data (max int size) tmp > 4 indicates. + if (byteCount > 4) { + throw new DERDecodingException("Data length byte size: [" + byteCount + "] is incorrect/too big"); + } + byte[] intSizeBytes = derEncodedIs.readNBytes(byteCount); + return new BigInteger(1, intSizeBytes).intValue(); + } + + + /** + * The first two nodes of the OID are encoded onto a single byte. + * The first node is multiplied by the decimal 40 and the result is added to the value of the second node. + * Node values less than or equal to 127 are encoded in one byte. + * Node values greater than or equal to 128 are encoded on multiple bytes. + * Bit 7 of the leftmost byte is set to one. Bits 0 through 6 of each byte contains the encoded value. + * + * @param oidBytes the byte array containing the OID + * @return the decoded OID as a string + */ + public static String decodeOID(byte[] oidBytes) { + + int length = oidBytes.length; + StringBuilder sb = new StringBuilder(length * 4); + + int fromPos = 0; + for (int i = 0; i < length; i++) { + // if the 8th bit is set, it means the next byte is part of the current segment + if ((oidBytes[i] & 0x80) != 0) { + continue; + } + + // decode the OID segment + long decodedValue = decodeBytes(oidBytes, fromPos, i - fromPos + 1); + if (fromPos == 0) { + // first OID segment consists of two numbers + if (decodedValue < 80) { + sb.append(decodedValue / 40); + decodedValue = decodedValue % 40; + } else { + sb.append('2'); + decodedValue = decodedValue - 80; + } + } + + //add next OID segment + sb.append('.'); + sb.append(decodedValue); + fromPos = i + 1; + } + return sb.toString(); + } + + /** + * Decode a byte array into a long value. The most significant bit of each byte is ignored because it + * is used as a continuation flag. Bits are shifted to the left so that the most significant 7 bits are in first byte + * next 7 bits in second byte and so on. + * + * @param inBytes the input byte array + * @param iOffset start point inside inBytes + * @param iLength number of bytes to decode + * @return long value decoded from the byte array. + */ + private static long decodeBytes(byte[] inBytes, int iOffset, int iLength) { + // check if the OID segment is too big to decode with long value! + if (iLength > 8) { + throw new IllegalArgumentException("OID segment too long to parse: ["+iLength+"]"); + } + if (iLength > 1) { + int iSteps = iLength - 1; + return ((long) (inBytes[iOffset] & 0x07f) << 7 * iSteps) + + decodeBytes(inBytes, iOffset + 1, iSteps); + } else { + return inBytes[iOffset] & 0x07f; + } + } +} diff --git a/src/main/java/org/apache/xml/security/utils/Encryption11ElementProxy.java b/src/main/java/org/apache/xml/security/utils/Encryption11ElementProxy.java new file mode 100644 index 000000000..ba9f6e394 --- /dev/null +++ b/src/main/java/org/apache/xml/security/utils/Encryption11ElementProxy.java @@ -0,0 +1,67 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.utils; + +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Class Encryption11ElementProxy + */ +public abstract class Encryption11ElementProxy extends ElementProxy { + + protected Encryption11ElementProxy() { + } + + /** + * Constructor Encryption11ElementProxy + * + * @param doc + */ + public Encryption11ElementProxy(Document doc) { + if (doc == null) { + throw new RuntimeException("Document is null"); + } + setDocument(doc); + setElement(XMLUtils.createElementInEncryption11Space(doc, this.getBaseLocalName())); + String prefix = ElementProxy.getDefaultPrefix(this.getBaseNamespace()); + if (prefix != null && prefix.length() > 0) { + getElement().setAttribute("xmlns:" + prefix, this.getBaseNamespace()); + } + } + + /** + * Constructor Signature11ElementProxy + * + * @param element + * @param baseURI + * @throws XMLSecurityException + */ + public Encryption11ElementProxy(Element element, String baseURI) throws XMLSecurityException { + super(element, baseURI); + + } + + /** {@inheritDoc} */ + @Override + public String getBaseNamespace() { + return EncryptionConstants.EncryptionSpec11NS; + } +} diff --git a/src/main/java/org/apache/xml/security/utils/EncryptionConstants.java b/src/main/java/org/apache/xml/security/utils/EncryptionConstants.java index 48381e9d0..284e43a65 100644 --- a/src/main/java/org/apache/xml/security/utils/EncryptionConstants.java +++ b/src/main/java/org/apache/xml/security/utils/EncryptionConstants.java @@ -45,6 +45,22 @@ public final class EncryptionConstants { /** Tag of Attr mimetype **/ public static final String _ATT_MIMETYPE = "MimeType"; + /** Tag of Attr AlgorithmID**/ + public static final String _ATT_ALGORITHM_ID = "AlgorithmID"; + + /** Tag of Attr PartyUInfo**/ + public static final String _ATT_PARTYUINFO = "PartyUInfo"; + + /** Tag of Attr PartyVInfo**/ + public static final String _ATT_PARTYVINFO = "PartyVInfo"; + + /** Tag of Attr PartyVInfo**/ + public static final String _ATT_SUPPPUBINFO = "SuppPubInfo"; + + /** Tag of Attr PartyVInfo**/ + public static final String _ATT_SUPPPRIVINFO = "SuppPrivInfo"; + + /** Tag of Element CarriedKeyName **/ public static final String _TAG_CARRIEDKEYNAME = "CarriedKeyName"; @@ -105,6 +121,12 @@ public final class EncryptionConstants { /** Tag of Element RecipientKeyInfo **/ public static final String _TAG_RECIPIENTKEYINFO = "RecipientKeyInfo"; + /** Tag of Element KeyDerivationMethod **/ + public static final String _TAG_KEYDERIVATIONMETHOD = "KeyDerivationMethod"; + + /** Tag of Element ConcatKDFParams **/ + public static final String _TAG_CONCATKDFPARAMS = "ConcatKDFParams"; + /** Field ENCRYPTIONSPECIFICATION_URL */ public static final String ENCRYPTIONSPECIFICATION_URL = "http://www.w3.org/TR/2001/WD-xmlenc-core-20010626/"; @@ -191,6 +213,10 @@ public final class EncryptionConstants { public static final String ALGO_ID_KEYAGREEMENT_DH = EncryptionConstants.EncryptionSpecNS + "dh"; + /** Key Agreement Diffie-Hellman for EC (and X) keys with the originator ephemeral and receiver static key */ + public static final String ALGO_ID_KEYAGREEMENT_ECDH_ES = + EncryptionConstants.EncryptionSpec11NS + "ECDH-ES"; + /** Symmetric Key Wrap - REQUIRED TRIPLEDES KeyWrap */ public static final String ALGO_ID_KEYWRAP_TRIPLEDES = EncryptionConstants.EncryptionSpecNS + "kw-tripledes"; @@ -259,6 +285,9 @@ public final class EncryptionConstants { public static final String MGF1_SHA512 = EncryptionConstants.EncryptionSpec11NS + "mgf1sha512"; + /** Key derivation function ConcatKDF */ + public static final String ALGO_ID_KEYDERIVATION_CONCATKDF = + EncryptionConstants.EncryptionSpec11NS + "ConcatKDF"; private EncryptionConstants() { // we don't allow instantiation diff --git a/src/main/java/org/apache/xml/security/utils/EncryptionElementProxy.java b/src/main/java/org/apache/xml/security/utils/EncryptionElementProxy.java new file mode 100644 index 000000000..c23b33d75 --- /dev/null +++ b/src/main/java/org/apache/xml/security/utils/EncryptionElementProxy.java @@ -0,0 +1,67 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.utils; + +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Class EncryptionElementProxy + * + */ +public abstract class EncryptionElementProxy extends ElementProxy { + + protected EncryptionElementProxy() { + } + + /** + * Constructor EncryptionElementProxy + * + * @param doc the {@link Document} in which Encryption Element will be placed + */ + public EncryptionElementProxy(Document doc) { + if (doc == null) { + throw new IllegalArgumentException("Document is null"); + } + setDocument(doc); + setElement(XMLUtils.createElementInEncryptionSpace(doc, this.getBaseLocalName())); + String prefix = ElementProxy.getDefaultPrefix(this.getBaseNamespace()); + if (prefix != null && !prefix.isEmpty()) { + getElement().setAttribute("xmlns:" + prefix, this.getBaseNamespace()); + } + } + + /** + * Constructor EncryptionElementProxy + * + * @param element Encryption Element + * @param baseURI the namespace URI of element + * @throws XMLSecurityException if a {@link XMLSecurityException} occurs + */ + public EncryptionElementProxy(Element element, String baseURI) throws XMLSecurityException { + super(element, baseURI); + } + + /** {@inheritDoc} */ + @Override + public String getBaseNamespace() { + return EncryptionConstants.EncryptionSpecNS; + } +} diff --git a/src/main/java/org/apache/xml/security/utils/KeyUtils.java b/src/main/java/org/apache/xml/security/utils/KeyUtils.java new file mode 100644 index 000000000..4a5d71ba4 --- /dev/null +++ b/src/main/java/org/apache/xml/security/utils/KeyUtils.java @@ -0,0 +1,281 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.utils; + +import org.apache.xml.security.algorithms.implementations.ECDSAUtils; +import org.apache.xml.security.encryption.XMLEncryptionException; +import org.apache.xml.security.encryption.params.ConcatKDFParams; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; +import org.apache.xml.security.encryption.params.KeyDerivationParameters; +import org.apache.xml.security.exceptions.DERDecodingException; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.encryption.keys.content.derivedKey.ConcatKDF; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.lang.System.Logger.Level; +import java.security.*; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.util.Arrays; + +/** + * A set of utility methods to handle keys. + */ +public class KeyUtils { + private static final System.Logger LOG = System.getLogger(KeyUtils.class.getName()); + /** + * Enumeration of Supported key algorithm types. + */ + public enum KeyAlgorithmType { + EC("EC", "1.2.840.10045.2.1"), + DSA("DSA", "1.2.840.10040.4.1"), + RSA("RSA", "1.2.840.113549.1.1.1"), + RSASSA_PSS("RSASSA-PSS", "1.2.840.113549.1.1.10"), + DH("DiffieHellman", "1.2.840.113549.1.3.1"), + XDH("XDH", null), + EdDSA("EdDSA", null); + private final String jceName; + private final String oid; + + KeyAlgorithmType(String jceName, String oid) { + this.jceName = jceName; + this.oid = oid; + } + + public String getJceName() { + return jceName; + } + + public String getOid() { + return oid; + } + + } + + /** + * Enumeration of specific key types. + */ + public enum KeyType { + DSA("DSA", "RFC 8017", KeyAlgorithmType.DSA, "1.2.840.10040.4.1"), + RSA("RSA", "RFC 8017", KeyAlgorithmType.RSA, "1.2.840.113549.1.1.1"), + RSASSA_PSS("RSASSA-PSS", "RFC 3447", KeyAlgorithmType.RSASSA_PSS, "1.2.840.113549.1.1.10"), + DH("DH", "PKCS #3", KeyAlgorithmType.DH, "1.2.840.113549.1.3.1"), + SECT163K1("sect163k1", "NIST K-163", KeyAlgorithmType.EC, "1.3.132.0.1"), + SECT163R1("sect163r1", "", KeyAlgorithmType.EC, "1.3.132.0.2"), + SECT163R2("sect163r2", "NIST B-163", KeyAlgorithmType.EC, "1.3.132.0.15"), + SECT193R1("sect193r1", "", KeyAlgorithmType.EC, "1.3.132.0.24"), + SECT193R2("sect193r2", "", KeyAlgorithmType.EC, "1.3.132.0.25"), + SECT233K1("sect233k1", "NIST K-233", KeyAlgorithmType.EC, "1.3.132.0.26"), + SECT233R1("sect233r1", "NIST B-233", KeyAlgorithmType.EC, "1.3.132.0.27"), + SECT239K1("sect239k1", "", KeyAlgorithmType.EC, "1.3.132.0.3"), + SECT283K1("sect283k1", "NIST K-283", KeyAlgorithmType.EC, "1.3.132.0.16"), + SECT283R1("sect283r1", "", KeyAlgorithmType.EC, "1.3.132.0.17"), + SECT409K1("sect409k1", "NIST K-409", KeyAlgorithmType.EC, "1.3.132.0.36"), + SECT409R1("sect409r1", "NIST B-409", KeyAlgorithmType.EC, "1.3.132.0.37"), + SECT571K1("sect571k1", "NIST K-571", KeyAlgorithmType.EC, "1.3.132.0.38"), + SECT571R1("sect571r1", "NIST B-571", KeyAlgorithmType.EC, "1.3.132.0.39"), + SECP160K1("secp160k1", "", KeyAlgorithmType.EC, "1.3.132.0.9"), + SECP160R1("secp160r1", "", KeyAlgorithmType.EC, "1.3.132.0.8"), + SECP160R2("secp160r2", "", KeyAlgorithmType.EC, "1.3.132.0.30"), + SECP192K1("secp192k1", "", KeyAlgorithmType.EC, "1.3.132.0.31"), + SECP192R1("secp192r1", "NIST P-192,X9.62 prime192v1", KeyAlgorithmType.EC, "1.2.840.10045.3.1.1"), + SECP224K1("secp224k1", "", KeyAlgorithmType.EC, "1.3.132.0.32"), + SECP224R1("secp224r1", "NIST P-224", KeyAlgorithmType.EC, "1.3.132.0.33"), + SECP256K1("secp256k1", "", KeyAlgorithmType.EC, "1.3.132.0.10"), + SECP256R1("secp256r1", "NIST P-256,X9.62 prime256v1", KeyAlgorithmType.EC, "1.2.840.10045.3.1.7"), + SECP384R1("secp384r1", "NIST P-384", KeyAlgorithmType.EC, "1.3.132.0.34"), + SECP521R1("secp521r1", "NIST P-521", KeyAlgorithmType.EC, "1.3.132.0.35"), + BRAINPOOLP256R1("brainpoolP256r1", "RFC 5639", KeyAlgorithmType.EC, "1.3.36.3.3.2.8.1.1.7"), + BRAINPOOLP384R1("brainpoolP384r1", "RFC 5639", KeyAlgorithmType.EC, "1.3.36.3.3.2.8.1.1.11"), + BRAINPOOLP512R1("brainpoolP512r1", "RFC 5639", KeyAlgorithmType.EC, "1.3.36.3.3.2.8.1.1.13"), + X25519("x25519", "RFC 7748", KeyAlgorithmType.XDH, "1.3.101.110"), + X448("x448", "RFC 7748", KeyAlgorithmType.XDH, "1.3.101.111"), + ED25519("ed25519", "RFC 8032", KeyAlgorithmType.EdDSA, "1.3.101.112"), + ED448("ed448", "RFC 8032", KeyAlgorithmType.EdDSA, "1.3.101.113"), + ; + + private final String name; + private final String origin; + private final KeyAlgorithmType algorithm; + private final String oid; + + KeyType(String name, String origin, KeyAlgorithmType algorithm, String oid) { + this.name = name; + this.origin = origin; + this.algorithm = algorithm; + this.oid = oid; + } + + + public String getName() { + return name; + } + + public KeyAlgorithmType getAlgorithm() { + return algorithm; + } + + public String getOid() { + return oid; + } + + public String getOrigin() { + return origin; + } + + public static KeyType getByOid(String oid) { + return Arrays.stream(KeyType.values()) + .filter(keyType -> keyType.getOid().equals(oid)) + .findFirst().orElse(null); + } + } + + /** + * Method generates DH keypair which match the type of given public key type. + * + * @param recipientPublicKey public key of recipient + * @param provider provider to use for key generation + * @return generated keypair + * @throws XMLEncryptionException if the keys cannot be generated + */ + public static KeyPair generateEphemeralDHKeyPair(PublicKey recipientPublicKey, Provider provider) throws XMLEncryptionException { + String algorithm = recipientPublicKey.getAlgorithm(); + KeyPairGenerator keyPairGenerator; + try { + + if (recipientPublicKey instanceof ECPublicKey) { + keyPairGenerator = createKeyPairGenerator(algorithm, provider); + ECPublicKey exchangePublicKey = (ECPublicKey) recipientPublicKey; + String keyOId = ECDSAUtils.getOIDFromPublicKey(exchangePublicKey); + if (keyOId == null) { + keyOId = DERDecoderUtils.getAlgorithmIdFromPublicKey(recipientPublicKey); + } + ECGenParameterSpec kpgparams = new ECGenParameterSpec(keyOId); + keyPairGenerator.initialize(kpgparams); + } else { + String keyOId = DERDecoderUtils.getAlgorithmIdFromPublicKey(recipientPublicKey); + KeyType keyType = KeyType.getByOid(keyOId); + keyPairGenerator = createKeyPairGenerator(keyType==null?keyOId: keyType.getName() , provider); + } + + return keyPairGenerator.generateKeyPair(); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | DERDecodingException e) { + throw new XMLEncryptionException(e); + } + } + + /** + * Create a KeyPairGenerator for the given algorithm and provider. + * + * @param algorithm the key JCE algorithm name + * @param provider the provider to use or null if default JCE provider should be used + * @return the KeyPairGenerator + * @throws NoSuchAlgorithmException if the algorithm is not supported + */ + public static KeyPairGenerator createKeyPairGenerator(String algorithm, Provider provider) throws NoSuchAlgorithmException { + return provider == null ? KeyPairGenerator.getInstance(algorithm) + : KeyPairGenerator.getInstance(algorithm, provider); + } + + + /** + * Method generates a secret key for given KeyAgreementParameterSpec. + * + * @param parameterSpec KeyAgreementParameterSpec which defines algorithm to derive key + * @return generated secret key + * @throws XMLEncryptionException if the secret key cannot be generated as: Key agreement is not supported, + * wrong key types, etc. + */ + public static SecretKey aesWrapKeyWithDHGeneratedKey(KeyAgreementParameters parameterSpec) + throws XMLEncryptionException { + try { + PublicKey publicKey = parameterSpec.getAgreementPublicKey(); + PrivateKey privateKey = parameterSpec.getAgreementPrivateKey(); + + String algorithm = publicKey.getAlgorithm(); + if ("EC".equalsIgnoreCase(algorithm)) { + LOG.log(Level.WARNING, "EC keys are detected for key agreement algorithm! " + + "Cryptographic algorithm may not be secure, consider using a different algorithm (and keys)."); + } + algorithm = algorithm + (algorithm.equalsIgnoreCase("EC") ? "DH" : ""); + KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithm); + keyAgreement.init(privateKey); + keyAgreement.doPhase(publicKey, true); + byte[] secret = keyAgreement.generateSecret(); + byte[] kek = deriveKeyEncryptionKey(secret, parameterSpec.getKeyDerivationParameter()); + return new SecretKeySpec(kek, "AES"); + + + } catch (XMLSecurityException | NoSuchAlgorithmException | InvalidKeyException e) { + throw new XMLEncryptionException(e); + } + } + + /** + * Defines the key size for the encrypting algorithm. + * + * @param keyWrapAlg the key wrap algorithm URI + * @return the key size in bits + * @throws XMLEncryptionException if the key wrap algorithm is not supported + */ + public static int getAESKeyBitSizeForWrapAlgorithm(String keyWrapAlg) throws XMLEncryptionException { + switch (keyWrapAlg) { + case EncryptionConstants.ALGO_ID_KEYWRAP_AES128: + return 128; + case EncryptionConstants.ALGO_ID_KEYWRAP_AES192: + return 192; + case EncryptionConstants.ALGO_ID_KEYWRAP_AES256: + return 256; + default: + throw new XMLEncryptionException("Unsupported KeyWrap Algorithm"); + } + } + + + /** + * Derive a key encryption key from a shared secret and keyDerivationParameter. Currently only the ConcatKDF is supported. + * @param sharedSecret the shared secret + * @param keyDerivationParameter the key derivation parameters + * @return the derived key encryption key + * @throws XMLSecurityException if the key derivation algorithm is not supported + */ + public static byte[] deriveKeyEncryptionKey(byte[] sharedSecret, KeyDerivationParameters keyDerivationParameter) + throws XMLSecurityException { + int iKeySize = keyDerivationParameter.getKeyBitLength()/8; + String keyDerivationAlgorithm = keyDerivationParameter.getAlgorithm(); + if (!EncryptionConstants.ALGO_ID_KEYDERIVATION_CONCATKDF.equals(keyDerivationAlgorithm)) { + throw new XMLEncryptionException( "unknownAlgorithm", + keyDerivationAlgorithm); + } + ConcatKDFParams ckdfParameter = (ConcatKDFParams) keyDerivationParameter; + + // get parameters + String digestAlgorithm = ckdfParameter.getDigestAlgorithm(); + + String algorithmID = ckdfParameter.getAlgorithmID(); + String partyUInfo = ckdfParameter.getPartyUInfo(); + String partyVInfo = ckdfParameter.getPartyVInfo(); + String suppPubInfo = ckdfParameter.getSuppPubInfo(); + String suppPrivInfo = ckdfParameter.getSuppPrivInfo(); + + ConcatKDF concatKDF = new ConcatKDF(digestAlgorithm); + return concatKDF.deriveKey(sharedSecret, algorithmID, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo, iKeySize); + } +} diff --git a/src/main/java/org/apache/xml/security/utils/XMLUtils.java b/src/main/java/org/apache/xml/security/utils/XMLUtils.java index 3ad292c90..9027469cd 100644 --- a/src/main/java/org/apache/xml/security/utils/XMLUtils.java +++ b/src/main/java/org/apache/xml/security/utils/XMLUtils.java @@ -706,6 +706,20 @@ public static Element selectXencNode(Node sibling, String nodeName, int number) return null; } + /** + * Helper method to get the "number"-th element for a given local + * name and namespace: http://www.w3.org/2009/xmlenc11#. If element with given search parameters is not found, + * null is returned. + * + * @param sibling the sibling node from which to start searching + * @param nodeName the local name of the element to search for + * @param number the index of the element to search for + * @return node with the given node name or null if not found. + */ + public static Element selectXenc11Node(Node sibling, String nodeName, int number) { + return selectNode(sibling, EncryptionConstants.EncryptionSpec11NS, nodeName, number); + } + /** * @param sibling * @param uri diff --git a/src/test/java/org/apache/xml/security/test/dom/encryption/XMLCipherTest.java b/src/test/java/org/apache/xml/security/test/dom/encryption/XMLCipherTest.java index 554cca280..b187f9dea 100644 --- a/src/test/java/org/apache/xml/security/test/dom/encryption/XMLCipherTest.java +++ b/src/test/java/org/apache/xml/security/test/dom/encryption/XMLCipherTest.java @@ -28,12 +28,10 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.nio.charset.StandardCharsets; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; @@ -44,6 +42,7 @@ import javax.crypto.spec.SecretKeySpec; import org.apache.xml.security.algorithms.JCEMapper; +import org.apache.xml.security.algorithms.MessageDigestAlgorithm; import org.apache.xml.security.c14n.Canonicalizer; import org.apache.xml.security.encryption.CipherData; import org.apache.xml.security.encryption.EncryptedData; @@ -52,14 +51,24 @@ import org.apache.xml.security.encryption.EncryptionProperties; import org.apache.xml.security.encryption.EncryptionProperty; import org.apache.xml.security.encryption.XMLCipher; +import org.apache.xml.security.encryption.keys.KeyInfoEnc; +import org.apache.xml.security.encryption.params.ConcatKDFParams; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; +import org.apache.xml.security.encryption.params.KeyDerivationParameters; import org.apache.xml.security.keys.KeyInfo; import org.apache.xml.security.parser.XMLParserException; import org.apache.xml.security.test.dom.TestUtils; +import org.apache.xml.security.testutils.JDKTestUtils; +import org.apache.xml.security.testutils.KeyTestUtils; import org.apache.xml.security.transforms.params.XPathContainer; import org.apache.xml.security.utils.EncryptionConstants; +import org.apache.xml.security.utils.KeyUtils; import org.apache.xml.security.utils.XMLUtils; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -128,6 +137,13 @@ public XMLCipherTest() throws Exception { JCEMapper.translateURItoJCEID(EncryptionConstants.ALGO_ID_KEYWRAP_AES128) != null; } + @AfterEach + public void afterTest() throws Exception { + // remove the dynamically installed provider + if (JDKTestUtils.getAuxiliaryProvider()!=null) { + Security.removeProvider(JDKTestUtils.getAuxiliaryProvider().getName()); + } + } /** * Test encryption using a generated AES 128 bit key that is * encrypted using a AES 192 bit key. Then reverse using the KEK @@ -268,6 +284,92 @@ void testAES256ElementRSAKWCipherUsingKEK() throws Exception { } } + /** + * Test that encrypt and decrypt using ECDH-ES for key encryption + *

+ * @throws Exception Thrown when there is any problem in signing or verification + */ + @ParameterizedTest + @EnumSource(value = KeyUtils.KeyType.class, mode = EnumSource.Mode.INCLUDE, + names = {"SECP256R1", "SECP384R1", "SECP521R1", "X25519", "X448"}) + void testAES128ElementEcdhEsKWCipher(KeyUtils.KeyType keyType) throws Exception { + // Skip test for IBM JDK + Assumptions.assumeTrue(haveISOPadding, + "Test testAES128ElementEcdhEsKWCipher for key ["+keyType+"] was skipped as necessary algorithms not available!" ); + Assumptions.assumeTrue(JDKTestUtils.isAlgorithmSupported(keyType.getAlgorithm().getJceName(), true), + "Test testAES128ElementEcdhEsKWCipher for key ["+keyType+"] was skipped as necessary algorithms not available!" ); + + // init parameters encrypted key object + String dataEncryptionAlgorithm = XMLCipher.AES_256_GCM; + String keyWrapAlgorithm = XMLCipher.AES_128_KeyWrap; + int transportKeyBitLength = 128; + + // prepare the test document + Document d = document(); // source + Document ed = null; + Document dd = null; + Element e = (Element) d.getElementsByTagName(element()).item(index()); + Element ee = null; + String source = null; + String target = null; + + source = toString(d); + + // Generate test recipient key pair + KeyPair recipientKeyPair = KeyTestUtils.generateKeyPair(keyType); + PrivateKey privRecipientKey = recipientKeyPair.getPrivate(); + PublicKey pubRecipientKey = recipientKeyPair.getPublic(); + + // Generate a traffic key + KeyGenerator keygen = KeyGenerator.getInstance("AES"); + keygen.init(transportKeyBitLength); + Key ephemeralSymmetricKey = keygen.generateKey(); + + + XMLCipher cipherEncKey = XMLCipher.getInstance(keyWrapAlgorithm); + cipherEncKey.init(XMLCipher.WRAP_MODE, pubRecipientKey); + cipherEncKey.setSecureValidation(true); + // create key agreement parameters + int keyBitLen = KeyUtils.getAESKeyBitSizeForWrapAlgorithm(keyWrapAlgorithm); + KeyDerivationParameters keyDerivationParameter = new ConcatKDFParams(keyBitLen, + MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256); + AlgorithmParameterSpec parameterSpec = new KeyAgreementParameters( + KeyAgreementParameters.ActorType.ORIGINATOR, + EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES, + keyDerivationParameter); + // encrypt transport key with KeyAgreement + EncryptedKey encryptedKey = cipherEncKey.encryptKey(d, ephemeralSymmetricKey, parameterSpec, null); + assertEquals(1, ((KeyInfoEnc)encryptedKey.getKeyInfo()).lengthAgreementMethod()); + + + // encrypt data + XMLCipher cipherEncData = XMLCipher.getInstance(dataEncryptionAlgorithm); + cipherEncData.init(XMLCipher.ENCRYPT_MODE, ephemeralSymmetricKey); + EncryptedData builder = cipherEncData.getEncryptedData(); + // add encrypted key to key info in encrypted data + KeyInfo builderKeyInfo = builder.getKeyInfo(); + if (builderKeyInfo == null) { + builderKeyInfo = new KeyInfo(d); + builder.setKeyInfo(builderKeyInfo); + } + builderKeyInfo.add(encryptedKey); + + ed = cipherEncData.doFinal(d, e); + + Files.write(Paths.get("target","test-enc-"+keyType.name()+".xml"), toString(ed).getBytes()); + + //decrypt + ee = (Element) ed.getElementsByTagName("xenc:EncryptedData").item(0); + XMLCipher cipherDecData = XMLCipher.getInstance(dataEncryptionAlgorithm); + cipherDecData.init(XMLCipher.DECRYPT_MODE, null); + cipherDecData.setKEK(privRecipientKey); + cipherDecData.setSecureValidation(true); + dd = cipherDecData.doFinal(ed, ee); + + target = toString(dd); + assertEquals(source, target); + } + /** * Test encryption using a generated AES 192 bit key that is * encrypted using a 3DES key. Then reverse by decrypting @@ -1004,4 +1106,4 @@ private int index() { return Integer.parseInt(elementIndex); } -} \ No newline at end of file +} diff --git a/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryption11Test.java b/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryption11Test.java index 4d15bc5b2..08175d221 100644 --- a/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryption11Test.java +++ b/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryption11Test.java @@ -18,12 +18,12 @@ */ package org.apache.xml.security.test.dom.encryption; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; +import java.io.*; import java.lang.System.Logger; import java.lang.System.Logger.Level; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; @@ -33,36 +33,48 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; import java.util.HashMap; import java.util.Map; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; +import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.xml.security.algorithms.JCEMapper; +import org.apache.xml.security.c14n.Canonicalizer; +import org.apache.xml.security.encryption.AgreementMethod; import org.apache.xml.security.encryption.EncryptedData; import org.apache.xml.security.encryption.EncryptedKey; import org.apache.xml.security.encryption.XMLCipher; +import org.apache.xml.security.encryption.keys.KeyInfoEnc; +import org.apache.xml.security.encryption.params.ConcatKDFParams; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; +import org.apache.xml.security.encryption.params.KeyDerivationParameters; import org.apache.xml.security.keys.KeyInfo; import org.apache.xml.security.keys.content.X509Data; import org.apache.xml.security.keys.content.x509.XMLX509Certificate; +import org.apache.xml.security.parser.XMLParserException; import org.apache.xml.security.test.dom.DSNamespaceContext; import org.apache.xml.security.utils.Constants; import org.apache.xml.security.utils.EncryptionConstants; +import org.apache.xml.security.utils.KeyUtils; import org.apache.xml.security.utils.XMLUtils; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import static org.apache.xml.security.test.XmlSecTestEnvironment.resolveFile; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeFalse; @@ -72,11 +84,16 @@ * http://www.w3.org/2008/xmlsec/Drafts/xmlenc-core-11/test-cases/ * * Note: I had to convert the given .p12 file into a .jks as it could not be loaded with KeyStore. - * - * TODO As of now all of the KeyWrapping tests are supported, but none of the KeyAgreement tests. */ class XMLEncryption11Test { + private static final DocumentBuilderFactory DEFAULT_DOCUMENT_BUILDER_FACTORY; + static { + DEFAULT_DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + DEFAULT_DOCUMENT_BUILDER_FACTORY.setNamespaceAware(true); + } + private static final String RESOURCE_FOLDER = "/org/w3c/www/interop/xmlenc-core-11/"; + private static String cardNumber; private static int nodeCount = 0; private boolean haveISOPadding; @@ -515,35 +532,268 @@ void testKeyWrappingRSA4096EncryptDecryptSHA224() throws Exception { // XMLUtils.outputDOM(dd.getFirstChild(), System.out); checkDecryptedDoc(dd, true); } else { - LOG.log(Level.WARNING, "Skipping testRSA2048 as necessary crypto algorithms are not available"); + LOG.log(Level.WARNING, + "Skipping testRSA2048 as necessary " + + "crypto algorithms are not available" + ); } } + /** + * The KeyAgreement test cases from the W3C test suite using XML as input. + * + * https://www.w3.org/2008/xmlsec/Drafts/xmlenc-core-11/test-cases/#sec-KeyAgreement + */ + @ParameterizedTest + @CsvSource({ + "AGRMNT.1, plaintext.xml, EC-P256_SHA256WithECDSA-v02.p12, PKCS12, passwd, test-certificate, http://www.w3.org/2001/04/xmlenc#kw-aes128, http://www.w3.org/2009/xmlenc11#aes128-gcm, http://www.w3.org/2001/04/xmlenc#sha256", + "AGRMNT.2, plaintext.xml, EC-P384_SHA256WithECDSA-v02.p12, PKCS12, passwd, test-certificate, http://www.w3.org/2001/04/xmlenc#kw-aes192, http://www.w3.org/2009/xmlenc11#aes192-gcm, http://www.w3.org/2001/04/xmldsig-more#sha384", + "AGRMNT.3, plaintext.xml, EC-P521_SHA256WithECDSA-v02.p12, PKCS12, passwd, test-certificate, http://www.w3.org/2001/04/xmlenc#kw-aes256, http://www.w3.org/2009/xmlenc11#aes256-gcm, http://www.w3.org/2001/04/xmlenc#sha512", + }) + void testAgreementKeyEncryptDecryptDocument(String w3cTag, + String data, + String keystoreFile, String keystoreType, + String passwd, String alias, + String keyWrapAlgorithm, + String encryptionAlgorithm, + String kdfAlgorithm) throws Exception { + Assumptions.assumeTrue(haveISOPadding, + "Skipping testAgreementKey ["+w3cTag+"] as necessary crypto algorithms are not available"); + + KeyStore keyStore = loadKeyStoreFromResource(keystoreFile, passwd, keystoreType); + Certificate cert = keyStore.getCertificate(alias); + + KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) + keyStore.getEntry(alias, new KeyStore.PasswordProtection(passwd.toCharArray())); + PrivateKey ecKey = pkEntry.getPrivateKey(); + + // Perform encryption + Document doc = loadDocumentFromResource(data); + Key sessionKey = getSessionKey(encryptionAlgorithm); + + + int keyBitLen = KeyUtils.getAESKeyBitSizeForWrapAlgorithm(keyWrapAlgorithm); + KeyDerivationParameters keyDerivationParameter = new ConcatKDFParams(keyBitLen, kdfAlgorithm); + AlgorithmParameterSpec parameterSpec = new KeyAgreementParameters( + KeyAgreementParameters.ActorType.ORIGINATOR, + EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES, + keyDerivationParameter); + + + EncryptedKey encryptedKey = + createEncryptedKey( + doc, + (X509Certificate)cert, + sessionKey, + keyWrapAlgorithm, + parameterSpec, null); + + + doc = encryptDocument( + doc, + encryptedKey, + sessionKey, + encryptionAlgorithm + ); + + Files.write(Paths.get("target","test-enc-"+w3cTag+".xml"), toString(doc.getFirstChild()).getBytes()); + // XMLUtils.outputDOM(doc.getFirstChild(), System.out); + + // Perform decryption + Document dd = decryptElement(doc, ecKey, (X509Certificate)cert); + // XMLUtils.outputDOM(dd.getFirstChild(), System.out); + checkDecryptedDoc(dd, true); + } + + /** + * The KeyAgreement test cases from the W3C test suite using XML as input. The method decrypts the document from + * test page and compares the result with the expected result. + * + * https://www.w3.org/2008/xmlsec/Drafts/xmlenc-core-11/test-cases/#sec-KeyAgreement + */ + @ParameterizedTest + @CsvSource({ + "AGRMNT.1-dec, cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-1.xml, EC-P256_SHA256WithECDSA-v02.p12, PKCS12, passwd, test-certificate", + "AGRMNT.2-dec, cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-2.xml, EC-P384_SHA256WithECDSA-v02.p12, PKCS12, passwd, test-certificate", + "AGRMNT.3-dec, cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-3.xml, EC-P521_SHA256WithECDSA-v02.p12, PKCS12, passwd, test-certificate", + }) + void testAgreementKeyDecryptDocument(String w3cTag, + String encryptedData, + String keystoreFile, String keystoreType, + String passwd, String alias) throws Exception { + Assumptions.assumeTrue(haveISOPadding, + "Skipping testAgreementKey ["+w3cTag+"] as necessary crypto algorithms are not available"); + + KeyStore keyStore = loadKeyStoreFromResource(keystoreFile, passwd, keystoreType); + Certificate cert = keyStore.getCertificate(alias); + + KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) + keyStore.getEntry(alias, new KeyStore.PasswordProtection(passwd.toCharArray())); + PrivateKey ecKey = pkEntry.getPrivateKey(); + + // get encrypted data from test page + Document ecryptedXmlDocument = loadDocumentFromResource(encryptedData); + // Perform decryption + Document dd = decryptElement(ecryptedXmlDocument, ecKey, (X509Certificate)cert); + checkDecryptedDoc(dd, true); + } + + /** + * The KeyAgreement test cases from the W3C test suite using bytearray as input. + * + * https://www.w3.org/2008/xmlsec/Drafts/xmlenc-core-11/test-cases/#sec-KeyAgreement + */ + @ParameterizedTest + @CsvSource({ + "AGRMNT.4, binary-data.hex, EC-P256.pfx, PKCS12, 1234, certreq-5b4623c8-5790-4b32-b59f-540c8bcfda4a, http://www.w3.org/2001/04/xmlenc#kw-aes128, http://www.w3.org/2009/xmlenc11#aes128-gcm, http://www.w3.org/2001/04/xmlenc#sha256", + "AGRMNT.5, binary-data.hex, EC-P384.pfx, PKCS12, 1234, certreq-4c7c6242-e408-4391-a7e7-1a87a2ef2ba8, http://www.w3.org/2001/04/xmlenc#kw-aes192, http://www.w3.org/2009/xmlenc11#aes192-gcm, http://www.w3.org/2001/04/xmldsig-more#sha384", + "AGRMNT.6, binary-data.hex, EC-P521.pfx, PKCS12, 1234, certreq-61afb173-5eab-475a-8c54-0cb792c82820, http://www.w3.org/2001/04/xmlenc#kw-aes256, http://www.w3.org/2009/xmlenc11#aes256-gcm, http://www.w3.org/2001/04/xmlenc#sha512" + }) + void testAgreementKeyEncryptDecryptData(String w3cTag, + String resourceHexFileName, + String keystoreFile, String keystoreType, + String passwd, String alias, + String keyWrapAlgorithm, + String encryptionAlgorithm, + String kdfAlgorithm) throws Exception { + Assumptions.assumeTrue(haveISOPadding, + "Skipping testAgreementKey ["+w3cTag+"] as necessary crypto algorithms are not available"); + + KeyStore keyStore = loadKeyStoreFromResource(keystoreFile, passwd, keystoreType); + Certificate cert = keyStore.getCertificate(alias); + + KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) + keyStore.getEntry(alias, new KeyStore.PasswordProtection(passwd.toCharArray())); + PrivateKey ecKey = pkEntry.getPrivateKey(); + + // Perform encryption + byte[] testData = hexFileContentByteArray(resourceHexFileName); + // create empty document for EncryptedKey + Document doc = DEFAULT_DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().newDocument(); + + Key sessionKey = getSessionKey(encryptionAlgorithm); + + + int keyBitLen = KeyUtils.getAESKeyBitSizeForWrapAlgorithm(keyWrapAlgorithm); + KeyDerivationParameters keyDerivationParameter = new ConcatKDFParams(keyBitLen, kdfAlgorithm); + AlgorithmParameterSpec parameterSpec = new KeyAgreementParameters( + KeyAgreementParameters.ActorType.ORIGINATOR, + EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES, + keyDerivationParameter); + + + EncryptedKey encryptedKey = + createEncryptedKey( + doc, + (X509Certificate)cert, + sessionKey, + keyWrapAlgorithm, + parameterSpec, null); + + + doc = encryptData( + doc, + encryptedKey, + sessionKey, + encryptionAlgorithm, + new ByteArrayInputStream(testData) + ); + + Files.write(Paths.get("target","test-enc-"+w3cTag+".xml"), toString(doc.getFirstChild()).getBytes()); + // Perform decryption + byte[] result = decryptData(doc, ecKey, (X509Certificate)cert); + // XMLUtils.outputDOM(dd.getFirstChild(), System.out); + assertNotNull(result); + assertArrayEquals(testData, result); + } + + + /** + * The KeyAgreement test cases from the W3C test suite using bytearray as input. + * + * https://www.w3.org/2008/xmlsec/Drafts/xmlenc-core-11/test-cases/#sec-KeyAgreement + */ + @ParameterizedTest + @CsvSource({ + "AGRMNT.4-dec, binary-data.hex, cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-4.xml, EC-P256.pfx, PKCS12, 1234, certreq-5b4623c8-5790-4b32-b59f-540c8bcfda4a", + "AGRMNT.5-dec, binary-data.hex, cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-5.xml, EC-P384.pfx, PKCS12, 1234, certreq-4c7c6242-e408-4391-a7e7-1a87a2ef2ba8", + "AGRMNT.6-dec, binary-data.hex, cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-6.xml, EC-P521.pfx, PKCS12, 1234, certreq-61afb173-5eab-475a-8c54-0cb792c82820" + }) + void testAgreementKeyDecryptData(String w3cTag, + String resourceHexFileName, + String decryptResourceData, + String keystoreFile, String keystoreType, + String passwd, String alias) throws Exception { + Assumptions.assumeTrue(haveISOPadding, + "Skipping testAgreementKey ["+w3cTag+"] as necessary crypto algorithms are not available"); + + KeyStore keyStore = loadKeyStoreFromResource(keystoreFile, passwd, keystoreType); + Certificate cert = keyStore.getCertificate(alias); + + KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) + keyStore.getEntry(alias, new KeyStore.PasswordProtection(passwd.toCharArray())); + PrivateKey ecKey = pkEntry.getPrivateKey(); + + // Perform encryption + byte[] testData = hexFileContentByteArray(resourceHexFileName); + + // get encrypted data from test page + Document doc = loadDocumentFromResource(decryptResourceData); + + // Perform decryption + byte[] result = decryptData(doc, ecKey, (X509Certificate)cert); + // compare results + assertNotNull(result); + assertArrayEquals(testData, result); + } + private KeyStore loadKeyStore(File keystore) - throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException { - KeyStore keyStore = KeyStore.getInstance("jks"); + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + KeyStore keyStore = KeyStore.getInstance(getKeystoreTypeForFileName(keystore.getName())); try (FileInputStream inputStream = new FileInputStream(keystore)) { keyStore.load(inputStream, "passwd".toCharArray()); } return keyStore; } + private KeyStore loadKeyStoreFromResource(String filename, String passwd, String keystoreType) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + + KeyStore keyStore = KeyStore.getInstance(keystoreType); + try ( InputStream keystoreIS = getResourceInputStream(filename);) { + keyStore.load(keystoreIS, passwd!=null?passwd.toCharArray():null); + } + return keyStore; + } + + private Document loadDocumentFromResource(String resourceName) + throws IOException, XMLParserException { + + try (InputStream dataXMLInputStream = getResourceInputStream(resourceName)){ + return XMLUtils.read(dataXMLInputStream, false); + } + } + + private String getKeystoreTypeForFileName(String filename){ + return filename.toLowerCase().endsWith(".p12") ? "PKCS12" : "JKS"; + } + /** * Method decryptElement * * Take a key, encryption type and a file, find an encrypted element * decrypt it and return the resulting document * - * @param filename File to decrypt from - * @param key The Key to use for decryption + * @param decKey The Key to use for decryption + * @param encCert The certificate used to encrypt the key */ - private Document decryptElement(File file, Key rsaKey, X509Certificate rsaCert) throws Exception { + private Document decryptElement(File file, Key decKey, X509Certificate encCert) throws Exception { // Parse the document in question Document doc; try (FileInputStream inputStream = new FileInputStream(file)) { doc = XMLUtils.read(inputStream, false); } - return decryptElement(doc, rsaKey, rsaCert); + return decryptElement(doc, decKey, encCert); } /** @@ -552,10 +802,11 @@ private Document decryptElement(File file, Key rsaKey, X509Certificate rsaCert) * Take a key, encryption type and a document, find an encrypted element * decrypt it and return the resulting document * - * @param filename File to decrypt from - * @param key The Key to use for decryption + * @param doc the XML document wrrapping the encrypted data + * @param decKey The Key to use for decryption + * @param encCert The certificate used to encrypt the key */ - private Document decryptElement(Document doc, Key rsaKey, X509Certificate rsaCert) throws Exception { + private Document decryptElement(Document doc, Key decKey, X509Certificate encCert) throws Exception { // Create the XMLCipher element XMLCipher cipher = XMLCipher.getInstance(); @@ -566,14 +817,18 @@ private Document decryptElement(Document doc, Key rsaKey, X509Certificate rsaCer KeyInfo ki = encryptedData.getKeyInfo(); EncryptedKey encryptedKey = ki.itemEncryptedKey(0); - KeyInfo kiek = encryptedKey.getKeyInfo(); + KeyInfoEnc kiek = (KeyInfoEnc)encryptedKey.getKeyInfo(); + if (kiek.containsAgreementMethod()){ + AgreementMethod agreementMethod = kiek.itemAgreementMethod(0); + kiek = agreementMethod.getRecipientKeyInfo(); + } X509Data certData = kiek.itemX509Data(0); XMLX509Certificate xcert = certData.itemCertificate(0); X509Certificate cert = xcert.getX509Certificate(); - assertEquals(rsaCert, cert); + assertEquals(encCert, cert); XMLCipher cipher2 = XMLCipher.getInstance(); - cipher2.init(XMLCipher.UNWRAP_MODE, rsaKey); + cipher2.init(XMLCipher.UNWRAP_MODE, decKey); Key key = cipher2.decryptKey(encryptedKey, encryptedData.getEncryptionMethod().getAlgorithm()); cipher.init(XMLCipher.DECRYPT_MODE, key); @@ -582,6 +837,46 @@ private Document decryptElement(Document doc, Key rsaKey, X509Certificate rsaCer return dd; } + /** + * Method decryptElement + * + * Take a key, encryption type and a document, find an encrypted element + * decrypt it and return the resulting document + * + * @param doc the XML document wrrapping the encrypted data + * @param decKey The Key to use for decryption + * @param rsaCert The certificate used to encrypt the key + * + */ + private byte[] decryptData(Document doc, Key decKey, X509Certificate rsaCert) throws Exception { + // Create the XMLCipher element + XMLCipher cipher = XMLCipher.getInstance(); + + // Need to pre-load the Encrypted Data so we can get the key info + Element ee = (Element) doc.getElementsByTagNameNS("http://www.w3.org/2001/04/xmlenc#", "EncryptedData").item(0); + cipher.init(XMLCipher.DECRYPT_MODE, null); + EncryptedData encryptedData = cipher.loadEncryptedData(doc, ee); + + KeyInfo ki = encryptedData.getKeyInfo(); + EncryptedKey encryptedKey = ki.itemEncryptedKey(0); + KeyInfoEnc kiek = (KeyInfoEnc)encryptedKey.getKeyInfo(); + if (kiek.containsAgreementMethod()){ + AgreementMethod agreementMethod = kiek.itemAgreementMethod(0); + kiek = agreementMethod.getRecipientKeyInfo(); + } + X509Data certData = kiek.itemX509Data(0); + XMLX509Certificate xcert = certData.itemCertificate(0); + X509Certificate cert = xcert.getX509Certificate(); + assertEquals(rsaCert, cert); + + XMLCipher cipher2 = XMLCipher.getInstance(); + cipher2.init(XMLCipher.UNWRAP_MODE, decKey); + Key key = cipher2.decryptKey(encryptedKey, encryptedData.getEncryptionMethod().getAlgorithm()); + + cipher.init(XMLCipher.DECRYPT_MODE, key); + return cipher.decryptToByteArray(ee); + } + /** * Create an EncryptedKey object using the given parameters. */ @@ -628,6 +923,39 @@ private EncryptedKey createEncryptedKey( return encryptedKey; } + private EncryptedKey createEncryptedKey( + Document doc, + X509Certificate cert, + Key sessionKey, + String encryptionMethod, + AlgorithmParameterSpec params, + SecureRandom random + ) throws Exception { + // Create the XMLCipher element + XMLCipher cipher = XMLCipher.getInstance(encryptionMethod, null, null); + + cipher.init(XMLCipher.WRAP_MODE, cert.getPublicKey()); + + EncryptedKey encryptedKey = cipher.encryptKey(doc, sessionKey, params, random); + + KeyInfo builderKeyInfo = encryptedKey.getKeyInfo(); + if (builderKeyInfo == null) { + builderKeyInfo = new KeyInfoEnc(doc); + encryptedKey.setKeyInfo(builderKeyInfo); + } + + X509Data x509Data = new X509Data(doc); + x509Data.addCertificate(cert); + if (builderKeyInfo instanceof KeyInfoEnc + && ((KeyInfoEnc)builderKeyInfo).lengthAgreementMethod()>0) { + AgreementMethod agreementMethod = ((KeyInfoEnc)builderKeyInfo).itemAgreementMethod(0); + agreementMethod.getRecipientKeyInfo().add(x509Data); + } else { + builderKeyInfo.add(x509Data); + } + return encryptedKey; + } + /** * Generate a session key using the given algorithm */ @@ -671,6 +999,36 @@ private Document encryptDocument( } + /** + * Encrypt a Document using the given parameters. + */ + private Document encryptData( + Document doc, + EncryptedKey encryptedKey, + Key sessionKey, + String encryptionMethod, + InputStream dataToEncrypt + ) throws Exception { + // Create the XMLCipher element + XMLCipher cipher = XMLCipher.getInstance(encryptionMethod); + + cipher.init(XMLCipher.ENCRYPT_MODE, sessionKey); + EncryptedData builder = cipher.getEncryptedData(); + + KeyInfo builderKeyInfo = builder.getKeyInfo(); + if (builderKeyInfo == null) { + builderKeyInfo = new KeyInfo(doc); + builder.setKeyInfo(builderKeyInfo); + } + + builderKeyInfo.add(encryptedKey); + + EncryptedData endData = cipher.encryptData(doc, null, dataToEncrypt); + Element encDataElement = cipher.martial(endData); + doc.appendChild(encDataElement); + return doc; + } + /** * Method countNodes * @@ -746,4 +1104,39 @@ private void checkDecryptedDoc(Document d, boolean doNodeCheck) throws Exception } } -} \ No newline at end of file + public static byte[] hexFileContentByteArray(String fileName) throws IOException { + byte[] data; + try (InputStream is = getResourceInputStream(fileName)) { + int l = is.available() / 2; + data = new byte[l]; + byte[] charByte = new byte[2]; + for (int i = 0; i < l; i++) { + is.read(charByte); + data[i] = (byte) ((Character.digit(charByte[0], 16) << 4) + + Character.digit(charByte[1], 16)); + } + } + return data; + } + + /** + * Method returns a resource input stream object from resources folder '/org/w3c/www/interop/xmlenc-core-11/' + * @param resourceName name of the resource file + * @return InputStream object or null if resource not found + */ + public static InputStream getResourceInputStream(String resourceName) { + return XMLEncryption11Test.class.getResourceAsStream(RESOURCE_FOLDER + resourceName); + } + + + private String toString (Node n) throws Exception { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS); + + c14n.canonicalizeSubtree(n, baos); + baos.flush(); + + return baos.toString(StandardCharsets.UTF_8); + } + } +} diff --git a/src/test/java/org/apache/xml/security/test/dom/keys/DEREncodedKeyValueTest.java b/src/test/java/org/apache/xml/security/test/dom/keys/DEREncodedKeyValueTest.java index 6ac1bc838..2b583b6a0 100644 --- a/src/test/java/org/apache/xml/security/test/dom/keys/DEREncodedKeyValueTest.java +++ b/src/test/java/org/apache/xml/security/test/dom/keys/DEREncodedKeyValueTest.java @@ -20,24 +20,23 @@ import java.nio.file.Files; import java.nio.file.Path; -import java.security.KeyFactory; -import java.security.PublicKey; +import java.security.*; import java.security.spec.X509EncodedKeySpec; import org.apache.xml.security.keys.content.DEREncodedKeyValue; import org.apache.xml.security.test.XmlSecTestEnvironment; import org.apache.xml.security.test.dom.TestUtils; +import org.apache.xml.security.testutils.JDKTestUtils; import org.apache.xml.security.utils.Constants; import org.apache.xml.security.utils.XMLUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class DEREncodedKeyValueTest { @@ -47,11 +46,26 @@ class DEREncodedKeyValueTest { private final PublicKey rsaKeyControl; private final PublicKey dsaKeyControl; private final PublicKey ecKeyControl; + private final PublicKey edecKeyControl; + private final PublicKey xecKeyControl; + private final PublicKey dhKeyControl; + private final PublicKey rsaSsaPssKeyControl; public DEREncodedKeyValueTest() throws Exception { rsaKeyControl = loadPublicKey("rsa.key", "RSA"); dsaKeyControl = loadPublicKey("dsa.key", "DSA"); ecKeyControl = loadPublicKey("ec.key", "EC"); + edecKeyControl = loadPublicKey("ed25519.key", "EdDSA"); + xecKeyControl = loadPublicKey("x25519.key", "XDH"); + dhKeyControl = loadPublicKey("dh.key", "DiffieHellman"); + rsaSsaPssKeyControl = loadPublicKey("rsassa-pss.key", "RSASSA-PSS"); + } + + @AfterEach + void cleanup() { + if (JDKTestUtils.isAuxiliaryProviderRegistered()) { + JDKTestUtils.unregisterAuxiliaryProvider(); + } } @Test @@ -99,6 +113,66 @@ void testECPublicKeyFromElement() throws Exception { assertEquals(ID_CONTROL, derEncodedKeyValue.getId()); } + @Test + void testEdECPublicKeyFromElement() throws Exception { + Assumptions.assumeTrue(edecKeyControl != null, "Skip tests since EdEC Key is not supported"); + if (!JDKTestUtils.isAlgorithmSupportedByJDK(edecKeyControl.getAlgorithm())) { + JDKTestUtils.registerAuxiliaryProvider(); + } + + Document doc = loadXML("DEREncodedKeyValue-EdEC.xml"); + NodeList nl = doc.getElementsByTagNameNS(Constants.SignatureSpec11NS, Constants._TAG_DERENCODEDKEYVALUE); + Element element = (Element) nl.item(0); + + DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(element, ""); + assertEquals(edecKeyControl, derEncodedKeyValue.getPublicKey()); + assertArrayEquals(edecKeyControl.getEncoded(), derEncodedKeyValue.getBytesFromTextChild()); + assertEquals(ID_CONTROL, derEncodedKeyValue.getId()); + } + + @Test + void testXECPublicKeyFromElement() throws Exception { + Assumptions.assumeTrue(xecKeyControl != null, "Skip tests since XEC Key is not supported"); + String algorithm = xecKeyControl.getAlgorithm(); + if (!JDKTestUtils.isAlgorithmSupportedByJDK(algorithm)) { + JDKTestUtils.registerAuxiliaryProvider(); + } + + Document doc = loadXML("DEREncodedKeyValue-XEC.xml"); + NodeList nl = doc.getElementsByTagNameNS(Constants.SignatureSpec11NS, Constants._TAG_DERENCODEDKEYVALUE); + Element element = (Element) nl.item(0); + + DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(element, ""); + assertNotNull(xecKeyControl.getAlgorithm(), derEncodedKeyValue.getPublicKey().getAlgorithm()); + assertEquals(ID_CONTROL, derEncodedKeyValue.getId()); + } + + @Test + void testDiffieHellmanPublicKeyFromElement() throws Exception { + Assumptions.assumeTrue(dhKeyControl != null, "Skip tests since DH Key is not supported"); + + Document doc = loadXML("DEREncodedKeyValue-DH.xml"); + NodeList nl = doc.getElementsByTagNameNS(Constants.SignatureSpec11NS, Constants._TAG_DERENCODEDKEYVALUE); + Element element = (Element) nl.item(0); + + DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(element, ""); + assertNotNull(dhKeyControl.getAlgorithm(), derEncodedKeyValue.getPublicKey().getAlgorithm()); + assertEquals(ID_CONTROL, derEncodedKeyValue.getId()); + } + + @Test + void testRsaSsaPssKeyControlPublicKeyFromElement() throws Exception { + Assumptions.assumeTrue(rsaSsaPssKeyControl != null, "Skip tests since RSASSA-PSS Key is not supported"); + + Document doc = loadXML("DEREncodedKeyValue-RSASSA-PSS.xml"); + NodeList nl = doc.getElementsByTagNameNS(Constants.SignatureSpec11NS, Constants._TAG_DERENCODEDKEYVALUE); + Element element = (Element) nl.item(0); + + DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(element, ""); + assertNotNull(rsaSsaPssKeyControl.getAlgorithm(), derEncodedKeyValue.getPublicKey().getAlgorithm()); + assertEquals(ID_CONTROL, derEncodedKeyValue.getId()); + } + @Test void testRSAPublicKeyFromKey() throws Exception { DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(TestUtils.newDocument(), rsaKeyControl); @@ -120,6 +194,29 @@ void testECPublicKeyFromKey() throws Exception { assertArrayEquals(ecKeyControl.getEncoded(), derEncodedKeyValue.getBytesFromTextChild()); } + @Test + void testEdECPublicKeyFromKey() throws Exception { + Assumptions.assumeTrue(edecKeyControl != null, "Skip tests since EdEC Key is not supported"); + if (!JDKTestUtils.isAlgorithmSupportedByJDK(edecKeyControl.getAlgorithm())) { + JDKTestUtils.registerAuxiliaryProvider(); + } + + DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(TestUtils.newDocument(), edecKeyControl); + assertEquals(edecKeyControl, derEncodedKeyValue.getPublicKey()); + assertArrayEquals(edecKeyControl.getEncoded(), derEncodedKeyValue.getBytesFromTextChild()); + } + + @Test + void testXECPublicKeyFromKey() throws Exception { + Assumptions.assumeTrue(xecKeyControl != null, "Skip tests since XEC Key is not supported"); + if (!JDKTestUtils.isAlgorithmSupportedByJDK(xecKeyControl.getAlgorithm())) { + JDKTestUtils.registerAuxiliaryProvider(); + } + + DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(TestUtils.newDocument(), xecKeyControl); + assertArrayEquals(xecKeyControl.getEncoded(), derEncodedKeyValue.getBytesFromTextChild()); + } + @Test void testId() throws Exception { DEREncodedKeyValue derEncodedKeyValue = new DEREncodedKeyValue(TestUtils.newDocument(), rsaKeyControl); @@ -149,9 +246,14 @@ private Document loadXML(String fileName) throws Exception { private PublicKey loadPublicKey(String fileName, String algorithm) throws Exception { String fileData = Files.readString(getControlFilePath(fileName)); byte[] keyBytes = XMLUtils.decode(fileData); - KeyFactory kf = KeyFactory.getInstance(algorithm); + if (!JDKTestUtils.isAlgorithmSupported(algorithm, true)) { + return null; + } + Provider provider = JDKTestUtils.isAlgorithmSupportedByJDK(algorithm) ? null : JDKTestUtils.getAuxiliaryProvider(); + + KeyFactory kf = provider == null? + KeyFactory.getInstance(algorithm) : KeyFactory.getInstance(algorithm, provider); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); return kf.generatePublic(keySpec); } - -} \ No newline at end of file +} diff --git a/src/test/java/org/apache/xml/security/testutils/JDKTestUtils.java b/src/test/java/org/apache/xml/security/testutils/JDKTestUtils.java new file mode 100644 index 000000000..f7254b6da --- /dev/null +++ b/src/test/java/org/apache/xml/security/testutils/JDKTestUtils.java @@ -0,0 +1,147 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.testutils; + +import java.lang.System.Logger.Level; +import java.lang.reflect.Constructor; +import java.security.Provider; +import java.security.Security; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +/** + * The class provides testing utility methods to test XMLSEC functionality with various JDK version. Where possible + * we use JDK provided algorithm implementations. However, some algorithms are not supported in lower JDK versions. For example + * XDH keys were supported from JDK 11, EdDSA keys from JDK 16, etc. To ensure tests are executed for various JDK versions, + * we need to know which algorithms are supported from particular JDK version. + * + * If the JDK security providers do not support algorithm, the class provides auxiliary security provider (BouncyCastle) to the test + * xmlsec functionality ... + * + */ +public class JDKTestUtils { + + + // Purpose of auxiliary security provider is to enable testing of algorithms not supported by default JDK security providers. + private static final String TEST_PROVIDER_CLASSNAME_PROPERTY = "test.auxiliary.jce.provider.classname"; + private static final String TEST_PROVIDER_CLASSNAME_DEFAULT = "org.bouncycastle.jce.provider.BouncyCastleProvider"; + private static final System.Logger LOG = System.getLogger(JDKTestUtils.class.getName()); + + private static Provider auxiliaryProvider; + private static boolean auxiliaryProviderInitialized = false; + private static Set supportedAuxiliaryProviderAlgorithms = null; + + private static final Map javaAlgSupportFrom = Stream.of( + new AbstractMap.SimpleImmutableEntry<>("eddsa", 16), + new AbstractMap.SimpleImmutableEntry<>("ed25519", 16), + new AbstractMap.SimpleImmutableEntry<>("ed448", 16), + new AbstractMap.SimpleImmutableEntry<>("xdh", 11), + new AbstractMap.SimpleImmutableEntry<>("x25519", 11), + new AbstractMap.SimpleImmutableEntry<>("x448", 11)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + private static final Set SUPPORTED_ALGORITHMS = Stream.of(Security.getProviders()) + .flatMap(provider -> provider.getServices().stream()) + .map(Provider.Service::getAlgorithm) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + + + public static int getJDKVersion() { + try { + return Integer.getInteger("java.specification.version", 0); + } catch (NumberFormatException ex) { + // ignore + } + return 0; + } + + public static synchronized Provider getAuxiliaryProvider() { + if (auxiliaryProviderInitialized) { + return auxiliaryProvider; + } + try { + String providerClassName = System.getProperty(TEST_PROVIDER_CLASSNAME_PROPERTY, TEST_PROVIDER_CLASSNAME_DEFAULT); + LOG.log(Level.INFO, "Initialize the auxiliary security provider: [{0}]", providerClassName); + Class c = Class.forName(providerClassName); + Constructor cons = c.getConstructor(); + auxiliaryProvider = (Provider)cons.newInstance(); + supportedAuxiliaryProviderAlgorithms = auxiliaryProvider.getServices().stream() + .map(Provider.Service::getAlgorithm) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to initialize the auxiliary security provider: [{0}]", e.getMessage()); + } + auxiliaryProviderInitialized = true; + return auxiliaryProvider; + } + + public static void registerAuxiliaryProvider() { + // init provider if needed + Provider provider = getAuxiliaryProvider(); + if (provider == null) { + LOG.log(Level.WARNING, "Auxiliary security provider is not initialized. Cannot register it."); + return; + } + Security.addProvider(provider); + } + + public static void unregisterAuxiliaryProvider() { + if (auxiliaryProvider == null) { + LOG.log(Level.DEBUG, "Auxiliary security provider is not initialized. Cannot unregister it."); + return; + } + LOG.log(Level.DEBUG, "Unregister auxiliary security provider [{0}]", auxiliaryProvider.getName()); + Security.removeProvider(auxiliaryProvider.getName()); + } + + public static boolean isAuxiliaryProviderRegistered() { + return auxiliaryProvider!=null && Security.getProvider(auxiliaryProvider.getName())!=null ; + } + + + public static boolean isAlgorithmSupported(String algorithm, boolean useAuxiliaryProvider) { + String alg = algorithm.toLowerCase(); + int iJDKVersion = getJDKVersion(); + if (javaAlgSupportFrom.containsKey(alg) + && javaAlgSupportFrom.get(alg) <= iJDKVersion + || SUPPORTED_ALGORITHMS.contains(alg)) { + LOG.log(Level.DEBUG, "Algorithm [{0}] is supported by JDK version [{1}]", alg, iJDKVersion); + return true; + } + Provider provider = getAuxiliaryProvider(); + if (useAuxiliaryProvider + && provider!=null + && supportedAuxiliaryProviderAlgorithms.contains(alg)){ + LOG.log(Level.DEBUG, "Algorithm [{0}] is supported by auxiliary Provider [{1}].", + alg, provider.getName()); + return true; + } + // double check in all supported algorithms ... + LOG.log(Level.INFO, "Algorithm [{0}] is NOT supported!", alg); + return false; + } + + public static boolean isAlgorithmSupportedByJDK(String algorithm) { + return isAlgorithmSupported(algorithm, false); + } +} diff --git a/src/test/java/org/apache/xml/security/testutils/KeyTestUtils.java b/src/test/java/org/apache/xml/security/testutils/KeyTestUtils.java new file mode 100644 index 000000000..8e9090fac --- /dev/null +++ b/src/test/java/org/apache/xml/security/testutils/KeyTestUtils.java @@ -0,0 +1,141 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.testutils; + +import org.apache.xml.security.utils.KeyUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +import static java.lang.System.Logger.Level.DEBUG; + + +/** + * The class provides testing utility methods to test XMLSEC functionality with various JDK version. Where possible + * we use JDK provided algorithm implementations. However, some algorithms are not supported in lower JDK versions. For example + * XDH keys were supported from JDK 11, EdDSA keys from JDK 16, etc. To ensure tests are executed for various JDK versions, + * we need to know which algorithms are supported from particular JDK version. + * + * If the existing JDK security providers do not support the algorithm, the class provides auxiliary security provider (BouncyCastle) to the test + * xmlsec functionality. + * + */ +public class KeyTestUtils { + private static final System.Logger LOG = System.getLogger(KeyTestUtils.class.getName()); + /** + * The enum of the prepared test keys in resource folder KEY_RESOURCE_PATH. + */ + public enum TestKeys { + DSA("dsa.key", "DSA", "1.2.840.10040.4.1"), + RSA("rsa.key", "RSA", "1.2.840.113549.1.1.1"), + EC("ec.key", "EC", "1.2.840.10045.2.1"), + X25519("x25519.key", "XDH", "1.3.101.110"), + ED25519("ed25519.key", "EdDSA", "1.3.101.112"); + + private final String filename; + private final String algorithm; + private final String oid; + + TestKeys(String filename, String algorithm, String oid) { + this.filename = filename; + this.algorithm = algorithm; + this.oid = oid; + } + + public String getFilename() { + return filename; + } + + public String getAlgorithm() { + return algorithm; + } + + public String getOid() { + return oid; + } + } + + private static final String KEY_RESOURCE_PATH = "/org/apache/xml/security/keys/content/"; + + public static KeyPair generateKeyPair(KeyUtils.KeyType keyType) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidParameterSpecException { + String keyAlgorithm = keyType.getAlgorithm().getJceName(); + Provider provider = JDKTestUtils.isAlgorithmSupportedByJDK(keyAlgorithm) ? null : JDKTestUtils.getAuxiliaryProvider(); + KeyPairGenerator keyPairGenerator; + + switch (keyType.getAlgorithm()){ + case EC:{ + keyPairGenerator = provider == null ? KeyPairGenerator.getInstance(keyAlgorithm) : + KeyPairGenerator.getInstance(keyAlgorithm, provider); + ECGenParameterSpec kpgparams = new ECGenParameterSpec(keyType.getName()); + keyPairGenerator.initialize(kpgparams); + break; + } + case DSA: + case RSA: + case RSASSA_PSS: + case EdDSA: + case DH: + case XDH:{ + keyPairGenerator = provider == null ? KeyPairGenerator.getInstance(keyType.getName()) : + KeyPairGenerator.getInstance(keyType.getName(), provider); + break; + } + default: + throw new IllegalStateException("Unexpected value: " + keyAlgorithm); + + } + return keyPairGenerator.generateKeyPair(); + } + + public static KeyPair generateKeyPairIfSupported(KeyUtils.KeyType keyType){ + KeyPair keyPair = null; + try { + keyPair = generateKeyPair(keyType); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidParameterSpecException e) { + LOG.log(DEBUG, "Key algorithm [{0}] is not supported! Error message: [{1}]", keyType, e.getMessage()); + } + return keyPair; + } + + public static PublicKey loadPublicKey(String keyName, String algorithm) throws Exception { + byte[] keyBytes = getKeyResourceAsByteArray(keyName); + KeyFactory kf = JDKTestUtils.isAlgorithmSupportedByJDK(algorithm) ? + KeyFactory.getInstance(algorithm) : KeyFactory.getInstance(algorithm, JDKTestUtils.getAuxiliaryProvider()); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + return kf.generatePublic(keySpec); + } + + public static byte[] getKeyResourceAsByteArray(String fileName) throws IOException { + byte[] keyBytes; + try (InputStream keyIS = getKeyResourceAsInputStream(fileName)){ + keyBytes = new byte[keyIS.available()]; + keyIS.read(keyBytes); + } + return Base64.getMimeDecoder().decode(keyBytes); + } + + public static InputStream getKeyResourceAsInputStream(String fileName) { + return KeyTestUtils.class.getResourceAsStream(KEY_RESOURCE_PATH + fileName); + } +} diff --git a/src/test/java/org/apache/xml/security/utils/DERDecoderUtilsTest.java b/src/test/java/org/apache/xml/security/utils/DERDecoderUtilsTest.java new file mode 100644 index 000000000..69097ecc2 --- /dev/null +++ b/src/test/java/org/apache/xml/security/utils/DERDecoderUtilsTest.java @@ -0,0 +1,92 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.utils; + +import org.apache.xml.security.exceptions.DERDecodingException; +import org.apache.xml.security.testutils.JDKTestUtils; +import org.apache.xml.security.testutils.KeyTestUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class DERDecoderUtilsTest { + static { + org.apache.xml.security.Init.init(); + } + + @AfterEach + void tearDown() { + JDKTestUtils.unregisterAuxiliaryProvider(); + } + + @ParameterizedTest + @EnumSource(KeyTestUtils.TestKeys.class) + void testGetAlgorithmIdBytesFromKey(KeyTestUtils.TestKeys testKey) throws DERDecodingException, IOException { + + byte[] bytes; + try (InputStream keyIS = KeyTestUtils.getKeyResourceAsInputStream(testKey.getFilename()); + InputStream keyDecodedIS = Base64.getMimeDecoder().wrap(keyIS)){ + bytes = DERDecoderUtils.getAlgorithmIdBytes(keyDecodedIS); + } + + String oid = DERDecoderUtils.decodeOID(bytes); + assertNotNull(bytes); + assertEquals(testKey.getOid(), oid); + } + + @ParameterizedTest + @EnumSource(value = KeyUtils.KeyType.class) + void testGetAlgorithmIdBytesForGeneratedKeys(KeyUtils.KeyType testKey) throws DERDecodingException { + if (!JDKTestUtils.isAlgorithmSupportedByJDK(testKey.getAlgorithm().getJceName())) { + JDKTestUtils.registerAuxiliaryProvider(); + } + KeyPair keyPair = KeyTestUtils.generateKeyPairIfSupported(testKey); + Assumptions.assumeTrue(keyPair != null, "Key algorithm [" + testKey + "] not supported by JDK or auxiliary provider! Skipping test."); + + String oid = DERDecoderUtils.getAlgorithmIdFromPublicKey(keyPair.getPublic()); + assertEquals(testKey.getOid(), oid); + } + + @Test + void testKeyAlgorithmAndKeySpecificAlgorithm() throws DERDecodingException, IOException, NoSuchAlgorithmException, InvalidKeySpecException { + String keyBase64 = "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA/VlZpWEJjRyaFJV8abQQMxLNnaKc77MR4qFXZA3jruVRrJOzUFDD7UjcdA8FVciJY4AaEyVwdALtAM3kq4whzp4Apnp8mipZw/5VKRp3cciBr5q8A7sgPZ9qKd5RijvPsedYHIWKOjDKF0KrTx4TdnmhGR3iKqPtDSoXiRlvEYrqx9Y="; + byte[] publicKeyBytes = Base64.getDecoder().decode(keyBase64); + KeyFactory kf = KeyFactory.getInstance("EC"); // or "EC" or whatever + PublicKey publicKey = kf.generatePublic(new X509EncodedKeySpec(publicKeyBytes)); + byte[] oid = DERDecoderUtils.getAlgorithmIdBytes(new ByteArrayInputStream(publicKeyBytes)); + String keyAlgorithm = DERDecoderUtils.decodeOID(oid); + String keySpecificAlgorithm = DERDecoderUtils.getAlgorithmIdFromPublicKey(publicKey); + // expected algorithms + assertEquals(KeyUtils.KeyAlgorithmType.EC.getOid(), keyAlgorithm); + assertEquals(KeyUtils.KeyType.SECP521R1.getOid(), keySpecificAlgorithm); + } +} diff --git a/src/test/java/org/apache/xml/security/utils/KeyUtilsTest.java b/src/test/java/org/apache/xml/security/utils/KeyUtilsTest.java new file mode 100644 index 000000000..8e426a332 --- /dev/null +++ b/src/test/java/org/apache/xml/security/utils/KeyUtilsTest.java @@ -0,0 +1,87 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.xml.security.utils; + +import org.apache.xml.security.Init; +import org.apache.xml.security.testutils.JDKTestUtils; +import org.apache.xml.security.testutils.KeyTestUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.security.KeyPair; +import java.security.Provider; +import java.security.PublicKey; + +/** + * Unit test for {@link KeyUtils} + */ +class KeyUtilsTest { + static { + Init.init(); + } + + /** + * Test if the ephemeral key is generated with the same algorithm as the original key + * @param testKey the enumeration with exiting keys in resource folder + * @throws Exception if the key cannot be loaded + */ + @ParameterizedTest + @EnumSource(KeyTestUtils.TestKeys.class) + void generateEphemeralDHKeyPair(KeyTestUtils.TestKeys testKey) throws Exception { + String keyAlgorithm = testKey.getAlgorithm(); + Assumptions.assumeTrue(JDKTestUtils.isAlgorithmSupported(keyAlgorithm, true), "Algorithm [" + + keyAlgorithm + "] not supported by JDK or auxiliary provider! Skipping test."); + + PublicKey publicKey = KeyTestUtils.loadPublicKey(testKey.getFilename(), testKey.getAlgorithm()); + // when + KeyPair ephenmeralKeyPair = KeyUtils.generateEphemeralDHKeyPair(publicKey, JDKTestUtils.isAlgorithmSupportedByJDK(testKey.getAlgorithm()) ? + null : JDKTestUtils.getAuxiliaryProvider()); + // then + Assertions.assertNotNull(ephenmeralKeyPair); + Assertions.assertNotEquals(publicKey, ephenmeralKeyPair.getPublic()); + Assertions.assertEquals(publicKey.getAlgorithm(), ephenmeralKeyPair.getPublic().getAlgorithm()); + } + + /** + * Test if the ephemeral key is generated with the same algorithm as the original key. The initial keys are generated + * @param keyType the enumeration with most common EC and XEC keys + * @throws Exception if the key cannot be loaded + */ + @ParameterizedTest + @EnumSource(value = KeyUtils.KeyType.class) + void generateEphemeralDHKeyPair(KeyUtils.KeyType keyType) throws Exception { + + Provider testAuxiliaryProvider = JDKTestUtils.isAlgorithmSupportedByJDK(keyType.name()) ? null : JDKTestUtils.getAuxiliaryProvider(); + KeyPair keyPair = KeyTestUtils.generateKeyPairIfSupported(keyType); + Assumptions.assumeTrue(keyPair != null, "Key algorithm [" + keyType + "] not supported by JDK or auxiliary provider! Skipping test."); + + + // test DH key generation + KeyPair ephenmeralKeyPair = KeyUtils.generateEphemeralDHKeyPair(keyPair.getPublic(), testAuxiliaryProvider); + String ephemeralKeyOId = DERDecoderUtils.getAlgorithmIdFromPublicKey(ephenmeralKeyPair.getPublic()); + + // test if the ephemeral key is generated with the same algorithm as the original key + Assertions.assertNotNull(ephenmeralKeyPair); + Assertions.assertNotEquals(keyPair.getPublic(), ephenmeralKeyPair.getPublic()); + Assertions.assertEquals(keyPair.getPublic().getAlgorithm(), ephenmeralKeyPair.getPublic().getAlgorithm()); + Assertions.assertEquals(keyType.getOid(), ephemeralKeyOId); + } +} diff --git a/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-DH.xml b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-DH.xml new file mode 100644 index 000000000..688d13a2d --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-DH.xml @@ -0,0 +1,13 @@ + + +MIICKDCCARsGCSqGSIb3DQEDATCCAQwCggEBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiK +Z8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY3 +7WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7ORbPcIAfLihY78FmNpINhxV05ppFj+o/STP +X4NlXSPco62WHGLzViCFUrue1SkHcJaWbWcMNU5KvJgE8XRsCMoYIXwykF5GLjbOO+OedywYDoYD +myeDouwHoo+1xV3wb0xSyd4ry/aVWBcYOZVJfOqVauUV0iYYmPoFEBVyjlqKrKpo//////////8C +AQICAgDgA4IBBQACggEAXKjl1y9YiSWiTjc4cpHrl69VKckJluniOgxKl52b5n5Wg+uuDAQHw6GO +8+ZruzDUGfTxyLsw5ZKV9FW8Q2tYjGUANuokKhBx7TNc+wWwy8azLULhTuD75cRHxoCti7FDvg/a +klcJtBFu7Dm6udx2UgYjFSt08hYCinuTeORDC2UQ6/WWfuTLcgZjOItNLHw5QwXyt9v6YiVN/jRs +XjuFGZCxLZZzUv8Ow5gjqfdkXtXfs87ez16KcSZS/L66Q+GxbxevMwHVyYqNLSQIA/PK8tSxMZp1 +K97wH+Hynxq9bjAEUc9O1MkHYaSxHZhB7+Ig9GjU2AO0u9oVLq/fQ3z/Ug== + diff --git a/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-EdEC.xml b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-EdEC.xml new file mode 100644 index 000000000..b4d680a4e --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-EdEC.xml @@ -0,0 +1,7 @@ + + + +MCowBQYDK2VwAyEAqH13JH6hfyGxrQeHdy8YXSXKQU0jVSDNoo3RC14nU0w= + + + diff --git a/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-RSASSA-PSS.xml b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-RSASSA-PSS.xml new file mode 100644 index 000000000..69bd0cac3 --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-RSASSA-PSS.xml @@ -0,0 +1,9 @@ + + +MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBAMcmY5KhJ3jLWicJsGz/8OdyP8zq4PwRO8J1 +QVtD/m2barhNUiY5vWAXegfOi/SS8jdUlwem6g/iJ8OzcEyTYiG44aovL1kuJjNg6F2LDfoNRtR5 +zYKNRxYs/+pGRgmDABZNsxFByRlu487J7a9XD7uaBC3JvbGPd+BLfjGKNkKGtoIguRRQUKHdUOXK +DHyWGdKGYvGkD4kZV7CnYDHtBXgs6GClyR7XaYdlvQ0haI8T6WL9pc//YuFYuSbhSouwPW+Zedua +5tDTYylMbxWpYIcpfHk3xgQNwPBMuwX6pq941AqMpgwiyg197jRHJpNGW2jmHIqt14ReujQJGQPV +Q8kCAwEAAQ== + diff --git a/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-XEC.xml b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-XEC.xml new file mode 100644 index 000000000..262b11d1a --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/DEREncodedKeyValue-XEC.xml @@ -0,0 +1,7 @@ + + + +MCowBQYDK2VuAyEA5qmpaxEkMbX1AZid5NLABb/nlCViLeazazCrNStnmjs= + + + diff --git a/src/test/resources/org/apache/xml/security/keys/content/dh.key b/src/test/resources/org/apache/xml/security/keys/content/dh.key new file mode 100644 index 000000000..3a665ff51 --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/dh.key @@ -0,0 +1,10 @@ +MIICKDCCARsGCSqGSIb3DQEDATCCAQwCggEBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiK +Z8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY3 +7WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7ORbPcIAfLihY78FmNpINhxV05ppFj+o/STP +X4NlXSPco62WHGLzViCFUrue1SkHcJaWbWcMNU5KvJgE8XRsCMoYIXwykF5GLjbOO+OedywYDoYD +myeDouwHoo+1xV3wb0xSyd4ry/aVWBcYOZVJfOqVauUV0iYYmPoFEBVyjlqKrKpo//////////8C +AQICAgDgA4IBBQACggEAXKjl1y9YiSWiTjc4cpHrl69VKckJluniOgxKl52b5n5Wg+uuDAQHw6GO +8+ZruzDUGfTxyLsw5ZKV9FW8Q2tYjGUANuokKhBx7TNc+wWwy8azLULhTuD75cRHxoCti7FDvg/a +klcJtBFu7Dm6udx2UgYjFSt08hYCinuTeORDC2UQ6/WWfuTLcgZjOItNLHw5QwXyt9v6YiVN/jRs +XjuFGZCxLZZzUv8Ow5gjqfdkXtXfs87ez16KcSZS/L66Q+GxbxevMwHVyYqNLSQIA/PK8tSxMZp1 +K97wH+Hynxq9bjAEUc9O1MkHYaSxHZhB7+Ig9GjU2AO0u9oVLq/fQ3z/Ug== \ No newline at end of file diff --git a/src/test/resources/org/apache/xml/security/keys/content/ed25519.key b/src/test/resources/org/apache/xml/security/keys/content/ed25519.key new file mode 100644 index 000000000..f4e98aa65 --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/ed25519.key @@ -0,0 +1 @@ +MCowBQYDK2VwAyEAqH13JH6hfyGxrQeHdy8YXSXKQU0jVSDNoo3RC14nU0w= \ No newline at end of file diff --git a/src/test/resources/org/apache/xml/security/keys/content/rsassa-pss.key b/src/test/resources/org/apache/xml/security/keys/content/rsassa-pss.key new file mode 100644 index 000000000..77f535d1e --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/rsassa-pss.key @@ -0,0 +1,6 @@ +MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBAMcmY5KhJ3jLWicJsGz/8OdyP8zq4PwRO8J1 +QVtD/m2barhNUiY5vWAXegfOi/SS8jdUlwem6g/iJ8OzcEyTYiG44aovL1kuJjNg6F2LDfoNRtR5 +zYKNRxYs/+pGRgmDABZNsxFByRlu487J7a9XD7uaBC3JvbGPd+BLfjGKNkKGtoIguRRQUKHdUOXK +DHyWGdKGYvGkD4kZV7CnYDHtBXgs6GClyR7XaYdlvQ0haI8T6WL9pc//YuFYuSbhSouwPW+Zedua +5tDTYylMbxWpYIcpfHk3xgQNwPBMuwX6pq941AqMpgwiyg197jRHJpNGW2jmHIqt14ReujQJGQPV +Q8kCAwEAAQ== \ No newline at end of file diff --git a/src/test/resources/org/apache/xml/security/keys/content/x25519.key b/src/test/resources/org/apache/xml/security/keys/content/x25519.key new file mode 100644 index 000000000..01c6fa276 --- /dev/null +++ b/src/test/resources/org/apache/xml/security/keys/content/x25519.key @@ -0,0 +1 @@ +MCowBQYDK2VuAyEA5qmpaxEkMbX1AZid5NLABb/nlCViLeazazCrNStnmjs= \ No newline at end of file diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/DH-1024_SHA256WithDSA.p12 b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/DH-1024_SHA256WithDSA.p12 new file mode 100644 index 000000000..3d8682a73 Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/DH-1024_SHA256WithDSA.p12 differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256.pfx b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256.pfx new file mode 100644 index 000000000..432b0b351 Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256.pfx differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256_SHA256WithECDSA-v02.p12 b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256_SHA256WithECDSA-v02.p12 new file mode 100644 index 000000000..8de4f3a12 Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256_SHA256WithECDSA-v02.p12 differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256_SHA256WithECDSA.p12 b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256_SHA256WithECDSA.p12 new file mode 100644 index 000000000..1146d744e Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P256_SHA256WithECDSA.p12 differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384.pfx b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384.pfx new file mode 100644 index 000000000..e79f1ece0 Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384.pfx differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384_SHA256WithECDSA-v02.p12 b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384_SHA256WithECDSA-v02.p12 new file mode 100644 index 000000000..f16e2b8aa Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384_SHA256WithECDSA-v02.p12 differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384_SHA256WithECDSA.p12 b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384_SHA256WithECDSA.p12 new file mode 100644 index 000000000..3909e9d6a Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P384_SHA256WithECDSA.p12 differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521.pfx b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521.pfx new file mode 100644 index 000000000..a9c0798b6 Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521.pfx differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521_SHA256WithECDSA-v02.p12 b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521_SHA256WithECDSA-v02.p12 new file mode 100644 index 000000000..df85fcf9c Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521_SHA256WithECDSA-v02.p12 differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521_SHA256WithECDSA.p12 b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521_SHA256WithECDSA.p12 new file mode 100644 index 000000000..ff986e14d Binary files /dev/null and b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/EC-P521_SHA256WithECDSA.p12 differ diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/README.md b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/README.md new file mode 100644 index 000000000..196ab9390 --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/README.md @@ -0,0 +1,31 @@ +Test artifacts for XML Encryption 1.1 Core Interoperability Test Suite +================================================== + +This folder contains test artifacts from the page Test cases for XML Encryption 1.1 +https://www.w3.org/2008/xmlsec/Drafts/xmlenc-core-11/test-cases/#sec-KeyAgreement + +Artefacts are used in the Test class org.apache.xml.security.test.dom.encryption.XMLEncryption11Test + + + +### Script to regenerate the keystores +Some of the keystores are not recognized by up-to-date java keytool and JCP keystore provider. +The following script can be used to regenerate with openssl so that it can be used with latest java security policy + + +```bash +#!/usr/bin/env bash + +FILENAME=$1 +PASSPHRASE="passwd" +# Regenerate a PKCS12 with up-to-date encryption algorithm so that it can be used with latest java security policy +echo "Regenerating PKCS12 file $FILENAME.p12 to $FILENAME-v02.p12" +openssl pkcs12 -in "${FILENAME}.p12" -passin pass:${PASSPHRASE} -out "${FILENAME}.pem" -nodes -nokeys +openssl pkcs12 -in "${FILENAME}.p12" -passin pass:${PASSPHRASE} -out "${FILENAME}.key" -nodes -nocerts +openssl pkcs12 -export -out "${FILENAME}-v02.p12" -passin pass:${PASSPHRASE} -passout pass:${PASSPHRASE} -inkey "${FILENAME}.key" -in "${FILENAME}.pem" -name "test-certificate" +echo "Cleaning the temporary files ${FILENAME}.pem and ${FILENAME}.key" +rm "${FILENAME}.pem" +rm "${FILENAME}.key" +echo "Done" +``` + diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/binary-data.hex b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/binary-data.hex new file mode 100644 index 000000000..99e8ff478 --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/binary-data.hex @@ -0,0 +1 @@ +d9313225f88406e5a55909c5aff5269a diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-1.xml b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-1.xml new file mode 100755 index 000000000..fe24d2c5b --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-1.xml @@ -0,0 +1 @@ +BGoi18CKzCb0K8RTLj0pEFAj2MWDDilMemPZWlOIxS1WbAiR7UrhIstegXjYfIBEYXmnXZFEx/1Ns+yakYTm/B8=MIIBnDCCAUGgAwIBAgIIKg6abk/x5/YwDAYIKoZIzj0EAwIFADA2MRIwEAYDVQQLEwl4bWxlbmNyMTExIDAeBgNVBAMMF0VDLVAyNTZfU0hBMjU2V2l0aEVDRFNBMCAXDTExMTExOTAwMzc1NFoYDzMwMTEwMzIxMjMzNzU0WjA2MRIwEAYDVQQLEwl4bWxlbmNyMTExIDAeBgNVBAMMF0VDLVAyNTZfU0hBMjU2V2l0aEVDRFNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPch7nesUwGDPU2re7gYTT3SRIkLL4Mxjk1vBg8j+3RIBCdk46LeHH1Fis4UXIkVZS+S6CgRGuEXV956rvSaAbqM1MDMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUiT+AdwgJ2oDcGx2LNhx8x/6ewCIwDAYIKoZIzj0EAwIFAANHADBEAiABrzCZtLPGYst5WsKzWWN9ESPVnBK6681RL58Ie3nS7QIgSWSEEHMLC6vtfAcO9peB9Xi2QoJNat9qND0WIxTFCL0=CN=EC-P256_SHA256WithECDSA, OU=xmlencr113030529397845256182CN=EC-P256_SHA256WithECDSA, OU=xmlencr11iT+AdwgJ2oDcGx2LNhx8x/6ewCI=siDzcILUgIu6AKWiL9RsCNgeEWB4alujNUX+O05xmL+lwAAHOvHZmBGtrE/h1yKfG2HlLDfRwr8D7op+l4poX+nYRLDkjd2cedYpgoY9JI54G8i0vXHdK901RkjFIkVjl67VtqD7gB5XOHodoDhb8MvkNc36cYEmuYPigTaIXyP5Ej4B89VmbVXAf7Ap3rPGvTvKoUltJ99uFB3vtTWx9pjBnKX58l+yJQNsQeF90VFI+XyDqfXz2c4Zry1RyDoyE7ODGQ3NJLaKmVbNun+F+AgPuCvVbvGHMHz9AdKAJhukj/D0NbZUWZgrN42feXp6c3WY5Hv+xdkvi3Xa64RgADMWDBoeJfC2dB3WFxqa5hHjz/yVlJd6ia00RHf+2mLZ/kkbwp3pcBTDTpzkiKdLq082pvGcPNuwBSZ4CSZ1CIruhxtNKjaU1sh5hoM8WyH2poefbc3ocQPMBWOAK8VgRNLUc3zIusNMTak+9LTh4qc7aIKUfi43gGGb1V/lbnFhhCrAWM9ZMt9D3uxjiKPNJG/VVySUlJbZOwgzHxS7k9g0lsr+CvU1tu5WjsAN5V5GcGHMmtw/o2fDwl0ctaL7YmDaWDn6EabWjZkPqchBoAWYUXEqCypu7Ty3uXxeynZii/f/6qarzmTGIqGjXdIGdSWsxv1RoiPoZfZAFPdRjoXK7GxkwmTJk2rIgcCK6CfSSsfT++L6qORE2OSHgcgfX1W0Kv6FyXlJVlzH4o0iIbw7s2L+0ITrDLFAVcBwqmj8QFv9BcaEmP2jYni6BJgPGKqTaiMfda6C+k/sNV3OAjJgvinzhPbx7TI4MrdoDSKqSOLttojVE/c7WEg= diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-4.xml b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-4.xml new file mode 100755 index 000000000..5b4756347 --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P256__aes128-gcm__kw-aes128__ECDH-ES__ConcatKDF-4.xml @@ -0,0 +1,2 @@ +BGxdmfmeLvz/OUH4j6Uf5biRowuCMDNusmAAuVjhkCGnPFpWdv88eA/ugnX5j+kbKZoWF6B6I+SDE//UPnb2d6w=MIIBQTCB6aADAgECAhB2b9rs33TwhE6mzIeBDJdfMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMMB0VDLVAyNTYwHhcNMTIwOTA2MTg0NjM3WhcNMTMwOTA2MTkwNjM3WjASMRAwDgYDVQQDDAdFQy1QMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8NfSGbAD6WG5d6QQFRhlRhPlNgRbULHp2SBjbFuRuptoFYtIbYd+Ufxvt2/8sy+ffLZpy2AuyzUMOsyPt1EEfqMhMB8wHQYDVR0OBBYEFL4jJyYnHAmXZEZeWZmUkTRdUi5HMAoGCCqGSM49BAMCA0cAMEQCIAQBblilmQSENOcVzpf1vldNRK4Td8Gtet6BtHlW5Q9rAiB1xPPVjlvnHnTgubrYCHKxPTEXOLayD3ilzZ3neZOHkw== +CN=EC-P256127060950076581857612258860258983309174CN=EC-P256viMnJiccCZdkRl5ZmZSRNF1SLkc=7W729F0RbTLuMowbgCgbIj8JBSS292xiyv66vvrO263eyviIQoMewiF3dCRLciG3hNDUnFeSbd6SpcAe6FTcmzPryFY= \ No newline at end of file diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-2.xml b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-2.xml new file mode 100755 index 000000000..15575fdab --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-2.xml @@ -0,0 +1 @@ +BAdxgiS1lMEr8lzy9ai7fG7J9448PAxb7J5fVWflhBQISdtmEveP6g2jxe9Pk5QDcuP2nmMJWakc8ESmst+++VgO4h0PZ0Y/conzp/xe7qgSzPkA/AvepC5OISffbazlLA==MIIB2jCCAV6gAwIBAgIIWYeVuTW4P5owDAYIKoZIzj0EAwIFADA2MRIwEAYDVQQLEwl4bWxlbmNyMTExIDAeBgNVBAMMF0VDLVAzODRfU0hBMjU2V2l0aEVDRFNBMCAXDTExMTExOTAwMzc1NFoYDzMwMTEwMzIxMjMzNzU0WjA2MRIwEAYDVQQLEwl4bWxlbmNyMTExIDAeBgNVBAMMF0VDLVAzODRfU0hBMjU2V2l0aEVDRFNBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEl5Y0KFEzIubIA/XVfDILxk+LOZDMTx3FEJn63U4mKIvUat++lFM4vpciO5XWsNwnsFhCrqI8VaB+Fjb4Hh3WOXa21qEn5+3Wx3ZsfFRPT6/b87uu3gHgrX2UCKBtGDJKozUwMzASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRT7uuMAOh/i7WBvEg//K2IRI2pQTAMBggqhkjOPQQDAgUAA2gAMGUCMQC6XrEYR1RdCR7gl227ygbwNWfiHqy3AcX9Dj8OxS600Uz1bozs1VOHH9FT1HxeMkICMFruT7yLPVo38qREnfiNV7OzQCj8bP7DTCdscuUqwQ2E4KFhQH465yAFAUGyXR9iqg==CN=EC-P384_SHA256WithECDSA, OU=xmlencr116451289613934280602CN=EC-P384_SHA256WithECDSA, OU=xmlencr11U+7rjADof4u1gbxIP/ytiESNqUE=sESb9V0HhJpLQmghrjcTXOIUcX2S9zo9YqqTXcenhKA=NUX+O05xmL+lwAAIunRSEls8IilEPEVgX1ngfHYFPrDanvLROPiAsyBbq8ShN69zQd+fRB4Kv7NmTG4864XwnRThdQ3NXuoH80Nme9U8ZM61LbF4WiN8gAEXDwq37oQmAR4OG/xn0Js+pcmySdixjFLpS7/0W/YvFLevF9DRQUoMiJw1fRjYdezZhrUKAygjU9cS/1s/1xpPx4ftehLdQPNiP8TYtsiha5NmDhEcdp49J21pf08Vl4Jb5jof0HkC2ommudHAzWrnECwHlie+yEtI7Ncq6uu/cQuDHQ0tlc9PD434uKbLnw3uwH0264phNsJmnE/+4dJvf9GYld47m7KiYFiUm5KOrVUJGlU7cf3xlKd7ig2bgpJOeTbPVpWJw4zIuWstzL/HEuW7KmijIMiLATFTFBpbTQIMnVZxcawtC5E8HD2a4uHUjsSj7mFcqEoASgweD6/7kPW4GHp3gNdmvutHzu0qQ/TEpg/D0swuxoyPdlqAdES0O/rr/uDJqS5naRGvP222duQsC+gVG+Y6fmQKZqK6EwWptCbyW8VKthBlDSwVfKhSzDs/y5aeXGYvgIkx6cHeEHuT1hwtV5NrrbOY/3COyN8q5FH1KVwhZVI0YiazA+HA09ybQHMOb3IrhW8nhNjGDDALtzmY2aTCqvq219t9IwjPd19PrHeJ2pLuXDtpKcC+5x4lMbWyK0FZtAZoedcStUMU8Z3FEh8JpzpVgvbG0dmu336CZn+wA1eQZDybswe5PHjcie0MZLWVbKVrva0GhcUhvcIOW/n7cgDBCLSImIkQ44q0mSQtXdk= \ No newline at end of file diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-5.xml b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-5.xml new file mode 100755 index 000000000..c85b81d7e --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P384__aes192-gcm__kw-aes192__ECDH-ES__ConcatKDF-5.xml @@ -0,0 +1,2 @@ +BC3h5IgNlKqqCNyLZSAo1Y4H6908TyzQfmrvKdC5kTYWwev9fgOPVRn4cjhrcBACTQihtCaKFs0hUsMtNFsStZe0l9XouV1bECdjTRMk8Io2qVNLAZTwi5JfVf5ukQoiWg==MIIBgDCCAQagAwIBAgIQPL7tocTn+5VPjIZcxKPwSzAKBggqhkjOPQQDAjASMRAwDgYDVQQDDAdFQy1QMzg0MB4XDTEyMDkwNzAwMzk1MFoXDTEzMDkwNzAwNTk1MFowEjEQMA4GA1UEAwwHRUMtUDM4NDB2MBAGByqGSM49AgEGBSuBBAAiA2IABL0baNcwf70o+FYRAOmrmWBlG8U47OTLA8wsd3So7GlZgBMoRHc+UUXS6MyA5Us3MfTDl2KWP5SYTtsbp4kTydw2lZ2Rm4D9t1Ur8J61ED/qzW4umSKxDAlW+QXswRMikKMhMB8wHQYDVR0OBBYEFMH2ftb80gBcMTxXKkLWPF46/uRkMAoGCCqGSM49BAMCA2gAMGUCMQCzb2D6/fTVuLtOXwkz3Qi3fQS0sAL6Vud8QnOMJe7010BqRu2t1FhoTXUUWiO9p4MCMAXMgk3W7XPLpOJx8h045wEB0o3f7syMz3Nw3NsE8PSkDASHiwVk32CrfVd8uvG8vg== +CN=EC-P384100941572520037576679522097144609488444CN=EC-P384wfZ+1vzSAFwxPFcqQtY8Xjr+5GQ=R73CQ5Wa2jKOze7M9x/deplVV/ksN0t5auYV72Ey5f0=yv66vvrO263eyviIJAZm7N6tulNdXxn0ymEadYy4JKReVm2dEv3fYBbm84M= \ No newline at end of file diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-3.xml b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-3.xml new file mode 100755 index 000000000..7b75b85bd --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-3.xml @@ -0,0 +1 @@ +BAF/tsSwQ0oL6uGyQtqJpjLkXfxrRGwPPw+wGXK80I2jHScaPMdMjIV46774/vx6npR5xEciD4qgjbHfdkFX8BMR6QFiAQcaOtZZSVfKEC0gBgSpv+GJXNZQEzAMYI45G4NwIp2bW5C7NbTEiz112KjT2EO8cYMm01jbUl32Qn6IVm/ecg==MIICJTCCAYSgAwIBAgIIOomZsRJ1kPQwDAYIKoZIzj0EAwIFADA2MRIwEAYDVQQLEwl4bWxlbmNyMTExIDAeBgNVBAMMF0VDLVA1MjFfU0hBMjU2V2l0aEVDRFNBMCAXDTExMTExOTAwMzc1NFoYDzMwMTEwMzIxMjMzNzU0WjA2MRIwEAYDVQQLEwl4bWxlbmNyMTExIDAeBgNVBAMMF0VDLVA1MjFfU0hBMjU2V2l0aEVDRFNBMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAGjTm3NCtivCNFtnhocXgO9lv7xlYFVSlkLijROC88/vCNhjWODzlpArxqaI+3sl2c82+9KXTxX+VDpZwD4GsF4kAswMEgXksYdSHTadSk0u4ar7vH5AfMmtaWDZ+tEaz/D99LypDW8ZKB/oStrtLeVgFPARu4Mb6Uauic5unjdANO5GjNTAzMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFA5VFtYzDCp5grC/KSPv6hqOQg1qMAwGCCqGSM49BAMCBQADgYwAMIGIAkIBlSWu8DdY2YFOI2vvyaaYJ4Y8xS4jrR0se+3/fb1oCPDCm3Mr1xcPJEJNRHTNwS4u9GCiUQoFlkTh8r+zDCUGJCwCQgG8dY3pP0rphOxxOSCouN3AShXc0SFQTaeaynK1+2R+pvc7GLOVelHZslkr2nrklyyVXLPlE0SBMS4TGZZHXl473A==CN=EC-P521_SHA256WithECDSA, OU=xmlencr114218071511807135988CN=EC-P521_SHA256WithECDSA, OU=xmlencr11DlUW1jMMKnmCsL8pI+/qGo5CDWo=Xn5PLgSEWuXaXUVsl7cy59DETFdBfNXddlxiv839GIj5MZnYG3fkyQ==NUX+O05xmL+lwAAJOcASrbW38r4u79x19RyEiXfx8aUCpth2WWzOjILVd0z9Ky947TPFhAhAqE7cbAhLAT6rMlwHHSfTMu0q0lDULMMNANKzBK0GqPMgjD7zL8dHCYcTK6RSztRywkThk4byxWKBegqOz875tn3lZC3UQHvLu5a0XNkLS14K2QTrk1YDGJgj4Y3P9QDlGK6eb0NiaTeQt0AOEsn7xkZS8iY6Hr+nOpAXosUbkgsrv0jBMahmtk7S4sJsAB5A0umMWhLGpcagXRkcKKYNVQtZpJffJpTjyQewT6D7blZ4CodOi6+naOJBvHgw3Qyspr0ULOQyZpbBg47LXRV49ppp9dbf8pZzgUZ5IDkwCMBU2YkhV/NjXs1kjtfSP4EAHGct/Yn7S2wjOtToVIl1f3l2Zsg4ttnObvFLWpLEUKrf1bt1NefjHSnTuvjfI2xH99KA6NkW53ZZEDEiwrDoZE3+VV+vSDbyULWR1uKvt95LEjMzHu/F6K+AkJWLPiUxhHTM7o4AD6z6uoDewWJgcllyYIWI2GsGSQgTCm9203CgKtVA/ruXgH9iokqtoYhqSdM29fn/VGhA8fbp8BytN/2Y7GovA4NVzwkuI9n+/U2N+AV+mkUZ8ezMcFVilPJYHJGOqm/x2NXl1O/G1RgpyVqtZYuX+hb+KP/wx1lb4730zJCOIE+OqcFg9Ml8l47vliB4Yj7SORLH+nrWErjqPK8SoRGdbSyeYn1hLPCVw7QydhW2NUeJgZRlAxr+8dC8jVqGX6Dc/bMGjs89Z25x2+fAyOEjZgE5If3ilB8= \ No newline at end of file diff --git a/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-6.xml b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-6.xml new file mode 100755 index 000000000..8fb488176 --- /dev/null +++ b/src/test/resources/org/w3c/www/interop/xmlenc-core-11/cipherText__EC-P521__aes256-gcm__kw-aes256__ECDH-ES__ConcatKDF-6.xml @@ -0,0 +1,2 @@ +BAHWWhX9XGPzKjtDNW84GeteO4pDPNavrmJxJl9laaa8gFN1mhUhp5ghs7tS33Hk/s+CEDOXVY9LZ8+6fRL3D/odQgAcpU4mlDDzA2hlPuOUmrA6o22xR9x2CirwGNPqH//Gvv9QQA6D2U50puaTkgzgztC6iiQqjKA8SA6jyEHSzOoxNw==MIIByzCCASygAwIBAgIQI3fwlCN2QY1BQHy+eMjyIzAKBggqhkjOPQQDAjASMRAwDgYDVQQDDAdFQy1QNTIxMB4XDTEyMDkwNzAxMDY0MFoXDTEzMDkwNzAxMjY0MFowEjEQMA4GA1UEAwwHRUMtUDUyMTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAfrGAll5gYIRVyKeG9bUVkg49yMMVvVUPXTzTE7egvwXDzHa8MvSIpo0KF+7V/5de77YOBCYSEbqWpbWSQ9qb99qAdz26QiBHD2ruCwwI3JzeWAx6WaRBVPf6wGjWXVJDHRzbSrW1btFVpUJfDFjGl/VKAORaNgGOVwwJl1NaUh4hBeLoyEwHzAdBgNVHQ4EFgQUUbnpDinGJpJs/4VcMCmx15rz0mwwCgYIKoZIzj0EAwIDgYwAMIGIAkIBeqv+JyzFXuIs/Oj4je3tWPj1G27nmDstkVUfxFgSnBFEsCXpJTIvIJOOJK87vWCkM/4e1ZyD0nnwQxSA6lkSQPACQgHHfIuTjBdkWRZYalOqXO0Cf/H27vSio9KycyYoOGNbVSyh4r28nUNX5v5SAsu9bZSw++dkkhC0vR3J0I2jx+L9sA== +CN=EC-P52147783581740490077991902519948829816611CN=EC-P521UbnpDinGJpJs/4VcMCmx15rz0mw=jCIKlMZeOH/baAYBN6vn7T3u+G0dmKHkOMSMuDN14SjBkYb0wzK7cg==yv66vvrO263eyviIEXlsL77iyOuBQCTp4aqUKTQgFpJf5sxpyC0sO0izVTg= \ No newline at end of file