电脑装配网

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

 人阅读 | 作者pangding | 时间:2024-03-21 19:18

上次咱们聊到了密钥的生成,还扒了扒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个字符,因为某些字符可能占用多于一个字节的空间。比如中文字符可能需要使用两到四个字节来表示。

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

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


文章标签:

本文链接:『转载请注明出处』