C# 如何在salt&;中使用SHA-512和Rfc2898DeriveBytes;散列码?

C# 如何在salt&;中使用SHA-512和Rfc2898DeriveBytes;散列码?,c#,hash,cryptography,salt,sha,C#,Hash,Cryptography,Salt,Sha,我对密码学是个新手,但是学习。我从网上的研究中收集了许多不同的建议,并制作了自己的类,用于处理哈希、salt、键拉伸以及关联数据的比较/转换 在研究了内置的.NET密码学库之后,我发现我拥有的仍然只有SHA-1。但我得出的结论是,这并不坏,因为我使用了哈希过程的多次迭代。对吗 但如果我想从更健壮的SHA-512开始,我如何在下面的代码中实现它呢?提前谢谢 using System; using System.Runtime.InteropServices; using System.Securi

我对密码学是个新手,但是学习。我从网上的研究中收集了许多不同的建议,并制作了自己的类,用于处理哈希、salt、键拉伸以及关联数据的比较/转换

在研究了内置的.NET密码学库之后,我发现我拥有的仍然只有SHA-1。但我得出的结论是,这并不坏,因为我使用了哈希过程的多次迭代。对吗

但如果我想从更健壮的SHA-512开始,我如何在下面的代码中实现它呢?提前谢谢

using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;

public class CryptoSaltAndHash
{
    private string strHash;
    private string strSalt;
    public const int SaltSizeInBytes = 128;
    public const int HashSizeInBytes = 1024;
    public const int Iterations = 3000;

    public string Hash { get { return strHash; } }
    public string Salt { get { return strSalt; } }

    public CryptoSaltAndHash(SecureString ThisPassword)
    {
        byte[] bytesSalt = new byte[SaltSizeInBytes];
        using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
        {
            crypto.GetBytes(bytesSalt);
        }
        strSalt = Convert.ToBase64String(bytesSalt);
        strHash = ComputeHash(strSalt, ThisPassword);
    }

    public static string ComputeHash(string ThisSalt, SecureString ThisPassword)
    {
        byte[] bytesSalt = Convert.FromBase64String(ThisSalt);
        Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(
            convertSecureStringToString(ThisPassword), bytesSalt, Iterations);
        using (pbkdf2)
        {
            return Convert.ToBase64String(pbkdf2.GetBytes(HashSizeInBytes));
        }
    }

    public static bool Verify(string ThisSalt, string ThisHash, SecureString ThisPassword)
    {
        if (slowEquals(getBytes(ThisHash), getBytes(ComputeHash(ThisSalt, ThisPassword))))
        {
            return true;
        }
        return false;
    }

    private static string convertSecureStringToString(SecureString MySecureString)
    {
        IntPtr ptr = IntPtr.Zero;
        try
        {
            ptr = Marshal.SecureStringToGlobalAllocUnicode(MySecureString);
            return Marshal.PtrToStringUni(ptr);
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(ptr);
        }
    }

    private static bool slowEquals(byte[] A, byte[] B)
    {
        int intDiff = A.Length ^ B.Length;
        for (int i = 0; i < A.Length && i < B.Length; i++)
        {
            intDiff |= A[i] ^ B[i];
        }
        return intDiff == 0;
    }

    private static byte[] getBytes(string MyString)
    {
        byte[] b = new byte[MyString.Length * sizeof(char)];
        System.Buffer.BlockCopy(MyString.ToCharArray(), 0, b, 0, b.Length);
        return b;
    }
}
使用系统;
使用System.Runtime.InteropServices;
使用系统安全;
使用System.Security.Cryptography;
公共类CryptoSaltAndHash
{
私有字符串strHash;
私有字符串strSalt;
公共常数int SaltSizeInBytes=128;
公共常量int HashSizeInBytes=1024;
公共常数int迭代次数=3000;
公共字符串哈希{get{return strHash;}}
公共字符串Salt{get{return strSalt;}}
公共CryptoSaltAndHash(SecureString ThisPassword)
{
byte[]bytesSalt=新字节[SaltSizeInBytes];
使用(RNGCryptoServiceProvider crypto=new RNGCryptoServiceProvider())
{
加密.GetBytes(bytesSalt);
}
strSalt=Convert.ToBase64String(bytesSalt);
strHash=ComputeHash(strSalt,ThisPassword);
}
公共静态字符串ComputeHash(字符串ThisSalt、SecureString ThisPassword)
{
字节[]bytesSalt=Convert.FromBase64String(ThisSalt);
Rfc2898DeriveBytes pbkdf2=新的Rfc2898DeriveBytes(
convertSecureStringToString(此密码)、bytesSalt、迭代);
使用(pbkdf2)
{
返回Convert.ToBase64String(pbkdf2.GetBytes(HashSizeInBytes));
}
}
公共静态bool验证(字符串ThisSalt、字符串ThisHash、安全字符串ThisPassword)
{
if(slowEquals(getBytes(ThisHash)、getBytes(ComputeHash(ThisSalt、ThisPassword)))
{
返回true;
}
返回false;
}
私有静态字符串转换器SecureStringToString(SecureString MySecureString)
{
IntPtr ptr=IntPtr.0;
尝试
{
ptr=封送。SecureStringToGlobalAllocUnicode(MySecureString);
返回Marshal.PtrToStringUni(ptr);
}
最后
{
ZeroFreeGlobalAllocUnicode元帅(ptr);
}
}
专用静态bool slowEquals(字节[]A,字节[]B)
{
int intDiff=A.长度^B.长度;
对于(int i=0;i
注意:我已经参考了许多来自的实践。slowEquals比较方法是通过防止分支来规范执行时间。SecureString的用途是在这个类和我的web应用程序中的其他类和页面之间有一个加密形式的密码传递。虽然这个网站将通过HTTPS,但在合理的范围内,尽可能多地确保事情尽可能安全总是很好的

在我的代码中,我将密钥字符串设置为128字节(尽管有时会变大,这很好),哈希大小设置为1KB,迭代次数设置为3000。它比典型的64字节salt、512字节hash和1000或2000次迭代稍微大一点,但登录速度和应用程序性能的优先级非常低

想法

  • 3000次迭代相当少。甚至10000美元也很低。但是,您需要权衡额外迭代的安全性增益与攻击者通过尝试经常登录而使您的服务器受到攻击的风险,这会为每次尝试触发昂贵的哈希
  • 大于128位/16字节的salt没有意义。盐应该是独一无二的,仅此而已
  • 哈希大小大于本机大小(SHA-1为20字节)会降低防御者的性能,但不会降低攻击者的性能。因为这意味着您可以承受更少的迭代,所以它实际上削弱了安全性

    例如,以与3000次迭代的1024字节哈希相同的成本,您可以负担156000次迭代的20字节哈希,这是破解成本的52倍

  • 要使用SHA-2,您需要一个完全不同的PBKDF2实现,.net中包含的实现是硬编码的,以使用SHA-1

    如果您费心使用第三方库,我宁愿使用bcrypt库,因为它对基于GPU的攻击者具有更强的抵御能力

  • 您的API很难使用,因为您将salt管理推送到调用方,而不是在
    Create
    /
    Verify
    函数中处理它

  • 使用
    SecureString
    然后将其转换为
    String
    是愚蠢的。这抵消了首先使用
    SecureString
    的全部意义

    就我个人而言,我不会在典型应用程序中使用
    SecureString
    。只有将它与广泛的全堆栈安全性检查结合起来,检查密码是否从未存储在
    字符串中,并且在不再需要密码时总是从可变存储中删除密码,这才是值得的

  • 我不会在实例变量中存储密码/密码。只需将它们保留在相关功能的本地。我只将配置存储在实例变量中(例如迭代计数)

  • 虽然SHA-1在加密方面被削弱,但攻击会产生冲突。因为密码散列冲突是不相关的,所以您关心的是第一个图像前攻击。SHA-1在这方面仍然相当强大

    T