8
votes

I have managed to encrypt and decrypt xml documents using examples on MSDN. http://msdn.microsoft.com/en-us/library/ms229744.aspx and http://msdn.microsoft.com/en-us/library/ms229943.aspx

This is all done according to the W3C XML encryption standard(XML Enc).

It all works good. My problem is that one xml document is intended for 2 or 3 recipients. I want to encrypt same xml with multiple keys (X509 certificate public key) so that document could be decrypted by multiple recipients.

This is all possible according to the W3C XML encryption standard by using multiple EncryptionKey elements that contain encrypted symmetric session key.

I couldn't find any example on how to achieve this in .Net using standard cryptography classes.

This must be implemented in .NET C#.

Is there a way to do this or code example somewhere?

1
Welcome to Stackoverflow, and thanks for the interesting question. Let's hope somebody with enough .NET knowledge can answer it. Note that you should always include a language tag. You don't need to put the language in the title though. - Maarten Bodewes
I'm not even a C# developer, but I think Stackoverflow should know this... - Maarten Bodewes
I dont think its a good idea: arstechnica.com/business/2011/10/…, here is the paper. - Jeremy Thompson
@JeremyThompson I know of that attack. Even better, I implemented a similar Padding Oracle attack on XML encryption before I read the paper. Furthermore I wrote an implementation for WDSL security that always checks the XML signature before decryption before they published the paper. As in all attacks, it depends on the threat model if the attack applies. It certainly has little to do with having multiple recipients or not. Always authenticating all your data in a communication channel before decryption should be enough. - Maarten Bodewes
I know about XML encryption weakness but we will be using private secured line for data exchange and it will not be used in web services. We are using standalone xml documents that contain base64 encoded data. We will use Xml digital signature above encrypted data. The only problem is how to avoid sending multiple documents encrypted with different keys. The only elegant solution is encrypting for multiple recipients within the same document. Max number of recipients will be 3 permanently. - Bokina

1 Answers

5
votes

The EncryptedElement class can take as many EncryptedKeys as you want. As long as the other party can correctly identify their EncryptedKey (using the Recipient or the KeyInfoName elements), you should not have any problems:

// example xml
XmlDocument xdoc = new XmlDocument();
xdoc.PreserveWhitespace = true;
xdoc.LoadXml(@"<root><encryptme>hello world</encryptme></root>");

var elementToEncrypt = (XmlElement)xdoc.GetElementsByTagName("encryptme")[0];

// keys
// rsa keys would normally be pulled from a store
RSA rsaKey1 = new RSACryptoServiceProvider();
RSA rsaKey2 = new RSACryptoServiceProvider();
var publicKeys = new[] { rsaKey1, rsaKey2 };

string sessKeyName = "helloworldkey";
var sessKey = new RijndaelManaged() { KeySize = 256 };

// encrypt
var encXml = new EncryptedXml();
var encryptedElement = new EncryptedData()
{
    Type = EncryptedXml.XmlEncElementUrl,
    EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url),
    KeyInfo = new KeyInfo()
};
encryptedElement.CipherData.CipherValue = encXml.EncryptData(elementToEncrypt, sessKey, false);
encryptedElement.KeyInfo.AddClause(new KeyInfoName(sessKeyName));

// encrypt the session key and add keyinfo's
int keyID = 0;
foreach (var pk in publicKeys)
{
    var encKey = new EncryptedKey()
    {
        CipherData = new CipherData(EncryptedXml.EncryptKey(sessKey.Key, pk, false)),
        EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url),
        Recipient = string.Format("recipient{0}@foobar.com", ++keyID),
        CarriedKeyName = sessKeyName,
    };
    encKey.KeyInfo.AddClause(new KeyInfoName(encKey.Recipient));
    encryptedElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(encKey));
}

// update the xml
EncryptedXml.ReplaceElement(elementToEncrypt, encryptedElement, false);

// show the result
Console.Write(xdoc.InnerXml);
Console.ReadLine();
Console.WriteLine(new string('-', 80));

Produces

<root>
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
        <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>helloworldkey</KeyName>
            <EncryptedKey Recipient="[email protected]" xmlns="http://www.w3.org/2001/04/xmlenc#">
                <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
                <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                    <KeyName>[email protected]</KeyName>
                </KeyInfo>
                <CipherData>
                    <CipherValue>bmVT4SuAgWto6NJoTnUhrwaQ5/bWx39WKfs8y/QEQbaEBqdvl2Wa3woQGZxfigZ2wsWZQJFW0YGMII0W6AATnsqGOOVEbdGxmnvXRISiRdhcyNHkHot0kDK987y446ws5CZQQuz8inGq/SNrhiK6RyVnBE4ykWjrJyIS5wScwqA=</CipherValue>
                </CipherData>
                <CarriedKeyName>helloworldkey</CarriedKeyName>
            </EncryptedKey>
            <EncryptedKey Recipient="[email protected]" xmlns="http://www.w3.org/2001/04/xmlenc#">
                <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
                <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                    <KeyName>[email protected]</KeyName>
                </KeyInfo>
                <CipherData>
                    <CipherValue>oR8NPTm1NasWeDXBjayLk+p9/5RTWOZwNJHUMTQpZB9v1Aasi75oSjGqSqN0HMTiviw6NWz8AvHB9+i08L4Hw8JRDLxZgjaKqTGu31wXmM3Vc0CoYQ15AWMZN4q4tSxDhwuT8fp9SN+WFBm+M3w3bcPoooAazzDHK3ErzfXzYiU=</CipherValue>
                </CipherData>
                <CarriedKeyName>helloworldkey</CarriedKeyName>
            </EncryptedKey>
        </KeyInfo>
        <CipherData>
            <CipherValue>ohjWIEFf2WO6v/CC+ugd7uxEKGJlxgdT9N+t3MhoTIyXHqT5VlknWs0XlAhcgajkxKFjwVO3p413eRSMTLXKCg==</CipherValue>
        </CipherData>
    </EncryptedData>
</root>

To decrypt the document, you must provide a mapping between the key name and the private key of the certificate:

// Decrypt
string myKeyName = "[email protected]";

// specify we want to use the key for recipient1
var encryptedDoc = new EncryptedXml(xdoc);
encryptedDoc.AddKeyNameMapping(myKeyName, rsaKey1);
encryptedDoc.Recipient = myKeyName;

// Decrypt the element.
encryptedDoc.DecryptDocument();

// show the result
Console.Write(xdoc.InnerXml);
Console.ReadLine();

Result:

<root><encryptme>hello world</encryptme></root>