読者です 読者をやめる 読者になる 読者になる
ようこそ。睡眠不足なプログラマのチラ裏です。

CSPで暗号化する場合、非対称秘密キーをローカルなんかに平文で保存しないでください。

プログラミング C#2.0 C#3.0

[方法:キー コンテナに非対称キーを格納する]
http://msdn.microsoft.com/ja-jp/library/tswxhw92.aspx

非対称秘密キーは、ローカル コンピュータにそのまま平文として保存しないでください。
秘密キーを格納する必要がある場合は、キー コンテナを使用することをお勧めします。
キー コンテナの詳細については、プラットフォーム SDK ドキュメント
(http://www.microsoft.com/japan/msdn) の「CryptoAPI」セクションを参照してください。

.NET(C#またはVB.NET)でRSA暗号化を行う場合、
素因数分解の原理を用いてゼロからゴリゴリ実装するゼ!というマゾはまずいないと思います。
通常、暗号サービスプロバイダ(CSP : Cryptographic Service Provider)によって提供された
RSA暗号化アルゴリズムを使用するRSACryptoServiceProviderクラスを使うのが一般的でしょう。


CSPによって提供される暗号化の処理というのは、なんらかの暗号化エンジン*1に鍵の種と暗号化されるデータを渡し、
その結果を受け取ることを言います。このときCSP内部の鍵が格納される場所をキーコンテナといいます。
このキーコンテナに格納された非対称秘密キーは、明示的に削除しない限り、安全に保存され続けます。
ですから、アプリケーションが終了したとしても、再び同じ秘密キーを利用することができます。
もちろんマシンを再起動したとしても、キーコンテナに格納された秘密キーを再び得ることができます。


というわけで、セキュリティ的な意味で、暗号の非対称秘密キーを保存しておく必要があるような場合は、
キーコンテナを利用してください。お願いします。


非対称秘密キーをキーコンテナに保存するサンプル


RSACryptoServiceProviderクラスでキーコンテナを利用したサンプルです。
WindowsXp以降のバージョンか否か判断することで、OAEPパディングの利用有無を自動的に判断しています。

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

namespace ConsoleApplication1
{
    public static partial class Utility
    {
        /// <summary>
        /// RSA暗号の公開鍵と秘密鍵を生成します
        /// </summary>
        /// <param name="keyContainerName">キーコンテナ名</param>
        /// <returns></returns>
        public static KeyValuePair<string, string> CreateKeysOfRSA(string keyContainerName)
        {
            var cspp = new CspParameters();
            cspp.KeyContainerName = keyContainerName;
            var rsa = new RSACryptoServiceProvider(cspp);
            return new KeyValuePair<string, string>(rsa.ToXmlString(false), rsa.ToXmlString(true));
        }

        /// <summary>
        /// 公開鍵を使って文字列をRSA暗号化します
        /// </summary>
        /// <param name="str">暗号化する文字列</param>
        /// <param name="publicKey">暗号化に使用する公開鍵(XML形式)</param>
        /// <returns>暗号化された文字列</returns>
        public static string EncryptOfRSA(string source, string publicKey)
        {
            var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(publicKey);

            byte[] data = Encoding.UTF8.GetBytes(source);
            //暗号化(Windows XP以降の場合のみ2項目にtrueを指定してOAEPパディングが使える)
            byte[] encryptedData = rsa.Encrypt(data, IsXpOrMore());

            return System.Convert.ToBase64String(encryptedData);
        }

        /// <summary>
        /// 秘密鍵を使って文字列をRSA復号化します
        /// </summary>
        /// <param name="str">Encryptメソッドにより暗号化された文字列</param>
        /// <param name="privateKey">復号化に必要な秘密鍵(XML形式)</param>
        /// <returns>復号化された文字列</returns>
        public static string DecryptOfRSA(string str, string privateKey)
        {
            var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(privateKey);

            byte[] data = System.Convert.FromBase64String(str);
            //復号化
            byte[] decryptedData = rsa.Decrypt(data, IsXpOrMore());

            return System.Text.Encoding.UTF8.GetString(decryptedData);
        }

        /// <summary>
        /// キーコンテナからRSA暗号キーを削除します。
        /// </summary>
        /// <param name="keyContainerName"></param>
        public static void DeleteKeyFromContainerOfRSA(string keyContainerName)
        {
            CspParameters cp = new CspParameters();
            cp.KeyContainerName = keyContainerName;
            var rsa = new RSACryptoServiceProvider(cp);
            rsa.PersistKeyInCsp = false;
            rsa.Clear();
        }

        /// <summary>
        /// WindowsXp以降のバージョンか否かを取得します。
        /// </summary>
        /// <returns></returns>
        public static bool IsXpOrMore()
        {
            OperatingSystem osInfo = Environment.OSVersion;
            switch (osInfo.Platform)
            {
                case PlatformID.Win32Windows:  // Windows 9x系
                    return false;
                case PlatformID.Win32NT:  // Windows NT系
                    if (osInfo.Version.Major == 4) return false;//Windows NT 4.0
                    if (osInfo.Version.Minor <= 0) return false; //Windows2000
                    return true;
                default: return true;
            }
        }
    }
}


お試し

using System;

namespace ConsoleApplication1
{
    static class Program
    {
        static void Main()
        {
            var keyContainerName = "キーコンテナの名前です。なんでもいいです。";

            for (var i = 0; i < 4; i++)
            {
                var keys = Utility.CreateKeysOfRSA(keyContainerName);
                Console.WriteLine("{0}:{1}", keys.Key, keys.Value);
                Console.WriteLine();

                Console.WriteLine("暗号化");
                var enc = Utility.EncryptOfRSA("あいうえお", keys.Key);
                Console.WriteLine(enc);
                Console.WriteLine();
                Console.WriteLine("復号化");
                var dec = Utility.DecryptOfRSA(enc, keys.Value);
                Console.WriteLine(dec);
            }
            //Utility.DeleteKeyFromContainerOfRSA(keyContainerName);
            Console.ReadKey();
        }
    }
}

コメントアウトしている

Utility.DeleteKeyFromContainerOfRSA(keyContainerName);

を有効にするとキーコンテナが削除されることとなり、
毎回新たな秘密キーがキーコンテナに格納されます。

*1:ブラックボックス化された何か