HMAC在Kotlin中的签名

huangapple go评论105阅读模式
英文:

HMAC signature in Kotlin

问题

以下是您要翻译的内容:

我有一些用Ruby编写的旧代码:

  1. decoded_key = Base64.decode64('YW55IGNhcm5hbCBwbGVhc3VyZS4=')
  2. to_sign = 'some string to sign'
  3. bin_key = Array(decoded_key).pack('H*')
  4. digest = OpenSSL::Digest::SHA256.new
  5. puts OpenSSL::HMAC.hexdigest(digest, bin_key, to_sign)

您可以在此处运行它以查看结果。

我正在尝试在Kotlin中获得相同的结果(相同的签名)。
我尝试过的代码:

  1. val decodedKey = Base64.getDecoder().decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=")
  2. //val binKeyHex = Hex.encodeHexString(decodedKey)
  3. val toSign = "some string to sign"
  4. val hMacSHA256 = Mac.getInstance("HmacSHA256")
  5. val secretKey = SecretKeySpec(decodedKey, "HmacSHA256")
  6. hMacSHA256.init(secretKey)
  7. val data = hMacSHA256.doFinal(toSign.toByteArray())
  8. val builder = StringBuilder()
  9. data.forEach { builder.append(String.format("%02X", it)) }

您可以在此处运行它以查看结果。

如果我们在Ruby代码中删除bin_key = Array(decoded_key).pack('H*'),只需将decoded_key传递给hexdigest函数,结果将是相同的。
不幸的是,我无法修改Ruby代码,所以我需要在Kotlin中使其相同。

在Kotlin中如何执行相同的操作以获得相同的结果?

英文:

I have legacy code written in ruby:

  1. decoded_key = Base64.decode64('YW55IGNhcm5hbCBwbGVhc3VyZS4=')
  2. to_sign = 'some string to sign'
  3. bin_key = Array(decoded_key).pack('H*')
  4. digest = OpenSSL::Digest::SHA256.new
  5. puts OpenSSL::HMAC.hexdigest(digest, bin_key, to_sign)

You may run it HERE to see the result.

I am trying to get same result (same signature) in Kotlin.
The code i tried so far:

  1. val decodedKey = Base64.getDecoder().decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=")
  2. //val binKeyHex = Hex.encodeHexString(decodedKey)
  3. val toSign = "some string to sign"
  4. val hMacSHA256 = Mac.getInstance("HmacSHA256")
  5. val secretKey = SecretKeySpec(decodedKey, "HmacSHA256")
  6. hMacSHA256.init(secretKey)
  7. val data = hMacSHA256.doFinal(toSign.toByteArray())
  8. val builder = StringBuilder()
  9. data.forEach { builder.append(String.format("%02X", it)) }

You may run it HERE to see the result

If we remove bin_key = Array(decoded_key).pack('H*') in ruby code and just pass decoded_key to hexdigest function - the results will be the same.
Unfortunately i can't modify ruby code, so i need to make it same in Kotlin.

How may i do the same in Kotlin to get same result?

答案1

得分: 4

并不是 Ruby 专家,但你确定 Array(decoded_key).pack('H*') 实际上会得到解码密钥的字符的十六进制表示吗?
输出的大小只有 10,而 decoded_key 是 "any carnal pleasure."。

我用你提供的链接进行了一些实验:

  1. puts ['an'].pack('H*') --> 错误,从 ASCII-8BIT 转换为 UTF-8 "\xA7"
  2. puts ['am'].pack('H*') --> 错误,从 ASCII-8BIT 转换为 UTF-8 "\xA6"
  3. puts ['gm'].pack('H*') --> 无错误,一个空行

据我所知,pack('H*') 指示 Ruby 将提供的字符串解析为十六进制字符串,因此在前两种情况下,它看到一个 "a" 并且认为它要解析十六进制,对所有后续字符进行 15 取模,从而丢失了很多信息。不清楚 Ruby 在字符串以非十六进制字符开头时会尝试做什么。

以下内容也证明了我的观点:

  1. puts bin_key.unpack('H*') --> a720cab7a5095eacebee
  2. 观察一下这个:
  3. **a**720**ca**b7**a**5095**ea**ceb**e**e
  4. **a**ny **ca**rn**a**l pl**ea**sur**e**.
  5. 我认为,这段 Ruby 代码可能只是不良实践。
  6. (第二次尝试)
  7. 关于 `pack` 函数,我在(某些)Ruby源代码中进行了检查
  8. ```c
  9. for (i=0; i++ < len; ptr++) {
  10. if (ISALPHA(*ptr))
  11. byte |= ((*ptr & 15) + 9) & 15;
  12. else
  13. byte |= *ptr & 15;
  14. if (i & 1)
  15. byte <<= 4;
  16. else {
  17. char c = castchar(byte);
  18. rb_str_buf_cat(res, &c, 1);
  19. byte = 0;
  20. }
  21. }

因此,它会将所有字母 a-zA-Z 视为相同:模 16,+9,模 16。
对于不是 a-zA-Z 的字符,它只会进行模 16 运算。

以下是如何在 Kotlin 中计算相同的 HMAC:

  1. import java.util.Base64
  2. import kotlin.math.abs
  3. import kotlin.text.*
  4. import javax.crypto.*
  5. import javax.crypto.spec.*
  6. import kotlin.experimental.and
  7. fun main() {
  8. val decodedKey = Base64.getDecoder().decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=")
  9. val builder2 = StringBuilder()
  10. var key = ByteArray(decodedKey.size)
  11. var i=0;
  12. decodedKey.forEachIndexed { nr, it ->
  13. var c:Char = it.toChar()
  14. val v = if (c.isLetter()) ((it and 15) + 9) and 15 else it and 15
  15. builder2.append(String.format("%01X", v ))
  16. if ( (nr and 1) > 0 ){
  17. key[i] = (key[i] + v.toByte()).toByte()
  18. i += 1
  19. }
  20. else
  21. {
  22. key[i] = ( key[i] + ((v.toInt()).shl(4)).toByte() ).toByte()
  23. }
  24. }
  25. println(builder2)
  26. val toSign = "some string to sign"
  27. val hMacSHA256 = Mac.getInstance("HmacSHA256")
  28. val secretKey = SecretKeySpec(key, "HmacSHA256")
  29. hMacSHA256.init(secretKey)
  30. val data = hMacSHA256.doFinal(toSign.toByteArray())
  31. val builder = StringBuilder()
  32. data.forEach { builder.append(String.format("%02X", it)) }
  33. println(builder)
  34. }
英文:

Not a Ruby expert either, but are you sure that Array(decoded_key).pack('H*') actually results in the HEX representation of the characters of the decoded key?
The size of the output is only 10, while the decoded_key is "any carnal pleasure."

I did some experimenting with the link you provided:

  1. puts [&#39;an&#39;].pack(&#39;H*&#39;) --&gt; error &quot;\xA7&quot; from ASCII-8BIT to UTF-8
  2. puts [&#39;am&#39;].pack(&#39;H*&#39;) --&gt; error &quot;\xA6&quot; from ASCII-8BIT to UTF-8
  3. puts [&#39;gm&#39;].pack(&#39;H*&#39;) --&gt; no error, a blank line

As far as I understand, pack('H*') instructs ruby to parse the provided string as a HEX string, so in the first two cases, it sees an a and it believes that it's going to parse HEX, treating all subsequent characters modulo 15, thereby loosing a lot of info. No idea what ruby tries to do when the string starts with a non-HEX character.

The following also illustrates my point:

  1. puts bin_key.unpack(&#39;H*&#39;) --&gt; a720cab7a5095eacebee

Look at this:

a720cab7a5095eacebee

any carnal pleasure.

IMHO, the ruby code might be just bad practice.

(2nd go at it)
As for the pack function, I checked it in (some) Ruby source:

  1. for (i=0; i++ &lt; len; ptr++) {
  2. if (ISALPHA(*ptr))
  3. byte |= ((*ptr &amp; 15) + 9) &amp; 15;
  4. else
  5. byte |= *ptr &amp; 15;
  6. if (i &amp; 1)
  7. byte &lt;&lt;= 4;
  8. else {
  9. char c = castchar(byte);
  10. rb_str_buf_cat(res, &amp;c, 1);
  11. byte = 0;
  12. }
  13. }

So it will treat all letters a-zA-Z the same: mod 16, +9 , mod 16.
For chars that are not a-zA-Z, it will just do mod 16.

Here's how to compute the same HMAC in Kotlin:

  1. import java.util.Base64
  2. import kotlin.math.abs
  3. import kotlin.text.*
  4. import javax.crypto.*
  5. import javax.crypto.spec.*
  6. import kotlin.experimental.and
  7. fun main() {
  8. val decodedKey = Base64.getDecoder().decode(&quot;YW55IGNhcm5hbCBwbGVhc3VyZS4=&quot;)
  9. val builder2 = StringBuilder()
  10. var key = ByteArray(decodedKey.size)
  11. var i=0;
  12. decodedKey.forEachIndexed { nr, it -&gt;
  13. var c:Char = it.toChar()
  14. val v = if (c.isLetter()) ((it and 15) + 9) and 15 else it and 15
  15. builder2.append(String.format(&quot;%01X&quot;, v ))
  16. if ( (nr and 1) &gt; 0 ){
  17. key[i] = (key[i] + v.toByte()).toByte()
  18. i += 1
  19. }
  20. else
  21. {
  22. key[i] = ( key[i] + ((v.toInt()).shl(4)).toByte() ).toByte()
  23. }
  24. }
  25. println(builder2)
  26. val toSign = &quot;some string to sign&quot;
  27. val hMacSHA256 = Mac.getInstance(&quot;HmacSHA256&quot;)
  28. val secretKey = SecretKeySpec(key, &quot;HmacSHA256&quot;)
  29. hMacSHA256.init(secretKey)
  30. val data = hMacSHA256.doFinal(toSign.toByteArray())
  31. val builder = StringBuilder()
  32. data.forEach { builder.append(String.format(&quot;%02X&quot;, it)) }
  33. println(builder)
  34. }

huangapple
  • 本文由 发表于 2020年8月19日 18:12:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/63484772.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定