编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

惊呆了!原来密钥生成可以这么简单又安全!快来看看怎么做的!

wxchong 2024-07-29 08:03:03 开源技术 10 ℃ 0 评论

上次咱们聊到了密钥的生成,还扒了扒System.Random的小底裤,哈哈,你居然发现它不是安全的!是不是很意外?

这次,我给朋友们秀一个更简洁、更安全的写法!让我们一起见证奇迹吧!

首先我来理下思路:他(勒索病毒Z作者lucky)的本意是要生成一个32字节的密钥。采用的方式是生成长度为 60 字节的密钥,然后取32个字节。这就像我们常说的那个什么什么放P,多此一举。何不直接生成一个32字节的密钥呢?![捂脸]

这里,我为了还原当时场景,还是模拟他的写法阿,看我如何改为更安全的随机密钥!

第一种写法:利用RNGCryptoServiceProvider生成

        private const string AllowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        private static readonly RNGCryptoServiceProvider CryptoRandom = new RNGCryptoServiceProvider();
        public static string GenerateKey()
        {
            StringBuilder sb = new StringBuilder(60); // 设置初始容量为60
            byte[] randomBytes = new byte[1];
            for (int i = 0; i < 60; i++)
            {
                CryptoRandom.GetBytes(randomBytes);
                int index = randomBytes[0] % AllowedChars.Length;
                sb.Append(AllowedChars[index]);
            }
            return sb.ToString().Substring(0, 32); // 截取前32个字符
        }

使用RNGCryptoServiceProvider 是因为它是一个提供加密强随机数生成服务的类,可以确保生成的随机性更高,也更安全。原理呢,还是创建一个容量为60的 StringBuilder 对象,然后在循环中进行60次迭代,每次迭代时生成一个字节的随机数,最后调用 sb.ToString().Substring(0, 32) 来获取前32个字符作为最终生成的密钥。这个解释够清楚了吧,后面的我就不细说了,能看这个的应该都是高水平的人了[微笑]。

第二种写法:利用RandomNumberGenerator生成

    public class KeyGenerator  
    {  
        private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";  
          // 用于生成随机数的加密服务提供器  
        private readonly RandomNumberGenerator rng;  
          // 构造函数,初始化随机数生成器  
        public KeyGenerator()  
        {  
            rng = RandomNumberGenerator.Create();  
        }  
          // 生成指定长度的随机密钥  
        public string GenerateKey(int length)  
        {  
            // 检查长度参数是否有效  
            if (length <= 0)  
                throw new ArgumentOutOfRangeException(nameof(length), "密钥长度必须大于零。");  
              // 创建一个字节数组来存储随机生成的字节  
            byte[] keyBytes = new byte[length];  
            // 使用随机数生成器填充字节数组  
            rng.GetBytes(keyBytes);  
              // 将随机字节转换为指定的字符集,并构建密钥字符串  
            StringBuilder sb = new StringBuilder();  
            foreach (byte b in keyBytes)  
            {  
                // 计算字符集索引,确保不越界  
                int randomIndex = b % AllowedCharacters.Length;  
                sb.Append(AllowedCharacters[randomIndex]);  
            }    
            // 返回生成的密钥字符串  
            return sb.ToString();  
        }  

这个写法不用解释了吧,我直接在代码中注释的非常详细了。这样写的好处是,你可以指定生成的字节数,更具有灵活性,更通用,写到一个类中方便调用。使用 RandomNumberGenerator 还可以提高随机性和安全性。RandomNumberGenerator是新版本的写法,第一种写法中的RNGCryptoServiceProvider已经弃用了,当然,也是可以使用的。

这个写法的调用方法是:

// 创建 KeyGenerator 实例  
            KeyGenerator keyGenerator = new KeyGenerator();    
            // 生成一个长度为 32 的随机密钥  
            string key = keyGenerator.GenerateKey(32);    
            // 输出生成的密钥  
            Console.WriteLine("生成的密钥: " + key);  

看到了吧,长度可以随便你设定,这里设为32即可。


第三种写法:利用RandomNumberGenerator生成,但更简洁高效。

看写法:    
private static readonly string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    public static string GenerateKey(int length)
    {
        byte[] randomBytes = new byte[length];
        using var rng = RandomNumberGenerator.Create();
        rng.GetBytes(randomBytes);
        return new string(randomBytes.Select(b => Alphabet[b % (byte)Alphabet.Length]).ToArray());
    }

是不是非常简洁?[微笑]

这个写法通过 LINQ 和 BitConverter 来简化生成随机字符。代码中使用System.Security.Cryptography.RandomNumberGenerator.Create()生成随机字节,确保了生成的密钥具有良好的随机性和安全性,非常适合如密码、会话密钥或一次性令牌生成。利用LINQ表达式.Select(b => Alphabet[b % (byte)Alphabet.Length])将随机字节映射为预定义字母表中的字符,正好可以防止意外,因为这里需要的是32个字节(256位),这不一定正好是32个有效字符。唉,还是多说几句吧,应该不少人会经常弄混这个。

其实,主要是字符的大小和编码方式有关。在单字节编码(如ASCII)中,一个字符通常对应一个字节。但在多字节编码(如UTF-8、UTF-16、UTF-32)中,一个字符可能对应一个或多个字节。

如果是在单字节编码(如ASCII)中,一般是相同的。但是,如果是在多字节编码(如UTF-8)中,32字节可能包含多于32个字符,因为某些字符可能占用多于一个字节的空间。比如中文字符可能需要使用两到四个字节来表示。

你明白了吗?是不是又学到了?[呲牙]

下节内容我将讲解如何更好的写加密算法,如何安全加密文件,欢迎关注我!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表