将传入凭据与 Oracle 散列密码进行比较

Posted

技术标签:

【中文标题】将传入凭据与 Oracle 散列密码进行比较【英文标题】:Comparing incoming credentials against Oracle hashed password 【发布时间】:2018-10-11 13:45:32 【问题描述】:

我有一个 Oracle 数据库。我想在 C# 中加密传入的密码,并将其与存储在名为 sys.user$ 的内置 oracle 表中的值进行比较,特别是“spare4”列。

可以这样做吗?我试图加密密码,但它没有生成与 Oracle 数据库表中生成的值相同的输出,所以很明显我缺少 Oracle 应用的逻辑。有什么想法吗?

据我了解,前 40 个字符是散列密码,后 20 个字符是散列盐值。

从 Oracle 11g 开始,它使用 Salt(不知道 Oracle 是如何生成它的)并应用于密码,然后使用 SHA-1 对其进行哈希处理以生成 S: 根据以下博客 password hashes 的一部分备​​用值和Spare 4 tips

这是当前代码:

    public static string GenerateSaltedSHA1(string plainTextString, int saltSize)
    
        HashAlgorithm algorithm = new SHA1Managed();
        var saltBytes = GenerateSalt(saltSize);
        var plainTextBytes = Encoding.ASCII.GetBytes(plainTextString);

        var plainTextWithSaltBytes = AppendByteArrays(plainTextBytes, saltBytes);
        var saltedSHA1Bytes = algorithm.ComputeHash((byte[]) plainTextWithSaltBytes);
        var saltedSHA1WithAppendedSaltBytes = AppendByteArrays(saltedSHA1Bytes, saltBytes);

        return Convert.ToBase64String(saltedSHA1WithAppendedSaltBytes);
    

    private static byte[] GenerateSalt(int saltSize)
    
        var rng = new RNGCryptoServiceProvider();
        var buff = new byte[saltSize];
        rng.GetBytes(buff);
        return buff;
    

   private static byte[] AppendByteArrays(byte[] byteArray1, byte[] byteArray2)
    
        var byteArrayResult =
            new byte[byteArray1.Length + byteArray2.Length];

        for (var i = 0; i < byteArray1.Length; i++)
            byteArrayResult[i] = byteArray1[i];
        for (var i = 0; i < byteArray2.Length; i++)
            byteArrayResult[byteArray1.Length + i] = byteArray2[i];

        return byteArrayResult;
    

我了解在 Oracle 中 DBMS_CRYPTO 中有一些函数,例如哈希,可用于根据特定类型创建哈希,例如密码上的 MD5、SHA-1 等。

如果有更好的方法,我愿意接受建议。

【问题讨论】:

我认为您不想应用新生成的盐值。您想应用来自 spare4same 盐值。 你是对的,但根据他们关于 Oracle 如何生成盐的文档/博客,这并不明显。 你不需要知道它是如何生成的。只需将其从备用 4 中拉出即可。 Spare4 中的盐是纯文本盐,而不是盐的哈希值。那没有任何意义。盐不是“秘密”。 【参考方案1】:

如果我的 cmets 不清楚(或不相信),这里有一个小 SQL 语句,可以满足您的需求。这在 12c 上运行——但不能保证它适用于所有安全选项。如果你在sys.user$.spare4 中有一个 S: 部分,它可能应该可以工作。

with trial as 
(SELECT name, 
-- The 1st 20 bytes (40 characters) of the "S" part are the hashed password
substr(substr(spare4,3,60),1,40) hashed_pwd, 
-- The last 10 bytes (20 characters) of the "S" part are the plaintext salt
substr(substr(spare4,3,60),-20) salt, 
-- I want to know if the password for user MYUSERNAME is "MYPASSWORDGUESS"...
'MYPASSWORDGUESS' trial_password
from sys.user$
where name = 'MYUSERNAME' ),
hashit as (
-- This SELECT adds the salt to our trial password and hashes it.
select name, 
       trial_password, 
       hashed_pwd, 
       salt,
       -- The "3" is for the SH1 algorithm
       sys.dbms_crypto.hash(
           utl_raw.cast_to_raw(trial_password) || cast (salt as raw(10) ),3) 
          hashed_salted_trial
from trial )
-- If the resulting hash matches what Oracle has stored in spare4, it is good.
SELECT name, 
       trial_password, 
       case when hashed_pwd = hashed_salted_trial 
           then 'Y' ELSE 'N' END password_match
from hashit
;

根据您的数据库的需要更新“MYUSERNAME”和“MYPASSWORDGUESS”。

更新

基于我在 C# 中的原始代码,是否有在 C# 中执行以下操作的等效方法? sys.dbms_crypto.hash(utl_raw.cast_to_raw(trial_password) || cas​​t (salt as raw(10)),3)

utl_raw.cast_to_raw(trial_password) 在您的帖子中与Encoding.ASCII.GetBytes(plainTextString) 做同样的事情。每个的目标都是猜测密码并将其转换为字节。

cast(salt as raw(10)) 在您的帖子中没有等效项。稍后会详细介绍。

|| 是您在 Oracle 中连接 RAW 值的方式。它的作用与您帖子中的AppendByteArrays 相同。

sys.dbms_crypto.hash(..., 3) 是您在 Oracle 中生成 160 位 SHA-1 哈希的方式。我想这就是 algorithm.ComputeHash 在你的帖子中所做的。

因此,您的代码中唯一缺少的是 cast(salt as raw(10)) 的等效项。

正如我们所讨论的,您不能为此生成自己的盐。您必须使用user$.spare4 中编码的盐。那将是该字段的“S:”部分的最后 20 个字符。它看起来像这样:FA338B110F78548CCB44。每两个字符形成一个十六进制 BYTE,为您提供盐的一个字节。因此,在我的示例中,盐的第一个字节(由“FA”给出)是 15(对于“F”)x16+10(对于“A”)= 250。

希望这些逻辑足以让您在 C# 中实现。但是,实际上并没有必要这样做,因为 salt 的输入值只能来自 Oracle 数据库中的 user$.spare4。反正你给去了Oracle,就这么简单

SELECT cast(substr(substr(spare4,3,60),-20) as RAW) FROM user$

原来如此

SELECT spare4 FROM user$

剩下的就用 C# 来完成。你还不如让 Oracle 做它擅长的事情。

【讨论】:

我明白了。感谢您的意见和示例。基于我在 C# 中的原始代码是否有在 C# 中执行以下操作的等效方法? sys.dbms_crypto.hash(utl_raw.cast_to_raw(trial_password) || cas​​t (salt as raw(10) ),3) 我更新了我的答案,回复了您的评论。

以上是关于将传入凭据与 Oracle 散列密码进行比较的主要内容,如果未能解决你的问题,请参考以下文章

Shiro - 无法使用散列密码进行身份验证

使用 Spring Security BCryptPasswordEncoder 对密码进行哈希处理时凭据错误

传输前散列密码? (网络)

比较来自不同网络服务器的散列​​密码

oracle 数据库 显示新建数据库连接失败 错误原因ora-01017:用户名 口令无效 登陆被拒绝

如何从 11g Oracle (PL/SQL) 上的 URL 传递凭据和下载文件?