Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EncryptedXml DecryptDocument method throws "Bad Data" exception

I wrote a code block for Encrypt/Decrypt Streams. The code is working in my local machine. But when I publish my code on web The Decryption functions throws "Bad Data" exception Here is the my Encrypton and Decryption functions

private static MemoryStream EncryptStream(XmlDocument xmlDoc, XmlElement elementToEncrypt, string password)
{
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = password;
    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
    RijndaelManaged sessionKey = null;
    try
    {

        if (xmlDoc == null)
            throw new ArgumentNullException("xmlDoc");
        if (rsaKey == null)
            throw new ArgumentNullException("rsaKey");
        if (elementToEncrypt == null)
            throw new ArgumentNullException("elementToEncrypt");

        sessionKey = new RijndaelManaged();
        sessionKey.KeySize = 256;

        EncryptedXml eXml = new EncryptedXml();
        byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);

        EncryptedData edElement = new EncryptedData();
        edElement.Type = EncryptedXml.XmlEncElementUrl;
        edElement.Id = EncryptionElementID;
        edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);

        EncryptedKey ek = new EncryptedKey();
        byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, rsaKey, false);
        ek.CipherData = new CipherData(encryptedKey);
        ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);

        edElement.KeyInfo = new KeyInfo();

        KeyInfoName kin = new KeyInfoName();
        kin.Value = KeyName;

        ek.KeyInfo.AddClause(kin);
        edElement.CipherData.CipherValue = encryptedElement;
        edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));

        EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);

        if (sessionKey != null)
        {
            sessionKey.Clear();
        }
        rsaKey.Clear();
        MemoryStream stream = new MemoryStream();
        xmlDoc.Save(stream);
        stream.Position = 0;
        Encoding encodeing = System.Text.UnicodeEncoding.Default;
        return stream;
    }
    catch (Exception e)
    {
        if (sessionKey != null)
        {
            sessionKey.Clear();
        }
        rsaKey.Clear();
        throw (e);
    }
}

private static MemoryStream DecryptStream(XmlDocument xmlDoc, string password)
{
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = password;
    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
    EncryptedXml exml = null;
    try
    {
        if (xmlDoc == null)
            throw new ArgumentNullException("xmlDoc");
        if (rsaKey == null)
            throw new ArgumentNullException("rsaKey");

        exml = new EncryptedXml(xmlDoc);
        exml.AddKeyNameMapping(KeyName, rsaKey);

        exml.DecryptDocument();
        rsaKey.Clear();

        MemoryStream outStream = new MemoryStream();
        xmlDoc.Save(outStream);
        outStream.Position = 0;
        return outStream;
    }
    catch (Exception e)
    {
        rsaKey.Clear();
        throw (e);
    }
}

the exception is thrown on "exml.DecryptDocument();" line.

Do you have any idea about problem and the solution?

Edit:

in MSDN page, there is remark which is as follows

To use XML Encryption with X.509 certificates, you must have the Microsoft Enhanced Cryptographic Provider installed and the X.509 certificate must use the Enhanced Provider. If you do not have the Microsoft Enhanced Cryptographic Provider installed or the X.509 certificate does not use the Enhanced Provider, a CryptographicException with an "Unknown Error" will be thrown when you decrypt an XML document.

Do you have any idea about "Microsoft Enhanced Cryptographic Provider" and "X.509 certificate"? And Can my problem be related to this those?

like image 580
srcnaks Avatar asked Mar 11 '13 13:03

srcnaks


2 Answers

The reason the Decryption functions throws "Bad Data" exception when trying to Decrypt on another PC is that the CspParameters is linked to the session on the PC where the Encryption was run.

The cspParams object will need to be embedded and encrypted in the XML to enable Decryption on another PC. Luckily there is EncryptionProperty we can use for this.

private static MemoryStream EncryptStream(XmlDocument xmlDoc, XmlElement elementToEncrypt, string password)
{
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = password;
    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
    RijndaelManaged sessionKey = null;
    try
    {
        if (xmlDoc == null)
            throw new ArgumentNullException("xmlDoc");
        if (rsaKey == null)
            throw new ArgumentNullException("rsaKey");
        if (elementToEncrypt == null)
            throw new ArgumentNullException("elementToEncrypt");

        sessionKey = new RijndaelManaged();
        sessionKey.KeySize = 256;

        EncryptedXml eXml = new EncryptedXml();
        byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);

        EncryptedData edElement = new EncryptedData();
        edElement.Type = EncryptedXml.XmlEncElementUrl;
        edElement.Id = EncryptionElementID;
        edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);

        EncryptedKey ek = new EncryptedKey();
        byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, rsaKey, false);
        ek.CipherData = new CipherData(encryptedKey);
        ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);

        // Save some more information about the key using the EncryptionProperty element.

        // Create a new "EncryptionProperty" XmlElement object. 
        var property = new XmlDocument().CreateElement("EncryptionProperty", EncryptedXml.XmlEncNamespaceUrl);

        // Set the value of the EncryptionProperty" XmlElement object.
        property.InnerText = RijndaelManagedEncryption.EncryptRijndael(Convert.ToBase64String(rsaKey.ExportCspBlob(true)),
                        "Your Salt string here");

        // Create the EncryptionProperty object using the XmlElement object. 
        var encProperty = new EncryptionProperty(property);

        // Add the EncryptionProperty object to the EncryptedKey object.
        ek.AddProperty(encProperty);

        edElement.KeyInfo = new KeyInfo();

        KeyInfoName kin = new KeyInfoName();
        kin.Value = KeyName;

        ek.KeyInfo.AddClause(kin);
        edElement.CipherData.CipherValue = encryptedElement;
        edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));

        EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);

        if (sessionKey != null)
        {
            sessionKey.Clear();
        }
        rsaKey.Clear();
        MemoryStream stream = new MemoryStream();
        xmlDoc.Save(stream);
        stream.Position = 0;
        Encoding encodeing = System.Text.UnicodeEncoding.Default;
        return stream;
    }
    catch (Exception)
    {
        if (sessionKey != null)
        {
            sessionKey.Clear();
        }
        rsaKey.Clear();
        throw;
    }
}

private static MemoryStream DecryptStream(XmlDocument xmlDoc, string password)
{
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = password;
    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);

    var keyInfo = xmlDoc.GetElementsByTagName("EncryptionProperty")[0].InnerText;
        rsaKey.ImportCspBlob(
            Convert.FromBase64String(RijndaelManagedEncryption.DecryptRijndael(keyInfo,
                "Your Salt string here")));
    EncryptedXml exml = null;
    try
    {
        if (xmlDoc == null)
            throw new ArgumentNullException("xmlDoc");
        if (rsaKey == null)
            throw new ArgumentNullException("rsaKey");

        exml = new EncryptedXml(xmlDoc);
        exml.AddKeyNameMapping(KeyName, rsaKey);

        exml.DecryptDocument();
        rsaKey.Clear();

        MemoryStream outStream = new MemoryStream();
        xmlDoc.Save(outStream);
        outStream.Position = 0;
        return outStream;
    }
    catch (Exception)
    {
        rsaKey.Clear();
        throw;
    }
}

Have a look here for the RijndaelManagedEncryption class.

like image 190
Morodin Avatar answered Sep 18 '22 11:09

Morodin


Do not reinvent cryptography protocols. You will get it wrong. Case in point, mishandling the RSA key stored in the CSPs and expect them to magically appear on any machine.

To encrypt data in transfer, use SSL/TLS. .Net offers it out-of-the-box with SslStream. For WCF see How to: Configure an IIS-hosted WCF service with SSL.

like image 36
Remus Rusanu Avatar answered Sep 21 '22 11:09

Remus Rusanu