C# Programming

パスワードを暗号化してレジストリに保存する

開発環境: Visual Studio 2003 

1.目次

1.目次
2.目的
3.参考書
4.パスワードを暗号化、復号化するクラス
5.レジストリにデータをセット、ゲットする。
6.テスト用ソースコード

2.目的

SMTPサーバーにメールを送信する場合に、POP Before SMTPだとPOPのパスワードを覚えておく必要がありますよね。単純に暗号化していないパスワードをレジストリにそのまま書くのは危ないので、暗号化してしまっておく必要がありますよね。

テスト用に作ったアプリケーションはこんな感じです。

Encrypt ボタンを押すと、『今日は天気がいいですね』という入力文字列をパスワード『パスワードを入れる』で暗号化し、 暗号化されたEncrypted データをレジストリに書き込みます。

Decrypt ボタンを押すと、レジストリから、Encrypted データを読み出して、それを再度同じパスワードで復号化 するだけの簡単な動作です。

Image

暗号化方法に関しては、参考文献(1)に書いてあるので、暗号化の中身はそちらを見てね。

System.Security.Cryptography.SymmetricAlgorithm
では、代表的な対称アルゴリズムのDES, RC2, Rijndael, TripleDESがサポートされています。 対称暗号アルゴリズムには、暗号化と復号化の両方に使用される単一の共有キーがあり、これがばれてしまうとセキュリティは確保できません。詳細は、参考文献(2)を見てください。

System.Security.Cryptography.AsymmetricAlgorithm
では、代表的な非対称アルゴリズム (公開キー アルゴリズム) のDSA, RSAがサポートされています。非対称暗号アルゴリズムは、秘密キーと公開キー があります。初めての人は参考文献(3)がわかりやすいです。

System.Security.Cryptography.HashAlgorithmでは、代表的なMD5, SHA1, SHA256, SHA384, SHA512がサポートされています。

くらくらきますね。こんな方法でいいのだろうかと疑問になってしまう。。。orz

3.参考書

  1. MSDN Online = 10 行シリーズ 〜 10 行でズバリ !! 暗号化 (C#) 〜
  2. MSDN SymmetricAlgorithm クラス (対称アルゴリズム)
  3. サルにもわかるRSA - まいとう情報通信研究会 (すごくわかりやすい)

4.パスワードを暗号化、復号化するクラス


まず、参考書(1)の10行シリーズをもとに、簡単なパスワードを暗号化、復号化するクラスを作成します。

このクラスには、次の2つのパブリックスタティックメソッドを用意します。

// パスワード文字列 password をキー key でTriple DES暗号化を行います。
public static byte[] Encrypt(string password, string key);

// パスワード文字列 password をキー key でTriple DES復号化を行います。
public static string Decrypt(byte [] source, string key)

参考書のコードでは、暗号化、復号化を行う TripleDESCryptoServiceProvider のKeyとIV をローカル変数にして共通化していますので、常に復号化が可能です。しかし、普通は暗号化と復号化は別々のアプリになる可能性が高いので、 そのような場合でもKeyとIVを一致させてあげないと、復号化出来ません。

そこで、keyString からKeyとIV を生成するプライベートメソッド(GenerateKey, GenerateIV)を用意しました。 これによりkeyStringが一致していれば、同じKeyとIVを生成するようにします。これにより、keyStringさえ忘れなければ復号化できるます。

このクラスを使って暗号化するには、エンコードする文字列と、エンコードする際のキーをEncryptメソッドに渡してあげればOKです。また、復号化する場合には、同様にDecryptに復号化するデータとエンコードしたときと同じキーを渡してあげればOKです。

クラスの呼び出し方
...
using System.Text;
using Microsoft.Win32;
using Uchukamen.Security;
...
private void 暗号化処理()
{	// パスワードをTriple DES 暗号化する。
	byte[] encrypted = PasswordEncoder.Encrypt(エンコードする文字列, キー文字列);

	レジストリに書き込む処理。
}
private void 復号化処理
{	レジストリから、Triple DESエンコードされたバイト配列を取得する処理。

	// 暗号化されたパスワードを Triple DES 復号化する。
	string result = PasswordEncoder.Decrypt(複合化するバイト配列, キー文字列);
}		

5.レジストリにデータをセット、ゲットする。

レジストリにデータをセット、ゲットする簡単な方法は次のとおりです。

Applicaton.UserAppDataRegistry で、

CurrentUser\Software\CompanyName\ProductName\ProductVersion

のレジストリキーを生成してくれます。

そのために、つぎのようなメソッドを用意しておきます。

レジストリにデータをセット、ゲットする。
/// /// レジストリから name キーで、codeをセットする。
/// private void SetValue(string name, byte [] code)
{	RegistryKey key = Application.UserAppDataRegistry;
	key.SetValue(name, code);
	key.Close();
}
/// /// レジストリから name キーの値を取得する。
/// private byte[] GetValue(string name)
{	RegistryKey key = Application.UserAppDataRegistry;
	byte[] result  = (byte[]) key.GetValue(name);
	key.Close();
	return result;
}

レジストリには、たとえばこのように格納されます。
レジストリはそのときの環境によって違うので注意してください。

Image

 

注意
コンパイルしてバージョンが新しくなるたびに、新しいレジストリを追加してしまいます。
これを避けるためには、インストーラによるインストールを行い、アンインストールした場合に、古いレジストリを削除するようにする必要があります。

このテストをする場合もどんどん生成されているので、注意してね。

6.テスト用ソースコード(EncryptPassword.cs, Form1.cs)

変更履歴
2004/10/24 初版作成 V1.0
EncryptPassword.cs
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;

namespace Uchukamen.Security
{	/// 	/// PasswordEncoder はパスワードをTriple DESでエンコード、デコードします。
	/// 	public class PasswordEncoder
	{
		private PasswordEncoder()
		{
		}

		/// 		/// keyStringから TripleDESCryptoServiceProvider のKey を生成します。
		/// 注意: この生成方法を変更するとデコードできなくなります。
		/// 		/// 
		/// 
		/// 		private static byte[] GenerateKey(string keyString, int len)
		{
			byte [] key = new byte[len];
			for (int i = 0; i < len; i++)
				key[i] = (byte)(keyString[i % keyString.Length] + i);
			return key;
		}

		/// 		/// keyStringから TripleDESCryptoServiceProvider のIV を生成します。
		/// 注意: この生成方法を変更するとデコードできなくなります。
		/// 		/// 
		/// 
		/// 		private static byte[] GenerateIV(string keyString, int len)
		{
			byte [] iv = new byte[len];
				for (int i = 0; i < len; i++)
					iv[i] = (byte)(keyString[i % keyString.Length] - i);
			
			return iv;
		}

		/// 		/// パスワード文字列 password をキー key でTriple DES暗号化を行います。
		/// 		/// 
		/// 
		/// 		public static byte[] Encrypt(string password, string key)
		{
			// Tripe DES のサービス プロバイダを生成します
			TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider();

			tDes.Key = GenerateKey(key, tDes.Key.Length);
			tDes.IV = GenerateKey(key, tDes.IV.Length);

			// 文字列を byte 配列に変換します
			byte[] source = Encoding.Unicode.GetBytes(password);

			// 入出力用のストリームを生成します
			MemoryStream ms = new MemoryStream();
			CryptoStream cs = new CryptoStream(ms, 
				tDes.CreateEncryptor( tDes.Key, tDes.IV ), CryptoStreamMode.Write);

			// ストリームに暗号化するデータを書き込みます
			cs.Write(source, 0, source.Length);
			cs.Close();

			// 暗号化されたデータを byte 配列で取得します
			byte[] destination = ms.ToArray();
			ms.Close();

			return destination;
		}

		/// 		/// パスワード文字列 password をキー key でTriple DES復号化を行います。
		/// 		/// 
		/// 
		/// 		public static string Decrypt(byte [] source, string key)
		{
			// Tripe DES のサービス プロバイダを生成します
			TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider();
			tDes.Key = GenerateKey(key, tDes.Key.Length);
			tDes.IV = GenerateKey(key, tDes.IV.Length);

			// 入出力用のストリームを生成します
			MemoryStream ms = new MemoryStream();
			CryptoStream cs = new CryptoStream(ms, 
				tDes.CreateDecryptor( tDes.Key, tDes.IV ),CryptoStreamMode.Write);

			// ストリームに暗号化されたデータを書き込みます
			cs.Write(source, 0, source.Length);
			cs.Close();

			// 復号化されたデータを byte 配列で取得します
			byte[] destination = ms.ToArray();
			ms.Close();

			// byte 配列を文字列に変換して表示します
			return Encoding.Unicode.GetString(destination);		
		}
	}
} 
Form1.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using Microsoft.Win32;
using Uchukamen.Security;

namespace PasswordEncryption
{	/// 	/// Form1 の概要の説明です。
	/// 	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.Button button1;
		private System.Windows.Forms.TextBox textBoxInput;
		private System.Windows.Forms.TextBox textBoxEncrypted;
		private System.Windows.Forms.TextBox textBoxDecrypted;
		private System.Windows.Forms.Label label1;
		private System.Windows.Forms.Label label2;
		private System.Windows.Forms.Label label3;
		private System.Windows.Forms.Button button2;
		private System.Windows.Forms.Label label4;
		private System.Windows.Forms.TextBox textBoxPassword;
		/// 		/// 必要なデザイナ変数です。
		/// 		private System.ComponentModel.Container components = null;

		public Form1()
		{
			//
			// Windows フォーム デザイナ サポートに必要です。
			//
			InitializeComponent();

			//
			// TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。
			//
		}

		/// 		/// 使用されているリソースに後処理を実行します。
		/// 		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows フォーム デザイナで生成されたコード 
		/// 		/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
		/// コード エディタで変更しないでください。
		/// 		private void InitializeComponent()
		{
			this.button1 = new System.Windows.Forms.Button();
			this.textBoxInput = new System.Windows.Forms.TextBox();
			this.textBoxEncrypted = new System.Windows.Forms.TextBox();
			this.textBoxDecrypted = new System.Windows.Forms.TextBox();
			this.label1 = new System.Windows.Forms.Label();
			this.label2 = new System.Windows.Forms.Label();
			this.label3 = new System.Windows.Forms.Label();
			this.button2 = new System.Windows.Forms.Button();
			this.textBoxPassword = new System.Windows.Forms.TextBox();
			this.label4 = new System.Windows.Forms.Label();
			this.SuspendLayout();
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(96, 144);
			this.button1.Name = "button1";
			this.button1.Size = new System.Drawing.Size(64, 24);
			this.button1.TabIndex = 8;
			this.button1.Text = "Encrypt";
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// textBoxInput
			// 
			this.textBoxInput.Location = new System.Drawing.Point(80, 16);
			this.textBoxInput.Name = "textBoxInput";
			this.textBoxInput.Size = new System.Drawing.Size(160, 19);
			this.textBoxInput.TabIndex = 1;
			this.textBoxInput.Text = "";
			// 
			// textBoxEncrypted
			// 
			this.textBoxEncrypted.Location = new System.Drawing.Point(80, 80);
			this.textBoxEncrypted.Name = "textBoxEncrypted";
			this.textBoxEncrypted.ReadOnly = true;
			this.textBoxEncrypted.Size = new System.Drawing.Size(160, 19);
			this.textBoxEncrypted.TabIndex = 5;
			this.textBoxEncrypted.Text = "";
			// 
			// textBoxDecrypted
			// 
			this.textBoxDecrypted.Location = new System.Drawing.Point(80, 112);
			this.textBoxDecrypted.Name = "textBoxDecrypted";
			this.textBoxDecrypted.ReadOnly = true;
			this.textBoxDecrypted.Size = new System.Drawing.Size(160, 19);
			this.textBoxDecrypted.TabIndex = 7;
			this.textBoxDecrypted.Text = "";
			// 
			// label1
			// 
			this.label1.Location = new System.Drawing.Point(24, 24);
			this.label1.Name = "label1";
			this.label1.Size = new System.Drawing.Size(40, 16);
			this.label1.TabIndex = 0;
			this.label1.Text = "Input";
			// 
			// label2
			// 
			this.label2.Location = new System.Drawing.Point(16, 88);
			this.label2.Name = "label2";
			this.label2.Size = new System.Drawing.Size(64, 24);
			this.label2.TabIndex = 4;
			this.label2.Text = "Encrypted";
			// 
			// label3
			// 
			this.label3.Location = new System.Drawing.Point(16, 120);
			this.label3.Name = "label3";
			this.label3.Size = new System.Drawing.Size(64, 24);
			this.label3.TabIndex = 6;
			this.label3.Text = "Decrypted";
			// 
			// button2
			// 
			this.button2.Location = new System.Drawing.Point(176, 144);
			this.button2.Name = "button2";
			this.button2.Size = new System.Drawing.Size(64, 24);
			this.button2.TabIndex = 9;
			this.button2.Text = "Decrypt";
			this.button2.Click += new System.EventHandler(this.button2_Click);
			// 
			// textBoxPassword
			// 
			this.textBoxPassword.Location = new System.Drawing.Point(80, 48);
			this.textBoxPassword.Name = "textBoxPassword";
			this.textBoxPassword.Size = new System.Drawing.Size(160, 19);
			this.textBoxPassword.TabIndex = 3;
			this.textBoxPassword.Text = "";
			// 
			// label4
			// 
			this.label4.Location = new System.Drawing.Point(16, 56);
			this.label4.Name = "label4";
			this.label4.Size = new System.Drawing.Size(56, 24);
			this.label4.TabIndex = 2;
			this.label4.Text = "Password";
			// 
			// Form1
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
			this.ClientSize = new System.Drawing.Size(264, 182);
			this.Controls.Add(this.label4);
			this.Controls.Add(this.textBoxPassword);
			this.Controls.Add(this.button2);
			this.Controls.Add(this.label3);
			this.Controls.Add(this.label2);
			this.Controls.Add(this.label1);
			this.Controls.Add(this.textBoxDecrypted);
			this.Controls.Add(this.textBoxEncrypted);
			this.Controls.Add(this.textBoxInput);
			this.Controls.Add(this.button1);
			this.Name = "Form1";
			this.Text = "Triple DES";
			this.ResumeLayout(false);

		}
		#endregion

		/// 		/// アプリケーションのメイン エントリ ポイントです。
		/// 		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		private void button1_Click(object sender, System.EventArgs e)
		{
			try
			{
				this.textBoxEncrypted.Text = this.textBoxDecrypted.Text = "";

				// パスワードをTriple DES 暗号化する。
				byte[] encrypted = PasswordEncoder.Encrypt(
				this.textBoxInput.Text, 
				this.textBoxPassword.Text);

				// レジストリに書き込む。
				SetValue("DES", encrypted);

				// byte 配列を文字列に変換して表示します
				textBoxEncrypted.Text = Encoding.Unicode.GetString(encrypted);		
			}
			catch (Exception exc)
			{
				MessageBox.Show(exc.Message, Application.ProductName, 
				MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void button2_Click(object sender, System.EventArgs e)
		{
			try
			{

				// レジストリから、Triple DESエンコードされた文字列を取得する。
				byte [] source = GetValue("DES");

				// 暗号化されたパスワードを Triple DES 復号化する。
				string result = PasswordEncoder.Decrypt(
					source, this.textBoxPassword.Text);

				// byte 配列を文字列に変換して表示します
				this.textBoxDecrypted.Text = result;	
			}
			catch (Exception exc)
			{
				MessageBox.Show(exc.Message, Application.ProductName, 
					MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		/// 		/// レジストリから name キーで、codeをセットする。
		/// 		/// 
		/// 
		private void SetValue(string name, byte [] code)
		{
			RegistryKey key = Application.UserAppDataRegistry;
			key.SetValue(name, code);
			key.Close();
		}

		/// 		/// レジストリから name キーの値を取得する。
		/// 		/// 
		/// 		private byte[] GetValue(string name)
		{
			RegistryKey key = Application.UserAppDataRegistry;
			byte[] result  = (byte[]) key.GetValue(name);
			key.Close();
			return result;
		}
	}
}