coheigea commented on code in PR #264: URL: https://github.com/apache/ws-wss4j/pull/264#discussion_r1464799631
########## ws-security-dom/src/test/java/org/apache/wss4j/dom/message/EncryptionTest.java: ########## @@ -313,6 +314,66 @@ public void testEncryptionEncryption() throws Exception { verify(encryptedEncryptedDoc, encCrypto, keystoreCallbackHandler); } + /** + * Test that encrypt and decrypt a WS-Security envelope. + * This test uses the ECDSA-ES algorithm to (wrap) the symmetric key. + * <p/> + * + * @throws Exception Thrown when there is any problem in signing or verification + */ + @ParameterizedTest + @CsvSource({"xdh, X25519", + "xdh, X448", + "ec, secp256r1", + "ec, secp384r1", + "ec, secp521r1", + }) + public void testEncryptionDecryptionECDSA_ES(String algorithm, String certAlias) throws Exception { + try { + if (!JDKTestUtils.isAlgorithmSupportedByJDK(algorithm)) { + LOG.info("Add AuxiliaryProvider to execute test with algorithm [{}] and cert alias [{}]", algorithm, certAlias); + Security.addProvider(JDKTestUtils.getAuxiliaryProvider()); + } + Crypto encCrypto = CryptoFactory.getInstance("wss-ecdh.properties"); + + Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG); + WSSecHeader secHeader = new WSSecHeader(doc); + secHeader.insertSecurityHeader(); + + WSSecEncrypt builder = new WSSecEncrypt(secHeader); + builder.setUserInfo(certAlias); + builder.setKeyEncAlgo(WSConstants.KEYWRAP_AES128); + builder.setKeyAgreementMethod(WSConstants.AGREEMENT_METHOD_ECDH_ES); + builder.setDigestAlgorithm(WSS4JConstants.SHA256); + builder.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER); + + LOG.info("Before Encryption ..."); + KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.AES_128_GCM); + SecretKey symmetricKey = keyGen.generateKey(); + + Document encryptedDoc = builder.build(encCrypto, symmetricKey); + LOG.info("After Encryption ...."); + + String outputString = + XMLUtils.prettyDocumentToString(encryptedDoc); + if (LOG.isDebugEnabled()) { + LOG.debug("Encrypted message:"); + LOG.debug(outputString); + } + assertFalse(outputString.contains("counter_port_type")); Review Comment: Could we add an assertion against outputString that it is actually adding KeyAgreement in the test? ########## ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java: ########## @@ -142,55 +144,22 @@ public List<WSSecurityEngineResult> handleToken( throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noCipher"); } - Element keyInfoChildElement = getKeyInfoChildElement(elem, data); - X509Certificate[] certs = null; STRParser.REFERENCE_TYPE referenceType = null; PublicKey publicKey = null; boolean symmetricKeyWrap = isSymmetricKeyWrap(encryptedKeyTransportMethod); - if (!symmetricKeyWrap) { - if (SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(keyInfoChildElement.getLocalName()) - && WSConstants.WSSE_NS.equals(keyInfoChildElement.getNamespaceURI())) { - STRParserParameters parameters = new STRParserParameters(); - parameters.setData(data); - parameters.setStrElement(keyInfoChildElement); - - STRParser strParser = new EncryptedKeySTRParser(); - STRParserResult parserResult = strParser.parseSecurityTokenReference(parameters); - - certs = parserResult.getCertificates(); - publicKey = parserResult.getPublicKey(); - referenceType = parserResult.getCertificatesReferenceType(); - } else { - certs = getCertificatesFromX509Data(keyInfoChildElement, data); - if (certs == null || certs.length == 0) { - XMLSignatureFactory signatureFactory; - if (provider == null) { - // Try to install the Santuario Provider - fall back to the JDK provider if this does - // not work - try { - signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig"); - } catch (NoSuchProviderException ex) { - signatureFactory = XMLSignatureFactory.getInstance("DOM"); - } - } else { - signatureFactory = XMLSignatureFactory.getInstance("DOM", provider); - } - - publicKey = X509Util.parseKeyValue((Element)keyInfoChildElement.getParentNode(), - signatureFactory); - } - } - - if (publicKey == null && (certs == null || certs.length < 1 || certs[0] == null)) { - throw new WSSecurityException( - WSSecurityException.ErrorCode.FAILURE, - "noCertsFound", - new Object[] {"decryption (KeyId)"}); - } - if (certs != null && certs.length > 0) { - publicKey = certs[0].getPublicKey(); - } + AgreementMethod agreementMethod = null; + if (isDHKeyWrap) { + // get key agreement method value + agreementMethod = getAgreementMethodFromElement(keyInfoChildElement); + // get the recipient key info element + keyInfoChildElement = getRecipientKeyInfoChildElement(agreementMethod); Review Comment: There is a potential NPE here that should be handled ########## ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/EncryptedKeyProcessor.java: ########## @@ -357,6 +358,102 @@ private static byte[] getAsymmetricDecryptedBytes( } } + /** + * Method decrypts encryptedEphemeralKey using Key Agreement algorithm to derive symmetric key + * for decryption of the key. + * + * @param data RequestData context + * @param agreementMethod AgreementMethod element + * @param encryptedKeyTransportMethod Algorithm used to encrypt the key + * @param encryptedEphemeralKey Encrypted ephemeral/transport key + * @param privateKey Private key of the recipient + * @return Decrypted bytes of the ephemeral/transport key + * @throws WSSecurityException if the key decryption fails + */ + private static byte[] getDiffieHellmanDecryptedBytes( + RequestData data, + AgreementMethod agreementMethod, + String encryptedKeyTransportMethod, + byte[] encryptedEphemeralKey, + PrivateKey privateKey + ) throws WSSecurityException { + + SecretKey kek; + try { + KeyAgreementParameters parameterSpec = XMLCipherUtil.constructRecipientKeyAgreementParameters( + encryptedKeyTransportMethod, agreementMethod, privateKey); + + kek = org.apache.xml.security.utils.KeyUtils.aesWrapKeyWithDHGeneratedKey(parameterSpec); + } catch (XMLSecurityException ex) { + LOG.debug("Error occurred while resolving the Diffie Hellman key: " + ex.getMessage()); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex); + } + + String cryptoProvider = data.getDecCrypto().getCryptoProvider(); + Cipher cipher = KeyUtils.getCipherInstance(encryptedKeyTransportMethod, cryptoProvider); + + try { + cipher.init(Cipher.UNWRAP_MODE, kek); + String keyAlgorithm = JCEMapper.translateURItoJCEID(encryptedKeyTransportMethod); + return cipher.unwrap(encryptedEphemeralKey, keyAlgorithm, Cipher.SECRET_KEY).getEncoded(); + } catch (InvalidKeyException | NoSuchAlgorithmException ex) { + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex); + } + } + + /** + * if keyInfo element contains AgreementMethod element then check if it is supported EC Diffie-Hellman key agreement algorithm + * + * @param keyInfoChildElement The KeyInfo child element + * @return true if AgreementMethod element is present and DH algorithm supported and false if AgreementMethod element is not present + * @throws WSSecurityException if AgreementMethod element is present but DH algorithm is not supported + */ + private boolean isDiffieHellmanKeyWrap(Element keyInfoChildElement) throws WSSecurityException { + if (EncryptionConstants._TAG_AGREEMENTMETHOD.equals(keyInfoChildElement.getLocalName()) + && WSConstants.ENC_NS.equals(keyInfoChildElement.getNamespaceURI())) { + String algorithmURI = keyInfoChildElement.getAttributeNS(null, "Algorithm"); + // Only ECDH_ES is supported for AgreementMethod + if (!WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(algorithmURI)) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, + "unknownAlgorithm", new Object[]{algorithmURI}); + } + return true; + } + return false; + } + + /** + * Parse keyInfo content to AgreementMethod object. + * + * @param keyInfoChildElement The KeyInfo child element containing AgreementMethod data. + * @return the {@link AgreementMethod} object. + * @throws WSSecurityException if AgreementMethod element is invalid. + */ + private AgreementMethod getAgreementMethodFromElement(Element keyInfoChildElement) throws WSSecurityException { + try { + return new AgreementMethodImpl(keyInfoChildElement); + } catch (XMLSecurityException ex) { + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex); + } + } + + /** + * Get the RecipientKeyInfo child element from the AgreementMethod element. + * + * @param agreementMethod The AgreementMethod element + * @return the RecipientKeyInfo child element which contains the recipient's public key. + * @throws WSSecurityException if the RecipientKeyInfo element can not be retrieved. + */ + private Element getRecipientKeyInfoChildElement(AgreementMethod agreementMethod) throws WSSecurityException { + try { + Element receiverKeyInfoElement = agreementMethod.getRecipientKeyInfo().getElement(); Review Comment: nit: remove extra space after equals -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@ws.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@ws.apache.org For additional commands, e-mail: dev-h...@ws.apache.org