package com.airlock.iam.saml2.infrastructure.plugin;

import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.sun.identity.common.SystemConfigurationUtil;
import com.sun.identity.saml2.common.SAML2Exception;
import com.sun.identity.saml2.common.SAML2SDKUtils;
import com.sun.identity.saml2.xmlenc.EncProvider;
import com.sun.identity.shared.configuration.ISystemProperties;
import com.sun.identity.shared.configuration.SystemPropertiesManager;
import com.sun.identity.shared.xml.XMLUtils;
import jakarta.annotation.Nonnull;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Hashtable;
import java.util.Objects;
import java.util.Optional;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import org.apache.commons.lang3.StringUtils;
import org.apache.xml.security.Init;
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.XMLEncryptionException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public final class SamlEncryptionProvider implements EncProvider {
   private static final ELogger LOG = ELoggerFactory.getLogger(SamlEncryptionProvider.class);
   public static final String KEY_TRANSPORT_ALGORITHM = "keyTransportAlgorithm";
   public static final String RSA_OAEP_PARAMS = "rsaOaepParams";
   private static final Hashtable<String, SecretKey> CACHED_KEYS = new Hashtable<>();
   private static boolean encryptedKeyInKeyInfo = true;
   private final Optional<ISystemProperties> systemProperties = Optional.ofNullable(SystemPropertiesManager.getSystemProperties());

   private String getSystemProperty(String key) {
      return this.systemProperties.<String>map(properties -> properties.get(key)).orElse(null);
   }

   private String getSystemProperty(String key, String defaultValue) {
      String value = this.getSystemProperty(key);
      return StringUtils.isNotBlank(value) ? value : defaultValue;
   }

   @Override
   public Element encrypt(
      String xmlString, Key recipientPublicKey, String dataEncAlgorithm, int dataEncStrength, String recipientEntityID, String outerElementName
   ) throws SAML2Exception {
      return this.encrypt(xmlString, recipientPublicKey, null, dataEncAlgorithm, dataEncStrength, recipientEntityID, outerElementName);
   }

   @Override
   public Element encrypt(
      String xmlString,
      Key recipientPublicKey,
      SecretKey secretKey,
      String dataEncAlgorithm,
      int dataEncStrength,
      String recipientEntityID,
      String outerElementName
   ) throws SAML2Exception {
      String classMethod = "FMEncProvider.encrypt: ";
      if (xmlString == null
         || xmlString.isEmpty()
         || recipientPublicKey == null
         || dataEncAlgorithm == null
         || dataEncAlgorithm.isEmpty()
         || outerElementName == null
         || outerElementName.isEmpty()) {
         SAML2SDKUtils.DEBUG.error(classMethod + "Null input parameter(s).");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
      }

      if (!dataEncAlgorithm.equals("http://www.w3.org/2001/04/xmlenc#aes128-cbc")
         && !dataEncAlgorithm.equals("http://www.w3.org/2001/04/xmlenc#aes192-cbc")
         && !dataEncAlgorithm.equals("http://www.w3.org/2001/04/xmlenc#aes256-cbc")
         && !dataEncAlgorithm.equals("http://www.w3.org/2001/04/xmlenc#tripledes-cbc")) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("unsupportedKeyAlg"));
      }

      if ((!dataEncAlgorithm.equals("http://www.w3.org/2001/04/xmlenc#aes128-cbc") || dataEncStrength == 128)
         && (!dataEncAlgorithm.equals("http://www.w3.org/2001/04/xmlenc#aes192-cbc") || dataEncStrength == 192)
         && (!dataEncAlgorithm.equals("http://www.w3.org/2001/04/xmlenc#aes256-cbc") || dataEncStrength == 256)) {
         Document doc = XMLUtils.toDOMDocument(xmlString, SAML2SDKUtils.DEBUG);
         if (doc == null) {
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorObtainingElement"));
         }

         if (dataEncStrength <= 0) {
            dataEncStrength = 128;
         }

         Element rootElement = doc.getDocumentElement();
         if (rootElement == null) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Empty document.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("emptyDoc"));
         }

         if (secretKey == null) {
            if (recipientEntityID != null) {
               if (CACHED_KEYS.containsKey(recipientEntityID)) {
                  secretKey = CACHED_KEYS.get(recipientEntityID);
               } else {
                  secretKey = this.generateSecretKey(dataEncAlgorithm, dataEncStrength);
                  CACHED_KEYS.put(recipientEntityID, secretKey);
               }
            } else {
               secretKey = this.generateSecretKey(dataEncAlgorithm, dataEncStrength);
            }

            if (secretKey == null) {
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorGenerateKey"));
            }
         }

         String publicKeyEncAlg = recipientPublicKey.getAlgorithm();

         XMLCipher cipher;
         try {
            cipher = this.getXmlCipher(publicKeyEncAlg);
         } catch (XMLEncryptionException xe1) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Unable to obtain cipher with public key algorithm.", xe1);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipherForPublicKeyAlg"));
         }

         try {
            cipher.init(3, recipientPublicKey);
         } catch (XMLEncryptionException xe2) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to initialize cipher with public key", xe2);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedInitCipherWithPublicKey"));
         }

         EncryptedKey encryptedKey;
         try {
            if (publicKeyEncAlg.equals("RSA")
               && this.getSystemProperty("keyTransportAlgorithm", "http://www.w3.org/2001/04/xmlenc#rsa-1_5")
                  .equals("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p")) {
               Optional<String> oaepParams = Optional.ofNullable(this.getSystemProperty("rsaOaepParams"));
               byte[] bytes = oaepParams.<byte[]>map(s -> s.getBytes(StandardCharsets.UTF_8)).orElse(null);
               encryptedKey = cipher.encryptKey(doc, secretKey, null, bytes);
               LOG.debug("Using RSA-OAEP to encrypt secret key");
            } else {
               encryptedKey = cipher.encryptKey(doc, secretKey);
               LOG.debug("Using RSA v1.5 to encrypt secret key");
            }
         } catch (XMLEncryptionException xe3) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to encrypt secret key with public key", xe3);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedEncryptingSecretKeyWithPublicKey"));
         }

         try {
            cipher = createSecureXmlCipher(dataEncAlgorithm);
         } catch (XMLEncryptionException xe4) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to obtain a cipher for data encryption algorithm" + dataEncAlgorithm, xe4);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("cipherNotAvailableForDataEncAlg"));
         }

         try {
            cipher.init(1, secretKey);
         } catch (XMLEncryptionException xe5) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to initialize cipher with secret key.", xe5);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedInitCipherWithSecretKey"));
         }

         Document resultDoc;
         try {
            resultDoc = cipher.doFinal(doc, rootElement);
         } catch (Exception e) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to do the final data encryption.", e);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedEncryptingData"));
         }

         Element ek;
         try {
            ek = cipher.martial(doc, encryptedKey);
         } catch (Exception xe6) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to martial the encrypted key", xe6);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedMartialingEncryptedKey"));
         }

         String outerElemNS = "urn:oasis:names:tc:SAML:2.0:assertion";
         String outerElemPrefix = "saml";
         if ("NewEncryptedID".equals(outerElementName)) {
            outerElemNS = "urn:oasis:names:tc:SAML:2.0:protocol";
            outerElemPrefix = "samlp";
         }

         Element outerElement = resultDoc.createElementNS(outerElemNS, outerElemPrefix + ":" + outerElementName);
         outerElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + outerElemPrefix, outerElemNS);
         Element ed = resultDoc.getDocumentElement();
         resultDoc.replaceChild(outerElement, ed);
         outerElement.appendChild(ed);
         if (encryptedKeyInKeyInfo) {
            Element dsElement = resultDoc.createElementNS("http://www.w3.org/2000/09/xmldsig#", "ds:KeyInfo");
            dsElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");
            dsElement.appendChild(ek);
            NodeList nl = ed.getElementsByTagNameNS("http://www.w3.org/2001/04/xmlenc#", "CipherData");
            if (nl.getLength() == 0) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Unable to find required xenc:CipherData Element.");
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedEncryptingData"));
            }

            Element cipherDataElement = (Element)nl.item(0);
            ed.insertBefore(dsElement, cipherDataElement);
         } else {
            outerElement.appendChild(ek);
         }

         return resultDoc.getDocumentElement();
      } else {
         SAML2SDKUtils.DEBUG.error(classMethod + "Data encryption algorithm " + dataEncAlgorithm + "and strength " + dataEncStrength + " mismatch.");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("algSizeMismatch"));
      }
   }

   private XMLCipher getXmlCipher(String publicKeyEncAlg) throws XMLEncryptionException, SAML2Exception {
      switch (publicKeyEncAlg) {
         case "RSA":
            String keyTransportAlgorithm = this.getSystemProperty("keyTransportAlgorithm", "http://www.w3.org/2001/04/xmlenc#rsa-1_5");
            return createSecureXmlCipher(keyTransportAlgorithm);
         case "DESede":
            return createSecureXmlCipher("http://www.w3.org/2001/04/xmlenc#kw-tripledes");
         case "AES":
            return createSecureXmlCipher("http://www.w3.org/2001/04/xmlenc#kw-aes128");
         default:
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("unsupportedKeyAlg"));
      }
   }

   @Override
   public SecretKey getSecretKey(String xmlString, Key recipientPrivateKey) throws SAML2Exception {
      String classMethod = "FMEncProvider.getSecretKey: ";
      if (SAML2SDKUtils.DEBUG.messageEnabled()) {
         SAML2SDKUtils.DEBUG.message(classMethod + "Entering ...");
      }

      if (xmlString != null && !xmlString.isEmpty() && recipientPrivateKey != null) {
         Document doc = XMLUtils.toDOMDocument(xmlString, SAML2SDKUtils.DEBUG);
         if (doc == null) {
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorObtainingElement"));
         }

         Element rootElement = doc.getDocumentElement();
         if (rootElement == null) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Empty document.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("emptyDoc"));
         }

         Element firstChild = this.getNextElementNode(rootElement.getFirstChild());
         if (firstChild == null) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Missing the EncryptedData element.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElementEncryptedData"));
         }

         Element secondChild = this.getNextElementNode(firstChild.getNextSibling());
         if (secondChild == null) {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message(classMethod + "looking for encryption key inside first child.");
            }

            NodeList nl = firstChild.getElementsByTagNameNS("http://www.w3.org/2001/04/xmlenc#", "EncryptedKey");
            if (nl.getLength() == 0) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Missing the EncryptedKey element.");
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElementEncryptedKey"));
            }

            secondChild = (Element)nl.item(0);
         }

         XMLCipher cipher;
         try {
            cipher = createSecureXmlCipher();
         } catch (XMLEncryptionException xe1) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Unable to get a cipher instance.", xe1);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipher"));
         }

         try {
            cipher.init(2, null);
         } catch (XMLEncryptionException xe2) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to initialize cipher for decryption mode", xe2);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedInitCipherForDecrypt"));
         }

         EncryptedData encryptedData;
         try {
            encryptedData = cipher.loadEncryptedData(doc, firstChild);
         } catch (XMLEncryptionException xe3) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to load encrypted data", xe3);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedLoadingEncryptedData"));
         }

         EncryptedKey encryptedKey;
         try {
            encryptedKey = cipher.loadEncryptedKey(doc, secondChild);
         } catch (XMLEncryptionException xe4) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to load encrypted key", xe4);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedLoadingEncryptedKey"));
         }

         if (encryptedKey != null && encryptedData != null) {
            XMLCipher keyCipher;
            try {
               keyCipher = createSecureXmlCipher();
            } catch (XMLEncryptionException xe5) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to get a cipher instance for decrypting secret key.", xe5);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipher"));
            }

            try {
               keyCipher.init(4, recipientPrivateKey);
            } catch (XMLEncryptionException xe6) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to initialize cipher in unwrap mode with private key", xe6);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipherForUnwrap"));
            }

            try {
               return (SecretKey)keyCipher.decryptKey(encryptedKey, encryptedData.getEncryptionMethod().getAlgorithm());
            } catch (XMLEncryptionException xe7) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to decrypt the secret key", xe7);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedDecryptingSecretKey"));
            }
         } else {
            return null;
         }
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
      }
   }

   @Override
   public Element decrypt(String xmlString, Key recipientPrivateKey) throws SAML2Exception {
      String classMethod = "FMEncProvider.decrypt: ";
      if (SAML2SDKUtils.DEBUG.messageEnabled()) {
         SAML2SDKUtils.DEBUG.message(classMethod + "Entering ...");
      }

      if (xmlString != null && !xmlString.isEmpty() && recipientPrivateKey != null) {
         Document doc = XMLUtils.toDOMDocument(xmlString, SAML2SDKUtils.DEBUG);
         if (doc == null) {
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorObtainingElement"));
         }

         Element rootElement = doc.getDocumentElement();
         if (rootElement == null) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Empty document.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("emptyDoc"));
         }

         Element firstChild = this.getNextElementNode(rootElement.getFirstChild());
         if (firstChild == null) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Missing the EncryptedData element.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElementEncryptedData"));
         }

         Element secondChild = this.getNextElementNode(firstChild.getNextSibling());
         if (secondChild == null) {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message(classMethod + "looking for encryption key inside first child.");
            }

            NodeList nl = firstChild.getElementsByTagNameNS("http://www.w3.org/2001/04/xmlenc#", "EncryptedKey");
            if (nl.getLength() == 0) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Missing the EncryptedKey element.");
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElementEncryptedKey"));
            }

            secondChild = (Element)nl.item(0);
         }

         XMLCipher cipher;
         try {
            cipher = createSecureXmlCipher();
         } catch (XMLEncryptionException xe1) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Unable to get a cipher instance.", xe1);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipher"));
         }

         try {
            cipher.init(2, null);
         } catch (XMLEncryptionException xe2) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to initialize cipher for decryption mode", xe2);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedInitCipherForDecrypt"));
         }

         cipher.setSecureValidation(true);

         EncryptedData encryptedData;
         try {
            encryptedData = cipher.loadEncryptedData(doc, firstChild);
         } catch (XMLEncryptionException xe3) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to load encrypted data", xe3);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedLoadingEncryptedData"));
         }

         EncryptedKey encryptedKey;
         try {
            encryptedKey = cipher.loadEncryptedKey(doc, secondChild);
         } catch (XMLEncryptionException xe4) {
            SAML2SDKUtils.DEBUG.error(classMethod + "Failed to load encrypted key", xe4);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedLoadingEncryptedKey"));
         }

         Document decryptedDoc = null;
         if (encryptedKey != null && encryptedData != null) {
            XMLCipher keyCipher;
            try {
               keyCipher = createSecureXmlCipher();
            } catch (XMLEncryptionException xe5) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to get a cipher instance for decrypting secret key.", xe5);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipher"));
            }

            try {
               keyCipher.init(4, recipientPrivateKey);
            } catch (XMLEncryptionException xe6) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to initialize cipher in unwrap mode with private key", xe6);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipherForUnwrap"));
            }

            Key encryptionKey;
            try {
               encryptionKey = keyCipher.decryptKey(encryptedKey, encryptedData.getEncryptionMethod().getAlgorithm());
            } catch (XMLEncryptionException xe7) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to decrypt the secret key", xe7);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedDecryptingSecretKey"));
            }

            try {
               cipher = createSecureXmlCipher();
            } catch (XMLEncryptionException xe8) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to get cipher instance for final data decryption.", xe8);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("noCipher"));
            }

            try {
               cipher.init(2, encryptionKey);
            } catch (XMLEncryptionException xe9) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to initialize cipher with secret key.", xe9);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedInitCipherForDecrypt"));
            }

            try {
               decryptedDoc = cipher.doFinal(doc, firstChild);
            } catch (Exception e) {
               SAML2SDKUtils.DEBUG.error(classMethod + "Failed to decrypt data.", e);
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedDecryptingData"));
            }
         }

         Element root = Objects.requireNonNull(decryptedDoc).getDocumentElement();
         Element child = this.getNextElementNode(root.getFirstChild());
         if (child == null) {
            SAML2SDKUtils.DEBUG.error(classMethod + "decrypted document contains empty element.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("failedDecryptingData"));
         } else {
            root.removeChild(child);
            decryptedDoc.replaceChild(child, root);
            return decryptedDoc.getDocumentElement();
         }
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
      }
   }

   private Element getNextElementNode(Node node) {
      while (node != null) {
         if (node.getNodeType() == 1) {
            return (Element)node;
         }

         node = node.getNextSibling();
      }

      return null;
   }

   private SecretKey generateSecretKey(String algorithm, int keyStrength) throws SAML2Exception {
      KeyGenerator keygen;
      try {
         if (!algorithm.equals("http://www.w3.org/2001/04/xmlenc#aes128-cbc")
            && !algorithm.equals("http://www.w3.org/2001/04/xmlenc#aes192-cbc")
            && !algorithm.equals("http://www.w3.org/2001/04/xmlenc#aes256-cbc")) {
            if (!algorithm.equals("http://www.w3.org/2001/04/xmlenc#tripledes-cbc")) {
               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("unsupportedKeyAlg"));
            }

            keygen = KeyGenerator.getInstance("TripleDES");
         } else {
            keygen = KeyGenerator.getInstance("AES");
         }

         if (keyStrength != 0) {
            keygen.init(keyStrength);
         }
      } catch (NoSuchAlgorithmException ne) {
         throw new SAML2Exception(ne);
      }

      return keygen.generateKey();
   }

   private static XMLCipher createSecureXmlCipher() throws XMLEncryptionException {
      XMLCipher instance = XMLCipher.getInstance();
      instance.setSecureValidation(true);
      return instance;
   }

   @Nonnull
   private static XMLCipher createSecureXmlCipher(String keyTransportAlgorithm) throws XMLEncryptionException {
      XMLCipher instance = XMLCipher.getInstance(keyTransportAlgorithm);
      instance.setSecureValidation(true);
      return instance;
   }

   static {
      Init.init();
      String tmp = SystemConfigurationUtil.getProperty("com.sun.identity.saml.xmlenc.encryptedKeyInKeyInfo");
      if (tmp != null && tmp.equalsIgnoreCase("false")) {
         encryptedKeyInKeyInfo = false;
      }
   }

   public enum KeyTransportAlgorithm {
      RSA_15("http://www.w3.org/2001/04/xmlenc#rsa-1_5"),
      RSA_OAEP("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");

      private final String algorithm;

      KeyTransportAlgorithm(String algorithm) {
         this.algorithm = algorithm;
      }

      public String getAlgorithm() {
         return this.algorithm;
      }
   }
}
