sql - 如何在 SQL Server 中生成加密安全号码?

标签 sql sql-server cryptography

我目前正在使用 guid NEWID(),但我知道它在加密上并不安全。

是否有更好的方法在 SQL Server 中生成加密安全数字?

最佳答案

CRYPT_GEN_RANDOM 被记录为返回“加密随机数”。

它采用 18000 之间的长度参数,这是要返回的数字的长度(以字节为单位)。

长度 <= 8 字节。这可以转换为 SQL Server integer types 之一直截了当。

+-----------+------------------+---------+
| Data type |      Range       | Storage |
+-----------+------------------+---------+
| bigint    | -2^63 to 2^63-1  | 8 Bytes |
| int       | -2^31 to 2^31-1  | 4 Bytes |
| smallint  | -2^15 to 2^15-1  | 2 Bytes |
| tinyint   | 0 to 255         | 1 Byte  |
+-----------+------------------+---------+

其中三个是有符号整数,一个是无符号整数。以下内容将使用其各自数据类型的全部范围。

SELECT 
      CAST(CRYPT_GEN_RANDOM(1)  AS TINYINT),
      CAST(CRYPT_GEN_RANDOM(2)  AS SMALLINT),
      CAST(CRYPT_GEN_RANDOM(4)  AS INT),
      CAST(CRYPT_GEN_RANDOM(8)  AS BIGINT)

也可以提供比数据类型存储更短的值。

SELECT CAST(CRYPT_GEN_RANDOM(3)  AS INT)

在这种情况下,只能返回正数。符号位始终为 0,因为最后一个字节被视为 0x00。上述可以返回的可能数字范围在 0POWER(2, 24) - 1 之间(含)。

假设要求生成1到250之间的一些随机数

一种可能的方法是

SELECT  ( 1 + CAST(CRYPT_GEN_RANDOM(1)  AS TINYINT) % 250) AS X
INTO #T
FROM master..spt_values V1,  master..spt_values

但是这个方法有一个问题。

SELECT COUNT(*),X
FROM #T
GROUP BY X
ORDER BY X 

前十行结果是

+-------+----+
| Count | X  |
+-------+----+
| 49437 |  1 |
| 49488 |  2 |
| 49659 |  3 |
| 49381 |  4 |
| 49430 |  5 |
| 49356 |  6 |
| 24914 |  7 |
| 24765 |  8 |
| 24513 |  9 |
| 24732 | 10 |
+-------+----+

较低的数字(在本例中为 1 -6)的生成频率是其他数字的两倍,因为模函数有两个可能的输入可以生成每个结果。

一种可能的解决方案是丢弃所有 >= 250 的数字

UPDATE #T
SET    X = CASE
             WHEN Random >= 250 THEN NULL
             ELSE ( 1 + Random % 250 )
           END 
FROM #T
CROSS APPLY (SELECT CAST(CRYPT_GEN_RANDOM(1) AS TINYINT)) CA (Random)

这似乎在我的机器上有效,但可能不能保证 SQL Server 只会在 CASE 表达式中对 Random 的引用计算该函数一次。此外,它仍然存在需要第二次和后续传递来修复随机值被丢弃的 NULL 行的问题。

声明标量 UDF 可以解决这两个问题。

/*Work around as can't call CRYPT_GEN_RANDOM from a UDF directly*/
CREATE VIEW dbo.CRYPT_GEN_RANDOM1 
AS
SELECT CAST(CRYPT_GEN_RANDOM(1) AS TINYINT) AS Random

go


CREATE FUNCTION GET_CRYPT_GEN_RANDOM1()
RETURNS TINYINT
AS
BEGIN
    DECLARE @Result TINYINT

    WHILE (@Result IS NULL OR @Result >= 250)
            /*Not initialised or result to be discarded*/
        SELECT @Result = Random FROM dbo.CRYPT_GEN_RANDOM1 

    RETURN @Result

END

然后

UPDATE #T
SET    X  = dbo.GET_CRYPT_GEN_RANDOM1()

或者更直接地,人们可以简单地使用

CAST(CRYPT_GEN_RANDOM(8)  AS BIGINT) % 250

因为 bigint 的范围是如此之大,任何偏差都可能微不足道。从上面的查询可以生成 1 的方式有 73,786,976,294,838,208 种,249 的生成方式有 73,786,976,294,838,206 种。

如果即使是很小的可能偏差也是不允许的,您可以丢弃不在 -9223372036854775750 和 9223372036854775749 之间的任何值,如前所述。

关于sql - 如何在 SQL Server 中生成加密安全号码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13817452/

相关文章:

java - % 登录 Java 的 PreparedStatement

sql-server - 使用性能监视器来监视池连接

java - 将 PSS 参数中的 "salt length"参数更改为 BouncycaSTLe

java - 使用 Java SecureRandom 进行洗牌以实现加密目的?

java - 如何实现 CryptoSystem 、 PublicKey 接口(interface)

mysql - mysql 上的 SQL 正则表达式查询

mysql - SQL-使用自动增量id获取最近的插入

sql - 在 SQL Server 2008R2 中使用 CAST 函数除以 2 个数字

asp.net - 在 ASP.NET 应用程序中完全避免临时 SQL 是一个好习惯吗?

sql - 如何在 sql 数据库中保存顺序(排列)