ではメールの設定を暗号化しました。パスワードが入力されたとき正しくないと例外が発生してしまいます。そこでパスワードが正しいかどうかチェックをしたいのですが、どうすればいいでしょうか?
EncodeクラスのGetKeyFromPasswordメソッドで生成されるデータをファイルに保存して、パスワードが入力されたときにこれと同じものが生成されるかどうかを調べるという方法がありますが、これによってパスワードが推測されないようにしないといけません。
ハッシュ化するのはどうでしょうか? ハッシュ関数には「MD5」、「SHA-1」、「SHA-256」などがありますが、MD5には特定のハッシュ値を持つ、元のデータを計算されてしまう脆弱性があります。そこでMD5とは別のもの、SHA-256をつかうことを考えます。
1 2 3 4 5 6 7 8 9 |
using System.Security.Cryptography; using(SHA256 mySHA256 = SHA256.Create()) { string password = "パスワード"; byte[] data = Encoding.UTF8.GetBytes(password); byte[] hashValue = mySHA256.ComputeHash(data); string ret = Convert.ToBase64String(hashValue); } |
にも設定を保存できるように改良を加えます。
Encodeクラス
FormLoadPassクラス
FormSavePassクラスは
と同じです。
Docクラスにフィールド変数 MasterPasswordHashを作成して、マスターパスワードがセットされたらそのハッシュを格納します。ファイルを読み込むときにマスターパスワードがセットされたときもハッシュを生成して、ファイルに保存されているものと比較すれば入力されたパスワードが正しいかどうかわかります。
そのあとコンストラクタ内で引数としてわたされた文字列とデータを暗号化してフィールド変数内に格納しています。ファイルを読み出すときはプロパティをつかって復号されたデータを取得しています。
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 |
public class Doc { public Doc() { } public Doc(string masterPassword, List<Data> datas, string mailSubject, string mailContent, string mailAdress, string mailServer, string port, string id, string password) { MasterPassword = masterPassword; MasterPasswordHash = GetPasswordHash(masterPassword); foreach(Data data in datas) { data.EncodeData(masterPassword); } EncodedDatas = datas; EncodedMailContent = Encode.EncodeString(mailContent, masterPassword); EncodedMailSubject = Encode.EncodeString(mailSubject, masterPassword); EncodedMailAdress = Encode.EncodeString(mailAdress, masterPassword); EncodedMailServer = Encode.EncodeString(mailServer, masterPassword); EncodedPort = Encode.EncodeString(port, masterPassword); EncodedId = Encode.EncodeString(id, masterPassword); EncodedPassword = Encode.EncodeString(password, masterPassword); } public List<Data> GetDecodedDatas(string masterPassword) { foreach(Data data in EncodedDatas) { data.DecodeData(masterPassword); } return EncodedDatas; } string MasterPassword = ""; public bool SetMasterPassword(string masterPassword) { MasterPassword = masterPassword; return MasterPasswordHash == GetPasswordHash(masterPassword); } string GetPasswordHash(string password) { using(SHA256 mySHA256 = SHA256.Create()) { byte[] data = Encoding.UTF8.GetBytes(password); byte[] hashValue = mySHA256.ComputeHash(data); return Convert.ToBase64String(hashValue); } } public List<Data> EncodedDatas = null; public string EncodedMailServer = ""; public string EncodedPort = ""; public string EncodedId = ""; public string EncodedPassword = ""; public string EncodedMailAdress = ""; public string EncodedMailContent = ""; public string EncodedMailSubject = ""; public string MasterPasswordHash = ""; public string MailServer { get { return Encode.DecodeString(EncodedMailServer, MasterPassword); } } public string Port { get { return Encode.DecodeString(EncodedPort, MasterPassword); } } public string Id { get { return Encode.DecodeString(EncodedId, MasterPassword); } } public string Password { get { return Encode.DecodeString(EncodedPassword, MasterPassword); } } public string MailAdress { get { return Encode.DecodeString(EncodedMailAdress, MasterPassword); } } public string MailContent { get { return Encode.DecodeString(EncodedMailContent, MasterPassword); } } public string MailSubject { get { return Encode.DecodeString(EncodedMailSubject, MasterPassword); } } } |
DataクラスではEncodeDataメソッドとDecodeDataメソッドをつかってフィールド変数の暗号化と復号をおこなっています。
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 |
public class Data { [System.Xml.Serialization.XmlIgnore] public string Address = ""; [System.Xml.Serialization.XmlIgnore] public string[] Texts = null; public string EncodedAddress = ""; public string[] EncodedTexts = null; public Data() { } public Data(string address, string text1, string text2, string text3, string text4, string text5, string text6, string text7, string text8) { Address = address; List<string> vs = new List<string>(); vs.Add(text1); vs.Add(text2); vs.Add(text3); vs.Add(text4); vs.Add(text5); vs.Add(text6); vs.Add(text7); vs.Add(text8); Texts = vs.ToArray(); } public void EncodeData(string password) { EncodedAddress = Encode.EncodeString(Address, password); List<string> vs = new List<string>(); foreach(string text in Texts) { vs.Add(Encode.EncodeString(text, password)); } EncodedTexts = vs.ToArray(); } public void DecodeData(string password) { Address = Encode.DecodeString(EncodedAddress, password); List<string> vs = new List<string>(); foreach(string text in EncodedTexts) { vs.Add(Encode.DecodeString(text, password)); } Texts = vs.ToArray(); } } |
最後に設定ファイルの保存と読み込み、メール送信の処理を示します。
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 130 131 |
public partial class Form1 : Form { private void MenuItemSaveFile_Click(object sender, EventArgs e) { SaveCurData(); XmlSerializer xml = new XmlSerializer(typeof(Doc)); SaveFileDialog save = new SaveFileDialog(); save.Filter = "メールデータ(*.xml)|*.xml"; save.DefaultExt = "xml"; if(save.ShowDialog() == DialogResult.OK) { FormSavePass f = new FormSavePass(); f.ShowDialog(); if(f.Password != "") { Doc doc = new Doc( f.Password, Datas, TextBoxSubject.Text, richTextBoxMailContent.Text, TextBoxFrom.Text, textBoxMailServer.Text, textBoxPort.Text, textBoxId.Text, textBoxPass.Text); StreamWriter sw = new StreamWriter(save.FileName); xml.Serialize(sw, doc); sw.Close(); } f.Dispose(); } save.Dispose(); } private void MenuItemOpenFile_Click(object sender, EventArgs e) { XmlSerializer xml = new XmlSerializer(typeof(Doc)); OpenFileDialog open = new OpenFileDialog(); open.Filter = "メールデータ(*.xml)|*.xml"; open.DefaultExt = "xml"; if(open.ShowDialog() == DialogResult.OK) { FormLoadPass f = new FormLoadPass(); if(f.ShowDialog() == DialogResult.OK) { StreamReader sr = new StreamReader(open.FileName); Doc doc = (Doc)xml.Deserialize(sr); sr.Close(); // ここでパスワードが正しいかチェックをする // falseが返ってきたときはパスワードが正しくない if(doc.SetMasterPassword(f.Password)) { Datas = doc.GetDecodedDatas(f.Password); listBox1.SelectedIndex = -1; listBox1.Items.Clear(); foreach(Data data in Datas) { listBox1.Items.Add(data.Address); } textBoxMailServer.Text = doc.MailServer; textBoxPort.Text = doc.Port; textBoxId.Text = doc.Id; textBoxPass.Text = doc.Password; TextBoxFrom.Text = doc.MailAdress; TextBoxFrom.Text = doc.MailAdress; TextBoxSubject.Text = doc.MailSubject; richTextBoxMailContent.Text = doc.MailContent; if(Datas.Count > 0) listBox1.SelectedIndex = 0; } else { MessageBox.Show("パスワードが正しくありません!", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); } } f.Dispose(); } open.Dispose(); } public void SendMails(List<MailInfo> mailInfos) { string smtpserver = textBoxMailServer.Text; int port = int.Parse(textBoxPort.Text); string userid = textBoxId.Text; string pass = textBoxPass.Text; string from =TextBoxFrom.Text; System.Net.IPAddress address = System.Net.Dns.GetHostEntry(smtpserver).AddressList[0]; TKMP.Net.ISmtpLogon logon; logon = new TKMP.Net.AuthLogin(userid, pass); TKMP.Net.SmtpClient smtp = new TKMP.Net.SmtpClient(address, port, logon); smtp.AuthenticationProtocol = TKMP.Net.AuthenticationProtocols.SSL; smtp.CertificateValidation += new TKMP.Net.CertificateValidationHandler(smtp_CertificateValidation); if(!smtp.Connect()) { MessageBox.Show("接続失敗", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } int successCount = 0; foreach(MailInfo mail in mailInfos) { string to = mail.Adress; try { TKMP.Writer.MailWriter writer = new TKMP.Writer.MailWriter(); writer.FromAddress = from; writer.Headers.Add("From", from + " <" + from + ">"); writer.ToAddressList.Add(to); writer.Headers.Add("To", to + " <" + to + ">"); writer.Headers.Add("Subject", mail.MailSubject); writer.MainPart = new TKMP.Writer.TextPart(mail.MailContent.Replace("\n", "\r\n")); smtp.SendMail(writer); successCount++; } catch { MessageBox.Show(to + "への送信に失敗しました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); } } smtp.Close(); MessageBox.Show(successCount + "件送信しました", "完了"); } private void smtp_CertificateValidation(object sender, TKMP.Net.CertificateValidationArgs e) { //全ての証明書を信用します e.Cancel = false; } } |