読者です 読者をやめる 読者になる 読者になる

c4se記:さっちゃんですよ☆

.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)

.。oO(此のblogは、主に音樂考察Programmingに分類されますよ。ヾ(〃l _ l)ノ゙♬♪♡

音樂はSoundCloud等バラバラの場所に公開中です。申し訳ないがlinkをたどるなどして探してください。

考察は現在は主に此のblogで公表中です。

programmingは、ひろくみせるものはGitHubで、個人的なものはBitBucketで開発中です。

c4se

パスワードの保存にはPBKDF2が好いと聞いた

Programming C#

パスワードの保存には[PBKDF2 http://en.wikipedia.org/wiki/PBKDF2 ]が好いと聞いた。
cf. [エフセキュアブログ : 「SHA-1+salt」はパスワードに十分だと思いますか? http://blog.f-secure.jp/archives/50564743.html ]

パスワード+saltのハッシュ化 (SHA-1) に依ってレインボーテーブル攻撃に強くする事が出来る。HMACを使うのが簡便だ。総当り (ブルートフォースアタック) にも強くするには、streachingを行うのが好い。此れにはPBKDF2を使うのが簡便である。PBKDF2は内部でHMACを使用する。
此う云う事は数学的に追ってみないと理解出来ない。私は理解出来ていない。
実装してみる。

Window1.xaml

<Window x:Class="PasswordTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="PBKDF2 (Rfc2898DeriveBytes)" Height="338" Width="664">
    <Grid>
        <Label Height="28" HorizontalAlignment="Left" Margin="36,18,0,0" Name="labelUserID" VerticalAlignment="Top" Width="56">User</Label>
        <TextBox Height="24" Margin="97,22,261,0" Name="textBoxUserID" VerticalAlignment="Top" TextChanged="textBoxUserID_TextChanged" MaxLength="32" />
        <Label Height="28" HorizontalAlignment="Left" Margin="36,51,0,0" Name="labelPass" VerticalAlignment="Top" Width="56">Pass</Label>
        <TextBox Height="24" Margin="97,51,261,0" Name="textBoxPass" VerticalAlignment="Top" TextChanged="textBoxPass_TextChanged" MaxLength="256" />
        <Button Height="23" Margin="306,80,261,0" Name="buttonResist" VerticalAlignment="Top" Click="buttonResist_Click" IsEnabled="False">Resist</Button>
        <Label Height="28" HorizontalAlignment="Left" Margin="36,0,0,116" Name="labelSalt" VerticalAlignment="Bottom" Width="56">Salt</Label>
        <TextBox Height="24" Margin="97,0,261,120" Name="textBoxSalt" VerticalAlignment="Bottom" />
        <Label Height="28" Margin="36,0,550,83" Name="labelPbkdf2" VerticalAlignment="Bottom">PBKDF2</Label>
        <TextBox Height="24" Margin="0,0,261,83" Name="textBoxPbkdf2" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="284" />
        <Label Height="28" Margin="0,22,37,0" Name="labelValidateUserID" VerticalAlignment="Top" Visibility="Hidden" HorizontalAlignment="Right" Width="219">^[A-Za-z](?:[A-Za-z0-9]+[-_.]?)+$</Label>
        <Label Height="28" Margin="0,51,147,0" Name="labelValidatePass" VerticalAlignment="Top" Visibility="Hidden" HorizontalAlignment="Right" Width="109">^[ -~]{8,256}$</Label>
        <Label Height="28" HorizontalAlignment="Left" Margin="97,0,0,50" Name="labelTime" VerticalAlignment="Bottom" Width="185"></Label>
    </Grid>
</Window>

Window1.cs

using System;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;

namespace PasswordTest
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        private string UserID;
        private string Pass;

        private bool ValidateUserID(string userID)
        {
            if (8 <= userID.Length && userID.Length <= 32 && Regex.IsMatch(userID, "^[A-Za-z](?:[A-Za-z0-9]+[-_.]?)+$"))
                return true;
            return false;
        }

        private bool ValidatePass(string pass)
        {
            if (Regex.IsMatch(pass, "^[ -~]{8,256}$")) return true;
            return false;
        }

        private bool ShowInputValidation()
        {
            bool isValid = true;
            if (ValidateUserID(textBoxUserID.Text)) labelValidateUserID.Visibility = Visibility.Hidden;
            else
            {
                labelValidateUserID.Visibility = Visibility.Visible;
                isValid = false;
            }
            if (ValidatePass(textBoxPass.Text)) labelValidatePass.Visibility = Visibility.Hidden;
            else
            {
                labelValidatePass.Visibility = Visibility.Visible;
                isValid = false;
            }
            if (isValid) buttonResist.IsEnabled = true;
            else buttonResist.IsEnabled = false;
            return isValid;
        }

        private byte[] GenerateSalt()
        {
            Guid guid = Guid.NewGuid();
            return guid.ToByteArray();
        }

        private byte[] GenerateHash(string pass, byte[] salt)
        {
            var key = new Rfc2898DeriveBytes(pass, salt, 100000);
            return key.GetBytes(16);
        }

        private string ConvertByteArrayToString(byte[] sequence)
        {
            return Convert.ToBase64String(sequence);
        }

        private void ResistThreadStart()
        {
            var startTime = DateTime.Now;
            byte[] salt = GenerateSalt();
            byte[] hash = GenerateHash(Pass, salt);
            var endTime = DateTime.Now;
            Dispatcher.BeginInvoke((Action)delegate()
            {
                textBoxSalt.Text = ConvertByteArrayToString(salt);
                textBoxPbkdf2.Text = ConvertByteArrayToString(hash);
                labelTime.Content = endTime - startTime;
            });
        }

        public Window1()
        {
            InitializeComponent();
        }

        private void buttonResist_Click(object sender, RoutedEventArgs e)
        {
            bool isValid = ShowInputValidation();
            if (!isValid) return;
            UserID = textBoxUserID.Text;
            Pass = textBoxPass.Text;
            var thread = new Thread(new ThreadStart(ResistThreadStart));
            thread.IsBackground = true;
            thread.Start();
        }

        private void textBoxUserID_TextChanged(object sender, TextChangedEventArgs e)
        {
            ShowInputValidation();
        }

        private void textBoxPass_TextChanged(object sender, TextChangedEventArgs e)
        {
            ShowInputValidation();
        }
    }
}

System.Security.Cryptography.Rfc2898DeriveBytesを使う。ハッシュ関数は十万回繰り返している。私のCPU (Corei5 2.67GHz) では0.7秒程度掛かる。