videoguy
videoguy

Reputation: 1898

How to encrypt data in one instance of Windows and decrypt in different OS instance running on the same system?

This is an extension of How to encrypt data in one app and decrypt it in different Windows app with RSA keys tied to local system?.

From the linked question above, I was able to encrypt/decrypt data between multiple apps under same OS instance (i.e. Windows10). Then I boot into 2nd instance of Windows 10 on the same system as part of dual boot setup. The app in the link above couldn't decrypt data. I like to accomplish encryption/decryption between applications running in separate Windows 10 instances running on same physical system. I thought persisting a key will save the key in TPM and an app from different OS instance can get to the same key and use it.

Does TPM or CNG stack on Windows10 let you create TPM sealed keys or private keys that only exist in TPM, but can be accessed from any Windows OS instance running on the same system?

On 8/3/2022:

Updated code snippet based on @bartonjs suggestion below. Now it fails to Decrypt throwing an exception that the operation is not supported.

using System;
using System.IO;
using System.Security.Cryptography;

namespace TPMCrypto
{
    class Program
    {
        static byte[] data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };

        static byte[] privateKey;
        private static byte[] encrypted;
        private static byte[] decrypted;
        

        static void Main(string[] args)
        {
            const string MyKey = "MyRSAKey";
            CngKey cngKey = null;
            string cmd = args.Length > 0 ? args[0] : "";

            try
            {
                CngProvider theTPMProvider = new CngProvider("Microsoft Platform Crypto Provider");
                CngKeyCreationParameters cng = new CngKeyCreationParameters
                {
                    ExportPolicy = CngExportPolicies.None,
                    KeyUsage = CngKeyUsages.AllUsages,
                    KeyCreationOptions = CngKeyCreationOptions.MachineKey,
                    Provider = theTPMProvider,
                    Parameters =
                    {
                        new CngProperty("Length", BitConverter.GetBytes(2048), CngPropertyOptions.Persist),
                    },
                };

                if (!CngKey.Exists(MyKey, theTPMProvider, CngKeyOpenOptions.MachineKey))
                {
                    Console.WriteLine("Creating rsaKey");
                    cngKey = CngKey.Create(CngAlgorithm.Rsa, MyKey, cng);
                }
                else
                {
                    Console.WriteLine("Opening rsaKey");
                    cngKey = CngKey.Open(MyKey, theTPMProvider, CngKeyOpenOptions.MachineKey);
                }

                RSACng rsaKey = new RSACng(cngKey);
                //privateKey = rsaKey.Key.Export(CngKeyBlobFormat.GenericPrivateBlob);
                //string prvResult = ByteArrayToHexString(privateKey, 0, privateKey.Length);

                //Console.WriteLine("\nPrivate key - length = " + privateKey.Length + "\n" + prvResult + "\n");

                const string FILE_PATH = @"\temp\tpmtests\encryptedblob.dat";

                // Encrypt / decrypt
                if (cmd == "readfromfile")
                {
                    
                    Directory.CreateDirectory(Path.GetDirectoryName(FILE_PATH));
                    encrypted = File.ReadAllBytes(FILE_PATH);
                }
                else if (cmd == "deletekey")
                {
                    cngKey.Delete();
                    return;
                }
                else
                {
                    encrypted = Encrypt(rsaKey, data);
                    Console.WriteLine("The encrypted blob: ");
                    Console.WriteLine(ByteArrayToHexString(encrypted, 0, encrypted.Length));
                    File.WriteAllBytes(FILE_PATH, encrypted);
                }

                
                decrypted = Decrypt(rsaKey, encrypted);

                bool result = ByteArrayCompare(data, decrypted);

                if (result)
                    Console.WriteLine("Encrypt / decrypt works");
                else
                    Console.WriteLine("Encrypt / decrypt fails");
            }

            catch (Exception e)
            {
                Console.WriteLine("Exception " + e.Message);
            }
            finally
            {
                if (cngKey != null)
                    cngKey.Dispose();
            }

            Console.ReadLine();
        }

        static bool ByteArrayCompare(byte[] a1, byte[] a2)
        {
            if (a1.Length != a2.Length)
                return false;

            for (int i = 0; i < a1.Length; i++)
                if (a1[i] != a2[i])
                    return false;

            return true;
        }

        public static string ByteArrayToHexString(byte[] bytes, int start, int length)
        {
            string delimitedStringValue = BitConverter.ToString(bytes, start, length);
            return delimitedStringValue.Replace("-", "");
        }

        public static byte[] Sign512(byte[] data, byte[] privateKey)
        {
            CngKey key = CngKey.Import(privateKey, CngKeyBlobFormat.GenericPrivateBlob);
            RSACng crypto = new RSACng(key);
            return crypto.SignData(data, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
        }

        public static bool VerifySignature512(byte[] data, byte[] signature, byte[] publicKey)
        {
            CngKey key = CngKey.Import(publicKey, CngKeyBlobFormat.GenericPublicBlob);
            RSACng crypto = new RSACng(key);
            return crypto.VerifyData(data, signature, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
        }

        public static byte[] Encrypt(byte[] publicKey, byte[] data)
        {
            CngKey key = CngKey.Import(publicKey, CngKeyBlobFormat.GenericPublicBlob);
            RSACng crypto = new RSACng(key);
            var result = Encrypt(crypto, data);
            return result;
        }

        public static byte[] Encrypt(RSACng crypto, byte[] data)
        {
            if (null == crypto)
                return null;
            var result = crypto.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
            return result;
        }

        public static byte[] Decrypt(byte[] privateKey, byte[] data)
        {
            CngKey key = CngKey.Import(privateKey, CngKeyBlobFormat.GenericPrivateBlob);
            RSACng crypto = new RSACng(key);
            var result = Decrypt(crypto, data);
            return result;
        }

        public static byte[] Decrypt(RSACng aKey, byte[] data)
        {
            if (null == aKey)
                return null;
            var result = aKey.Decrypt(data, RSAEncryptionPadding.OaepSHA256);
            return result;
        }

    }
}

Update 8/4/22

With padding change above to SHA256, I was able to encrypt/decrypt data using TPM keys within same OS session as well as separate OS sessions of same OS instance.

Then I booted the system using different Windows OS instance on a USB key. Ran the application. It failed with the following call stack.

>TPMCrypto.exe readfromfile
Exception Message: Unknown error "-1073741275".
Exception stack: System.Security.Cryptography.CryptographicException: Unknown error "-1073741275".
   at System.Security.Cryptography.NCryptNative.OpenStorageProvider(String providerName)
   at System.Security.Cryptography.CngKey.Exists(String keyName, CngProvider provider, CngKeyOpenOptions options)
   at TPMCrypto.Program.Main(String[] args) in TPMCrypto\Program.cs:line 37

The app is failing to open TPM KSM and the app is running with admin privs. The same app with "Microsoft Software Storage Provider" was able to open the KSP, it is the TPM it is failing to open KSP.

It is not clear what the error code -1073741275 mean.

Upvotes: 0

Views: 741

Answers (1)

Hugh
Hugh

Reputation: 106

While I can't comment on the code in the link, I do know that keys not only persist in TPM for use on the same drive by two OS's, but stores keys for two or more drives. Where you have a Windows install on C: and one on D:, for example. In other words, it should certainly persist for two OS's on the same drive (different partitions).

So the answer to your last question in the post is yes, the TPM stack will let you create more than one key that should persist for use by another Windows OS instance.. I'm only guessing but I think the problem may be that due to the tiny memory of the TPM it can only hold three transient keys at once. It is possible that your app (or something else) is overwriting the key you need to stay persistent when you switch to the other OS?

Mine is an Intel 2.0 TPM but even if yours is not, there's valuable information here that I believe may help: https://www.intel.com/content/dam/support/us/en/documents/boardsandkits/D915GMH_TPM_QuickRefGuide01.pdf

Upvotes: 0

Related Questions