Codeunit 1466 EncryptedXml Impl. in 27

App
System Application
Namespace
System.Security.Encryption

Procedures, 4

Versions171819202122232425262728latest

Source242526272829

Source in 27

src/System Application/App/Cryptography Management/src/Xml/EncryptedXmlImpl.Codeunit.al246 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------

namespace System.Security.Encryption;

using System;

codeunit 1466 "EncryptedXml Impl."
{
    Access = Internal;
    InherentEntitlements = X;
    InherentPermissions = X;

    var
        ElementNotFoundErr: Label 'The "%1" element was not found.', Comment = '%1: The name of the xml element that could not be found.';
        UnsupportedSymmetricAlgorithmErr: Label 'Unsupported symmetric algorithm.';
        XmlEncElementUrlTok: Label 'http://www.w3.org/2001/04/xmlenc#Element', Locked = true;
        XmlEncRSA15UrlTok: Label 'http://www.w3.org/2001/04/xmlenc#rsa-1_5', Locked = true;
        XmlEncUrlTok: Label 'http://www.w3.org/2001/04/xmlenc#', Locked = true;

    procedure Encrypt(var XmlDocument: XmlDocument; ElementToEncrypt: Text; X509CertBase64Value: Text; X509CertPassword: SecretText)
    var
        XmlDotNetConvert: Codeunit "Xml DotNet Convert";
        X509Certificate2Impl: Codeunit "X509Certificate2 Impl.";
        DotNetEncryptedXml: DotNet EncryptedXml;
        DotNetX509Certificate2: DotNet X509Certificate2;
        DotNetXmlDocument: DotNet XmlDocument;
        DotNetXmlElementToEncrypt: DotNet XmlElement;
        DotNetEncryptedData: DotNet EncryptedData;
    begin
        //Initialize EncryptedXml
        DotNetEncryptedXml := DotNetEncryptedXml.EncryptedXml();

        //Convert XmlDocument to DotNet XmlDocument preserving whitespace
        XmlDotNetConvert.ToDotNet(XmlDocument, DotNetXmlDocument, true);

        //Get the DotNet XML element to encrypt
        DotNetXmlElementToEncrypt := DotNetXmlDocument.GetElementsByTagName(ElementToEncrypt).Item(0);
        if IsNull(DotNetXmlElementToEncrypt) then
            Error(ElementNotFoundErr, ElementToEncrypt);

        //Initialize a X509Certificate2.
        X509Certificate2Impl.InitializeX509Certificate(X509CertBase64Value, X509CertPassword, DotNetX509Certificate2);

        //Encrypt the element
        DotNetEncryptedData := DotNetEncryptedXml.Encrypt(DotNetXmlElementToEncrypt, DotNetX509Certificate2);
        DotNetEncryptedXml.ReplaceElement(DotNetXmlElementToEncrypt, DotNetEncryptedData, false);

        //Convert the encrypted DotNet XmlDocument to an XmlDocument.
        XmlDotNetConvert.FromDotNet(DotNetXmlDocument, XmlDocument);
    end;

    procedure Encrypt(var XmlDocument: XmlDocument; ElementToEncrypt: Text; X509CertBase64Value: Text; X509CertPassword: SecretText; SymmetricAlgorithm: Enum SymmetricAlgorithm)
    var
        XmlDotNetConvert: Codeunit "Xml DotNet Convert";
        X509Certificate2Impl: Codeunit "X509Certificate2 Impl.";
        SymmetricAlgorithmInterface: Interface SymmetricAlgorithm;
        DotNetEncryptedXml: DotNet EncryptedXml;
        DotNetXmlDocument: DotNet XmlDocument;
        DotNetXmlElementToEncrypt: DotNet XmlElement;
        DotNetEncryptedDataBytes, DotNetEncryptedKeyBytes : DotNet Array;
        DotNetEncryptedData: DotNet EncryptedData;
        DotNetEncryptionMethod: DotNet EncryptionMethod;
        DotNetEncryptedKey: DotNet EncryptedKey;
        DotNetSymmetricAlgorithm: DotNet "Cryptography.SymmetricAlgorithm";
        DotNetX509Certificate2: DotNet X509Certificate2;
        DotNetCipherData: DotNet CipherData;
        DotNetRSA: DotNet RSA;
        DotNetKeyInfo: DotNet KeyInfo;
        DotNetKeyInfoX509Data: DotNet KeyInfoX509Data;
        DotNetKeyInfoEncryptedKey: DotNet KeyInfoEncryptedKey;
    begin
        //Initialize EncryptedXml
        DotNetEncryptedXml := DotNetEncryptedXml.EncryptedXml();

        //Convert the XML document to a DotNet XML document and preserve whitespace
        XmlDotNetConvert.ToDotNet(XmlDocument, DotNetXmlDocument, true);

        //Get the DotNet XML element to encrypt
        DotNetXmlElementToEncrypt := DotNetXmlDocument.GetElementsByTagName(ElementToEncrypt).Item(0);
        if IsNull(DotNetXmlElementToEncrypt) then
            Error(ElementNotFoundErr, ElementToEncrypt);

        //Initialize an instance of a DotNet symmetric algorithm
        SymmetricAlgorithmInterface := SymmetricAlgorithm;
        SymmetricAlgorithmInterface.GetInstance(DotNetSymmetricAlgorithm);

        //Encrypt the data using the symmetric algorithm
        DotNetEncryptedDataBytes :=
            DotNetEncryptedXml.EncryptData(DotNetXmlElementToEncrypt, DotNetSymmetricAlgorithm, false);

        //Create a EncryptedData XML element
        DotNetEncryptedData := DotNetEncryptedData.EncryptedData();
        DotNetEncryptedData.CipherData().CipherValue := DotNetEncryptedDataBytes;
        DotNetEncryptedData.Type := XmlEncElementUrlTok;
        DotNetEncryptedData.EncryptionMethod :=
            DotNetEncryptionMethod.EncryptionMethod(SymmetricAlgorithmInterface.XmlEncrypmentMethodUrl());

        //Encrypt the symmetric algorithm key using the public key from a X509Certificate2.
        X509Certificate2Impl.InitializeX509Certificate(X509CertBase64Value, X509CertPassword, DotNetX509Certificate2);
        DotNetRSA := DotNetX509Certificate2.PublicKey."Key"();
        DotNetEncryptedKeyBytes := DotNetEncryptedXml.EncryptKey(DotNetSymmetricAlgorithm."Key", DotNetRSA, false);

        //Create an EncryptedKey XML element
        DotNetEncryptedKey := DotNetEncryptedKey.EncryptedKey();
        DotNetEncryptedKey.CipherData := DotNetCipherData.CipherData(DotNetEncryptedKeyBytes);
        DotNetEncryptedKey.EncryptionMethod := DotNetEncryptionMethod.EncryptionMethod(XmlEncRSA15UrlTok);
        DotNetKeyInfoX509Data := DotNetKeyInfoX509Data.KeyInfoX509Data(DotNetX509Certificate2.RawData);
        DotNetEncryptedKey.KeyInfo.AddClause(DotNetKeyInfoX509Data);

        //Create a KeyInfo XML element and add the EncryptedKey to it
        DotNetKeyInfo := DotNetKeyInfo.KeyInfo();
        DotNetKeyInfoEncryptedKey := DotNetKeyInfoEncryptedKey.KeyInfoEncryptedKey(DotNetEncryptedKey);
        DotNetKeyInfo.AddClause(DotNetKeyInfoEncryptedKey);
        DotNetEncryptedData.KeyInfo := DotNetKeyInfo;

        //Replace the original XML element with the encypted one
        DotNetEncryptedXml.ReplaceElement(DotNetXmlElementToEncrypt, DotNetEncryptedData, false);

        //Convert the encrypted DotNet XML Document to an XML document
        XmlDotNetConvert.FromDotNet(DotNetXmlDocument, XmlDocument);
    end;

    procedure DecryptDocument(var EncryptedDocument: XmlDocument; EncryptionKey: SecretText; SignatureAlgorithm: Enum SignatureAlgorithm): Boolean
    var
        XmlDotNetConvert: Codeunit "Xml DotNet Convert";
        DotNetXmlNamespaceManager: DotNet XmlNamespaceManager;
        DotNetXmlDocument: DotNet XmlDocument;
        DotNetEncryptedNodes: DotNet XmlNodeList;
        DotNetEncryptedNode: DotNet XmlNode;
        DotNetAsymmetricAlgorithm: DotNet AsymmetricAlgorithm;
        SignatureAlgorithmInterface: Interface "Signature Algorithm v2";
    begin
        //Convert the XmlDocument to a DotNet XmlDocument
        XmlDotNetConvert.ToDotNet(EncryptedDocument, DotNetXmlDocument, true);

        //Get the asymmetric algorithm instance to be used for decrypting the symmetric session key
        SignatureAlgorithmInterface := SignatureAlgorithm;
        SignatureAlgorithmInterface.FromSecretXmlString(EncryptionKey);
        SignatureAlgorithmInterface.GetInstance(DotNetAsymmetricAlgorithm);

        //Find all encrypted data elements and decrypt them
        DotNetXmlNamespaceManager := DotNetXmlNamespaceManager.XmlNamespaceManager(DotNetXmlDocument.NameTable);
        DotNetXmlNamespaceManager.AddNamespace('xenc', XmlEncUrlTok);
        DotNetEncryptedNodes := DotNetXmlDocument.SelectNodes('//xenc:EncryptedData', DotNetXmlNamespaceManager);
        foreach DotNetEncryptedNode in DotNetEncryptedNodes do
            DecryptDataElement(DotNetEncryptedNode, DotNetAsymmetricAlgorithm);

        //Convert the decrypted DotNet XML document to an XML document
        XmlDotNetConvert.FromDotNet(DotNetXmlDocument, EncryptedDocument, true);

        exit(true);
    end;

    local procedure DecryptDataElement(DotNetXmlElement: DotNet XmlElement; DotNetAsymmetricAlgorithm: DotNet AsymmetricAlgorithm): Boolean
    var
        SymmetricAlgorithmInterface: Interface SymmetricAlgorithm;
        DotNetEncryptedXml: DotNet EncryptedXml;
        DotNetEncryptedData: DotNet EncryptedData;
        DotNetKeyInfoClause: DotNet KeyInfoClause;
        DotNetKeyInfoEncryptedKey: DotNet KeyInfoEncryptedKey;
        DotNetEncryptedKey: DotNet EncryptedKey;
        DotNetCipherBytes, DotNetKeyBytes, DecryptedData : DotNet Array;
        DotNetSymmetricAlgorithm: DotNet "Cryptography.SymmetricAlgorithm";
        Ordinal: Integer;
    begin
        //Initialize a instance of the DotNet EncryptedData class.
        DotNetEncryptedData := DotNetEncryptedData.EncryptedData();
        DotNetEncryptedData.LoadXml(DotNetXmlElement);

        //Get the symmetric algorithm implementation from the KeyAlgorithm URL.
        foreach Ordinal in Enum::SymmetricAlgorithm.Ordinals() do begin
            SymmetricAlgorithmInterface := Enum::SymmetricAlgorithm.FromInteger(Ordinal);
            if SymmetricAlgorithmInterface.XmlEncrypmentMethodUrl() = DotNetEncryptedData.EncryptionMethod.KeyAlgorithm then begin
                SymmetricAlgorithmInterface.GetInstance(DotNetSymmetricAlgorithm);
                break;
            end;
        end;

        if IsNull(DotNetSymmetricAlgorithm) then
            Error(UnsupportedSymmetricAlgorithmErr);

        //Find EncryptedKey KeyInfo
        foreach DotNetKeyInfoClause in DotNetEncryptedData.KeyInfo do begin
            DotNetKeyInfoEncryptedKey := DotNetKeyInfoClause;
            if not IsNull(DotNetKeyInfoEncryptedKey) then begin
                DotNetEncryptedKey := DotNetKeyInfoEncryptedKey.EncryptedKey();
                DotNetCipherBytes := DotNetEncryptedKey.CipherData.CipherValue;
                //Decrypt the embedded symmetric key using the specified asymmetric key
                DotNetKeyBytes := DotNetEncryptedXml.DecryptKey(DotNetCipherBytes, DotNetAsymmetricAlgorithm, false);
                DotNetSymmetricAlgorithm."Key" := DotNetKeyBytes;
                break;
            end;
        end;

        //Decrypt the actual data using the symmetric key
        DotNetEncryptedXml := DotNetEncryptedXml.EncryptedXml();
        DecryptedData := DotNetEncryptedXml.DecryptData(DotNetEncryptedData, DotNetSymmetricAlgorithm);

        //Replace the encrypted data with the decrypted XML
        DotNetEncryptedXml.ReplaceData(DotNetXmlElement, DecryptedData);

        exit(true);
    end;

    procedure DecryptKey(EncryptedKey: XmlElement; EncryptionKey: SecretText; UseOAEP: Boolean; var KeyBase64Value: Text; SignatureAlgorithm: Enum SignatureAlgorithm): Boolean
    var
        XmlDocument: XmlDocument;
        XmlNamespaceManager: XmlNamespaceManager;
        CipherValue: XmlNode;
        DotNetEncryptedXml: DotNet EncryptedXml;
        DotNetCipherBytes, DotNetKeyBytes : DotNet Array;
        DotNetConvert: DotNet Convert;
        DotNetAsymmetricAlgorithm: DotNet AsymmetricAlgorithm;
        SignatureAlgorithmInterface: Interface "Signature Algorithm v2";
    begin
        //Get the asymmetric algorithm instance to be used for decrypting the key
        SignatureAlgorithmInterface := SignatureAlgorithm;
        SignatureAlgorithmInterface.FromSecretXmlString(EncryptionKey);
        SignatureAlgorithmInterface.GetInstance(DotNetAsymmetricAlgorithm);

        //Get the XML document of the XML element
        if not EncryptedKey.GetDocument(XmlDocument) then
            exit(false);

        //Create a XmlNamespaceManager and find the CipherValue (the Base64 encoded encrypted key)
        XmlNamespaceManager.NameTable := XmlDocument.NameTable;
        XmlNamespaceManager.AddNamespace('xenc', XmlEncUrlTok);
        if not EncryptedKey.SelectSingleNode('//xenc:CipherValue', XmlNamespaceManager, CipherValue) then
            exit(false);

        //Get key bytes
        DotNetCipherBytes := DotNetConvert.FromBase64String(CipherValue.AsXmlElement().InnerText);

        //Decrypt the key
        DotNetKeyBytes := DotNetEncryptedXml.DecryptKey(DotNetCipherBytes, DotNetAsymmetricAlgorithm, UseOAEP);

        //Convert the key bytes to Base64
        KeyBase64Value := DotNetConvert.ToBase64String(DotNetKeyBytes);

        exit(true);
    end;
}

Procedures, 4

NameParametersReturnsAccessObsolete
Encrypt(var XmlDocument, Text, Text, SecretText)public-
Encrypt(var XmlDocument, Text, Text, SecretText, Enum SymmetricAlgorithm)public-
DecryptDocument(var XmlDocument, SecretText, Enum SignatureAlgorithm)Booleanpublic-
DecryptKey(XmlElement, SecretText, Boolean, var Text, Enum SignatureAlgorithm)Booleanpublic-