安全代码审查:如何处理解密的对称密钥?

时间:2012-11-15 02:31:23

标签: c# security encryption azure rsa

在以下代码中:symmetricCryptoKey表示应始终受到保护的私人信息,因为它是加密对称密钥的解密版本。

问题:

  • 我是否可以在多租户环境(托管在Azure上)中保护内存,特别是与symmetricCryptoKey或keyCache的交互?

TL; DR代码示例

                byte[] symmetricCryptoKey = RSA.Decrypt(key.Key, true);

                AesManaged algorithm = new AesManaged();
                algorithm.IV = key.iv;
                algorithm.Key = symmetricCryptoKey;
                keyCache[key.Version] = algorithm;

完整代码文件... Codeplex source

using EncryptDecrypt.Exceptions;
using Microsoft.WindowsAzure.Storage;
using System;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace EncryptDecrypt
{
    /// <summary>
    /// Store the various encryption keys so that we don't need to load them from storage all the time
    /// </summary>
    /// <remarks>
    /// Is there something we should be doing to secure the memory used by this class?
    /// </remarks>
    internal class AzureTableCryptoKeyStore : IDisposable
    {
        private Dictionary<int, SymmetricAlgorithm> keyCache = new Dictionary<int, SymmetricAlgorithm>();
        internal CloudStorageAccount KeyStoreAccount { get; private set; }

        internal AzureTableCryptoKeyStore(CloudStorageAccount acct)
        {
            this.KeyStoreAccount = acct;

            SymmetricKeyStore keyTable = new SymmetricKeyStore(acct);
            List<SymmetricKey> allKeys = null;

            try
            {
                allKeys = keyTable.GetAllKeys();
            }
            catch (DataServiceQueryException dsq)
            {
                if (dsq.Response.StatusCode == (int)HttpStatusCode.NotFound)
                {
                    //Table hasn't been created, so there aren't any keys. Guess we'll just go with it. 
                    allKeys = new List<SymmetricKey>(0);
                }
                else
                {
                    throw new AzureTableCryptoInitializationException("Failed to load encryption keys from storage", dsq);
                }
            }
            catch (DataServiceClientException dsce)
            {
                if (dsce.StatusCode == (int)HttpStatusCode.NotFound)
                {
                    //Table hasn't been created, so there aren't any keys. Guess we'll just go with it. 
                    allKeys = new List<SymmetricKey>(0);
                }
                else
                {
                    throw new AzureTableCryptoInitializationException("Failed to load encryption keys from storage", dsce);
                }
            }
            catch (Exception ex)
            {
                throw new AzureTableCryptoInitializationException("Could not load encryption keys table", ex);
            }


            foreach (var key in allKeys)
            {
                try
                {
                    X509Certificate2 certificate = CertificateHelper.GetCertificateByThumbprint(key.CertificateThumbprint);
                    if (certificate == null)
                    {
                        //Can't find the cert for this key, just continue
                        continue;
                    }

                    RSACryptoServiceProvider RSA;
                    try
                    {
                        RSA = (RSACryptoServiceProvider)certificate.PrivateKey;
                    }
                    catch (CryptographicException)
                    {
                        throw new AzureTableCryptoPrivateKeyNotAccessibleException(key.Version, key.CertificateThumbprint);
                    }

                    byte[] symmetricCryptoKey = RSA.Decrypt(key.Key, true);

                    AesManaged algorithm = new AesManaged();
                    algorithm.IV = key.iv;
                    algorithm.Key = symmetricCryptoKey;
                    keyCache[key.Version] = algorithm;
                }
                catch (AzureTableCryptoException)
                {
                    //Just rethrow these
                    throw;
                }
                catch (Exception ex)
                {
                    throw new AzureTableCryptoInitializationException("Error initializing crypto key version " + key.Version, ex);
                }
            }
        }

        internal ICryptoTransform GetDecryptor(int version)
        {
            return GetAlgorithm(version).CreateDecryptor();
        }

        internal ICryptoTransform GetEncryptor(int version)
        {
            return GetAlgorithm(version).CreateEncryptor();
        }

        private SymmetricAlgorithm GetAlgorithm(int version)
        {
            SymmetricAlgorithm algo;
            if (!keyCache.TryGetValue(version, out algo))
            {
                throw new AzureTableCryptoNotFoundException(version);
            }
            return algo;
        }

        public void Dispose()
        {
            Dictionary<int, SymmetricAlgorithm> cache = keyCache;
            keyCache = null;

            foreach (var algo in cache.Values)
            {
                algo.Clear();
                algo.Dispose();
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

您可以使用一些诸如SecureString之类的东西来帮助防止内存嗅探器抓取密钥:

How is SecureString "encrypted" and still usable?

它们当然不是完美的,因为如果程序本身具有解密它的所有信息,那么计算机中的一切都是查看字符串所必需的。它确实减少了可能受到损害的情况。