How to replace ProtectedKey in Enterprise Library 6.0 by .net 4.5 Cryptography API

Topics: Cryptography Application Block
May 23, 2013 at 12:45 PM
Hi All,

We need to migrate Enterprise Library 5.0 to 6.0 and we are currently using Cryptography Application Block of 5.0.

I am using following lines of code to import password protected keyfile into byte[] which is used in encryption/decryption using SymmetricAlgorithm.


Now my question is ... Enterprise Library 6.0 cryptography block is no longer available then How to read keyfile into byte[] using .net 4.5 Cryptograhy API ?
/// include namespace.
using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography;

private ProtectedKey ImportKeyFile(string keyPath,DataProtectionScope scope)
        {
            ProtectedKey key=null;
            
             FileStream fs = new FileStream(keyPath,FileMode.Open,FileAccess.Read);
             key = KeyManager.Read(fs, scope);
             return key;            
        }

Thanks
mp2013
May 23, 2013 at 3:48 PM
Hi,

The deprecated crypto block may be replaced by the System.Security.Cryptography namespace using many of the primitives for the most common tasks.

In particular, you can follow the Crypto Block source in v5 for that function (KeyManager.Read) and with just a couple lines of code implement that functionality. Once get the bytes from the file, use them to protect/unprotect the key with ProtectedData class.

Regards,
Hernan
May 24, 2013 at 9:53 AM
Yes Herman, that's right. thanks a lot.

Underlying ProctectedKey & KeyManager it's core .net api using ProtectedData. But when I tried using it to decrypt keyfile using following code I am getting Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain. error
byte[] decryptedKey  = ProtectedData.Unprotect(encryptedKey, null, scope)  

 // here I am passing scope = System.Security.Cryptography.DataProtectionScope.LocalMachine 
Actually, here I am trying to decrypting password- protected key file which was previoulsy exported by Enterprise Library 5.0 Configuration Console.

Do you know what is the reason behind it?

Thanks
mp2013
May 24, 2013 at 5:39 PM
HI,

How did you read the bytes of " encryptedKey"? Notice that the first four bytes of the saved key are for the version number so you should skip them in your stream reader like this:
    byte[] encryptedKey = new byte[protectedKeyStream.Length - 4L];
    protectedKeyStream.Read(encryptedKey, 0, encryptedKey.Length);
Regards,
Hernan
May 29, 2013 at 9:55 AM
Hi Herman,

I am doing the same way as you mentioned.

Thanks
mp2013
May 31, 2013 at 10:49 AM
Hi Herman,

I am using the same code as below in Enterprise Lib 5.0 source code. Still getting same error " Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain.".
private byte[] ReadEncryptedKey(Stream protectedKeyStream)
        {
            byte[] encryptedKey = new byte[protectedKeyStream.Length - versionNumberLength];
            protectedKeyStream.Read(encryptedKey, 0, encryptedKey.Length);

            return encryptedKey;
        }
Do you have any idea why this happens ? Also we have tried with crypt32.dll api method call using CryptUnprotectData. But getting same error.


Thanks
mp2013
May 31, 2013 at 12:13 PM
Hi mp2013,

Notice that you should start reading the stream after the versionNumberLength bytes. In this case it looks like the Read should start at that position and not '0' as your code above. In Entlib v5 the reader first validates the version number so the position is moved 4 bytes (versionNumberLength) before ReadEncryptedKey is called.
You may try setting the Position property to versionNumberLength before calling Read.

Regards,
Hernan
May 31, 2013 at 1:03 PM
Hi Herman,

I am also calling ValidateKeyVersion method before calling ReadEncryptedKey as same as in Ent. Lib 5.0 source code. But still I am getting the same "Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain" error.

Below is my whole code snippet. Please help here if you could find anything.
private byte[] ReadKeyFile(string keyPath, DataProtectionScope scope)
        {
               FileStream fs = new FileStream(keyPath, FileMode.Open, FileAccess.Read);
               ValidateKeyVersion(fs);
               byte[] encryptedKey = ReadEncryptedKey(fs);
               byte[] decryptedKey = ProtectedData.Unprotect(encryptedKey, entropy, scope);
                key = (byte[])decryptedKey.Clone();
                return key;
       }

internal const int versionNumber = 4321;
internal const int versionNumberLength = 4;
private void ValidateKeyVersion(Stream protectedKeyStream)
        {
            uint readVersionNumber = ReadVersionNumber(protectedKeyStream);
            if (readVersionNumber != versionNumber)
            {
                //throw new InvalidOperationException(Resources.IncorrectKeyVersionError);
                throw new Exception("Key versions do not match between encrypted key and decryption algorithm.");
            }
        }

 private uint ReadVersionNumber(Stream protectedKeyStream)
        {
            byte[] keyFileVersion = new byte[versionNumberLength];
            protectedKeyStream.Read(keyFileVersion, 0, keyFileVersion.Length);

            return BitConverter.ToUInt32(keyFileVersion, 0);
        }
 public byte[] ReadEncryptedKey(Stream protectedKeyStream)
        {
            byte[] encryptedKey = new byte[protectedKeyStream.Length - versionNumberLength];
            protectedKeyStream.Read(encryptedKey, 0, encryptedKey.Length);
            return encryptedKey;
        }
Thanks
mp2013
May 31, 2013 at 7:01 PM
The posted code seems to work OK for me for a key file generated by Enterprise Library 5. Just curious, if you are running in IIS?

A search suggests that the problem involves CRYPTPROTECT_PROMPTSTRUCT but it's not clear to me what the root cause is.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Jun 2, 2013 at 10:03 AM
Hi Herman,

Thanks again for reply.

I am not running this code in IIS. This code is in Window Service.


Also I have tried with CryptUnprotectData (crypt32.dll api calls) & but I am getting The data is invalid error. I am using following CRYPTPROTECT_PROMPTSTRUCT. Please let us know if you could find thing.
private void InitPromptStructure(ref CRYPTPROTECT_PROMPTSTRUCT _ps)
    {
        _ps.cbSize = Marshal.SizeOf(
                                  typeof(CRYPTPROTECT_PROMPTSTRUCT));
        _ps.dwPromptFlags = 0;
        _ps.hwndApp = NullPtr;
        _ps.szPrompt = null;
    }