ではメールの設定をファイルに保存できるようにしました。しかしアカウントやパスワードをそのままファイルに保存するのは危険です。そこで暗号化した状態で保存することにします。
以下は文字列の暗号化と復号処理をするクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
using System.Security.Cryptography; public class Encode { private const int KEY_SIZE = 256; // 256Bit private const int BLOCK_SIZE = 128; // 128Bit private const int BUFFER_SIZE = BLOCK_SIZE * 32; // バッファーサイズはBlockSizeの倍数にする private const int SALT_SIZE = 8; // 8以上 static public string EncodeString(string srcString, string password) { // ソルトは SALT_SIZEバイト byte[] salt = CreateSalt("ダミー"); // IVは BLOCK_SIZE / 8バイト byte[] iv = CreateIV("ダミー"); // 暗号鍵は KEY_SIZE / 8バイト byte[] key = GetKeyFromPassword(password, salt); byte[] outBytes = null; using(AesManaged aes = new AesManaged()) { // AESインスタンスのパラメータ設定 aes.KeySize = KEY_SIZE; aes.BlockSize = BLOCK_SIZE; aes.Mode = CipherMode.CBC; aes.Key = key; aes.IV = iv; aes.Padding = PaddingMode.ISO10126; using(ICryptoTransform ct = aes.CreateEncryptor(aes.Key, aes.IV)) // 暗号化オブジェクト生成 using(var outStream = new System.IO.MemoryStream()) // 出力ストリーム { using(CryptoStream cryptoStream = new CryptoStream(outStream, ct, CryptoStreamMode.Write)) { byte[] buf = Encoding.Unicode.GetBytes(srcString); cryptoStream.Write(buf, 0, buf.Length); } outBytes = outStream.ToArray(); } } // ソルト、IV、暗号化されたデータをつなげてBase64に変換 List<byte> vs = new List<byte>(); vs.AddRange(salt); vs.AddRange(iv); vs.AddRange(outBytes); return Convert.ToBase64String(vs.ToArray()); } static public string DecodeString(string srcString, string password) { byte[] bytes = Convert.FromBase64String(srcString); // ソルトは SALT_SIZEバイト // IVは BLOCK_SIZE / 8バイト // 暗号鍵は KEY_SIZE / 8バイト byte[] salt = new byte[SALT_SIZE]; byte[] iv = new byte[BLOCK_SIZE / 8]; byte[] data = new byte[bytes.Length - SALT_SIZE - BLOCK_SIZE / 8]; using(System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes)) { ms.Read(salt, 0, SALT_SIZE); ms.Read(iv, 0, BLOCK_SIZE / 8); ms.Read(data, 0, bytes.Length - SALT_SIZE - BLOCK_SIZE / 8); } byte[] key = GetKeyFromPassword(password, salt); string decodeString = ""; using(AesManaged aes = new AesManaged()) { // AESインスタンスのパラメータ設定 aes.KeySize = KEY_SIZE; aes.BlockSize = BLOCK_SIZE; aes.Mode = CipherMode.CBC; aes.Key = key; aes.IV = iv; aes.Padding = PaddingMode.ISO10126; // 暗号化オブジェクト生成 ICryptoTransform ct = aes.CreateDecryptor(aes.Key, aes.IV); using(var inStream = new System.IO.MemoryStream(data, false)) // 入力ストリームを開く using(var outStream = new System.IO.MemoryStream()) // 出力ストリーム { using(CryptoStream cryptoStream = new CryptoStream(inStream, ct, CryptoStreamMode.Read)) { byte[] buffer = new byte[BUFFER_SIZE]; int len = 0; while((len = cryptoStream.Read(buffer, 0, BUFFER_SIZE)) > 0) outStream.Write(buffer, 0, len); } byte[] outBytes = outStream.ToArray(); decodeString = Encoding.Unicode.GetString(outBytes); } } return decodeString; } static private byte[] CreateSalt(string str) { Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes("8文字以上の文字列を指定すること", SALT_SIZE); return deriveBytes.Salt; } //初期化ベクトルを作成する static private byte[] CreateIV(string str) { Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes("8文字以上の文字列を指定すること", 100); return deriveBytes.GetBytes(BLOCK_SIZE / 8); } //パスワードとソルトから暗号鍵を求める static private byte[] GetKeyFromPassword(string password, byte[] salt) { //Rfc2898DeriveBytesオブジェクトを作成する Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(password, salt); //反復処理回数を指定する デフォルトで1000回 deriveBytes.IterationCount = 1000; //キーを生成する return deriveBytes.GetBytes(KEY_SIZE / 8); } } |
あとは前回のデータを暗号化してファイルに保存します。
Dataクラスのプロパティに格納されているデータの暗号化と復号処理をするメソッドと、それをシリアル化するときに必要になるフィールド変数を用意します。
Subjectプロパティ、DateTextプロパティ、MainTextプロパティはシリアル化の対象からはずし、これを暗号化したものをフィールド変数に格納して、これをXMLファイルとして保存します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
public class Data { public Data() { } public Data(string subject, string date, string mainText) { Subject = subject; MainText = mainText; Date = GetDateTime(date); DateText = Date.ToString("yyyy-MM-dd"); } [System.Xml.Serialization.XmlIgnore] public string Subject { get; private set; } [System.Xml.Serialization.XmlIgnore] public DateTime Date { get; private set; } [System.Xml.Serialization.XmlIgnore] public string MainText { get; private set; } [System.Xml.Serialization.XmlIgnore] public string DateText { get; private set; } DateTime GetDateTime(string str) { try { return System.DateTime.ParseExact(str, "ddd, d MMM yyyy HH':'mm':'ss zzz", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None); } catch { return DateTime.MinValue; } } public string EncodedSubject = ""; public string EncodedDateText = ""; public string EncodedMainText = ""; public void EncodeData(string password) { EncodedSubject = Encode.EncodeString(Subject, password); EncodedDateText = Encode.EncodeString(DateText, password); EncodedMainText = Encode.EncodeString(MainText, password); } public void DecodeData(string password) { Subject = Encode.DecodeString(EncodedSubject, password); DateText = Encode.DecodeString(EncodedDateText, password); MainText = Encode.DecodeString(EncodedMainText, password); } } |
Docクラスのコンストラクタでは、メールサーバーとアカウントID、パスワードを暗号化してフィールド変数に格納します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class Doc { public Doc() { } public Doc(string masterPassword, List<Data> datas, string mailServer, string port, string id, string password) { foreach(Data data in datas) { data.EncodeData(masterPassword); } EncodedDatas = datas; MailServer = Encode.EncodeString(mailServer, masterPassword); Port = Encode.EncodeString(port, masterPassword); Id = Encode.EncodeString(id, masterPassword); Password = Encode.EncodeString(password, masterPassword); } public List<Data> GetDecodedDatas(string masterPassword) { foreach(Data data in EncodedDatas) { data.DecodeData(masterPassword); } return EncodedDatas; } public List<Data> EncodedDatas = null; public string MailServer = ""; public string Port = ""; public string Id = ""; public string Password = ""; } |
ファイルを保存するときにマスターパスワードを設定するためのダイアログを表示します。両方のテキストボックスに同じパスワードが入力されていないとエラーを示すメッセージボックスを表示させます。
encode-mail-info1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public partial class FormSavePass : Form { public FormSavePass() { InitializeComponent(); textBox1.UseSystemPasswordChar = true; textBox2.UseSystemPasswordChar = true; buttonOK.Click += ButtonOK_Click; } public string Password = ""; private void ButtonOK_Click(object sender, EventArgs e) { if(textBox1.Text != "" && textBox1.Text == textBox2.Text) { Password = textBox1.Text; Close(); } else { MessageBox.Show("パスワードが正しく設定できていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public partial class Form1 : Form { private void buttonSaveXml_Click(object sender, EventArgs e) { SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "メールデータ(*.xml)|*.xml"; dlg.DefaultExt = "xml"; if(dlg.ShowDialog() == DialogResult.OK) { FormSavePass f = new FormSavePass(); f.ShowDialog(); if(f.Password != "") { Doc doc = new Doc(f.Password, Datas, textBoxMailServer.Text, textBoxPort.Text, textBoxId.Text, textBoxPass.Text); System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(Doc)); System.IO.StreamWriter sw = new System.IO.StreamWriter(dlg.FileName); xml.Serialize(sw, doc); sw.Close(); MessageBox.Show("保存完了"); } f.Dispose(); } dlg.Dispose(); } } |
ファイルを読み込むときもマスターパスワードを入力させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class FormLoadPass : Form { public FormLoadPass() { InitializeComponent(); textBox1.UseSystemPasswordChar = true; buttonOK.Click += ButtonOK_Click; buttonOK.DialogResult = DialogResult.OK; } public string Password = ""; private void ButtonOK_Click(object sender, EventArgs e) { Password = textBox1.Text; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public partial class Form1 : Form { private void buttonLoadXml_Click(object sender, EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); if(dlg.ShowDialog() == DialogResult.OK) { FormSavePass f = new FormSavePass(); if(f.ShowDialog() == DialogResult.OK) { System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(Doc)); System.IO.StreamReader sr = new System.IO.StreamReader(dlg.FileName); Doc doc = (Doc)xml.Deserialize(sr); sr.Close(); string masterPassword = f.Password; Datas = doc.GetDecodedDatas(masterPassword); textBoxMailServer.Text = Encode.DecodeString(doc.MailServer, f.Text); textBoxPort.Text = Encode.DecodeString(doc.Port, masterPassword); textBoxId.Text = Encode.DecodeString(doc.Id, masterPassword); textBoxPass.Text = Encode.DecodeString(doc.Password, masterPassword); // 新しいメールを上側に表示させる Datas = Datas.OrderByDescending(x => x.DateText).ToList(); listBox1.Items.Clear(); foreach(Data data in Datas) { listBox1.Items.Add(data.Subject); } } f.Dispose(); } dlg.Dispose(); } } |