将传入凭据与 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 等。
如果有更好的方法,我愿意接受建议。
【问题讨论】:
我认为您不想应用新生成的盐值。您想应用来自spare4
的 same 盐值。
你是对的,但根据他们关于 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) || cast (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) || cast (salt as raw(10) ),3) 我更新了我的答案,回复了您的评论。以上是关于将传入凭据与 Oracle 散列密码进行比较的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring Security BCryptPasswordEncoder 对密码进行哈希处理时凭据错误