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

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

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

音樂は SoundCloud 等に公開中です。

考察は現在は主に Scrapbox で公表中です。

Programming は GitHub で開発中です。

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

パスワードの保存には[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

|xml| <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>

||<

Window1.cs

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

namespace PasswordTest { ///

/// Window1.xaml の相互作用ロジック /// 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 秒程度掛かる。