【问题标题】:How to parse the password hash hashed by Spring Security?如何解析 Spring Security 散列的密码哈希?
【发布时间】:2020-12-21 14:16:49
【问题描述】:

我想将我的用户从我的应用程序迁移到外部 SSO。我希望我的用户保留他们现有的密码,因此我想检索散列密码并将其提供给 SSO 应用程序。两边的算法都是一样的(pbkdf2-sha256),所以它应该可以工作。

因此我需要能够从数据库中的条目中检索盐和哈希密码。

我的应用程序使用 Spring Security。

看来我需要手动执行此操作,因为 Pbkdf2PasswordEncoder 实现的接口 PasswordEncoder 没有提供从存储值中检索初始元素的方法。

实现这一目标的正确方法是什么?

谢谢

编辑:

这是我的代码

Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder("", 27500, 512);
String encoded = encoder.encode("password");
System.out.println(encoded); // prints the hex encoded hashed password: 2a319aaf0252d77e671cf1b074f149f7eed1b362afb47bef84e9b01a8140b26a733cd22df007d68668915c7bd51af9eefda1662216a184fa7eb034c176ce9518fc83f3fd935a2d3d
final byte[] digested = Hex.decode(encoded);
byte[] salt = EncodingUtils.subArray(digested, 0, 7);
byte[] password = EncodingUtils.subArray(digested, 8, digested.length);
System.out.println(new String(Base64.getEncoder().encode(salt))); // prints KjGarwJS134=
System.out.println(new String(Base64.getEncoder().encode(password))); // prints HPGwdPFJ9+7Rs2KvtHvvhOmwGoFAsmpzPNIt8AfWhmiRXHvVGvnu/aFmIhahhPp+sDTBds6VGPyD8/2TWi09

如果我然后转到this website 并输入以下值:

  • 主密码:密码
  • 盐:KjGarwJS134=
  • 迭代:27500
  • dkLen: 512
  • PBE 密码:sha256

然后我希望输出与我的代码的最后一个输出相匹配,但事实并非如此。

【问题讨论】:

  • @Vladimir 我认为他们想要访问 RAW 散列(但加密)密码位。并将该信息传输到新的 SSO 服务(这意味着用户不必更改密码)。
  • 弗拉基米尔,GhostCat 是对的,我只需将原始密码复制到一个新软件,它以不同的方式存储它(base64 与十六进制等,...)但使用相同的哈希算法。所以基本上我需要从存储的条目中检索盐。
  • @sroup 我猜在线工具有问题/不同。我尝试了您的代码并使用盐和密码生成了一个新的哈希,结果是相等的。所以盐是正确的。不过,我设置encoder.setAlgorithm(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256); 使用您想要的算法。

标签: java spring spring-security password-hash


【解决方案1】:

您显然无法从哈希中取回密码,但我认为您不是在问这个问题。以下是你的做法:

  1. 首先检查它是否是base64。如果是这样,请获取您的字符串并对其进行 base64 解码,将其转换为字节数组。 (默认只是十六进制半字节,即选项 2)。
  2. 如果不是,那么它只是十六进制半字节。你可以告诉;如果整个事物仅由数字和字母 a-f 组成,就是这个,否则就是 #1 (base64)。将十六进制半字节解析回字节数组相当简单;通过 parseInt 每 2 个字符折腾一次:Integer.parseInt(in.substring(x, x + 2), 0x10); - 末尾的 0x10 很重要。
  3. 由此获得的字节数组是盐和散列,按该顺序连接。默认情况下,salt 为 8 个字节,但这在 spring 中都是可配置的。所以,去掉数组的前 8 个字节 - 那是你的盐。
  4. 剩下的就是“只是”原始哈希。

【讨论】:

  • 我没有更改默认配置,所以它确实是 8 个字节和十六进制半字节。但是我没有使用你描述的算法来解码它。我正在使用 org.springframework.security.Hex.decode() 这似乎是 Spring Security 正在使用的。您能否详细说明一下,inx 是什么?
  • Hex.decode 也应该可以工作。 'in' 是您从数据库中获得的字符串。 x 是索引。 Hex.encode 只是将字节序列 0xAA 0x9F 转换为字符串“AA9F”。 Hex.decode 取消了这一点; Hex.decode 的来源会做类似byte[] decode(String in) {byte[] out = new byte[in.length() / 2]; for (int i = 0; i < out.length; i++) out[i] = (byte) Integer.parseInt(in.substring(i*2, i*2 + 2), 0x10); }
  • 好吧,我想我的代码中有一个错误,因为我正在使用您的算法和此解码功能,但它不起作用。我将添加一段代码来向您展示我在做什么。您是否确认,一旦我获得了 salt 和密码,如果我对它们进行 base64 编码并将密码和 base64-salt 提供给this website,那么我应该获得 base64-password,只要我输入相同的选项?
  • @sroup - 不,您无法从哈希中获取密码,这就是哈希的意义所在! - 拿盐,使用盐和密码(将密码发送到您无法控制的网站是......好吧,它让我的皮肤发痒)使用 PBKDF2-SHA256 生成哈希。那么,该哈希值的盐值应该与应用我的算法得到的值相匹配。
  • 你一直在说“原始密码”。要么你把事情弄糊涂了,要么就是你自己弄糊涂了。散列不是密码。从“哈希”回到“密码”是不可能的,这就是它们的全部意义所在。除非你解开你大脑中的这个烂摊子,否则你不会弄明白的。
猜你喜欢
  • 1970-01-01
  • 2023-03-09
  • 2012-03-28
  • 1970-01-01
  • 2015-04-20
  • 1970-01-01
  • 1970-01-01
  • 2011-04-29
  • 1970-01-01
相关资源
最近更新 更多