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

20 sql sql-server cryptography

我目前正在使用guid,NEWID()但我知道它不具有加密安全性.

有没有更好的方法在SQL Server中生成加密安全号码?

Mar*_*ith 26

CRYPT_GEN_RANDOM 记录为返回"加密随机数".

它需要一个长度参数1,8000它是以字节为单位返回的数字的长度.

长度<= 8字节.这可以直接转换为SQL Server整数类型之一.

+-----------+------------------+---------+
| 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  |
+-----------+------------------+---------+
Run Code Online (Sandbox Code Playgroud)

其中三个是有符号整数,一个是无符号整数.以下各项将使用各自数据类型的完整范围.

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)
Run Code Online (Sandbox Code Playgroud)

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

SELECT CAST(CRYPT_GEN_RANDOM(3)  AS INT)
Run Code Online (Sandbox Code Playgroud)

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

假设要求是在两者之间生成一些随机数1 and 250.

一种可行的方法是

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

但是这种方法存在问题.

SELECT COUNT(*),X
FROM #T
GROUP BY X
ORDER BY X 
Run Code Online (Sandbox Code Playgroud)

前十行结果是

+-------+----+
| Count | X  |
+-------+----+
| 49437 |  1 |
| 49488 |  2 |
| 49659 |  3 |
| 49381 |  4 |
| 49430 |  5 |
| 49356 |  6 |
| 24914 |  7 |
| 24765 |  8 |
| 24513 |  9 |
| 24732 | 10 |
+-------+----+
Run Code Online (Sandbox Code Playgroud)

较低的数字(在这种情况下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)
Run Code Online (Sandbox Code Playgroud)

这似乎可以在我的机器上运行,但可能无法保证SQL Server只会RandomCASE表达式中的两个引用中对函数进行一次评估.此外,它仍然存在需要第二次和后续传递来修复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
Run Code Online (Sandbox Code Playgroud)

然后

UPDATE #T
SET    X  = dbo.GET_CRYPT_GEN_RANDOM1()
Run Code Online (Sandbox Code Playgroud)

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

CAST(CRYPT_GEN_RANDOM(8)  AS BIGINT) % 250
Run Code Online (Sandbox Code Playgroud)

基于这样的范围bigint是如此巨大,以至于任何偏见都可能是微不足道的.1可以生成73,786,976,294,838,208种方法,并且249可以从上面的查询中获得73,786,976,294,838,206种方法.

如果不允许有可能的小偏差,您可以丢弃任何值NOT BETWEEN -9223372036854775750 AND 9223372036854775749,如前所示.


lep*_*pie 11

有趣的问题:)

我认为这会奏效: CRYPT_GEN_RANDOM