HMAC在Kotlin中的签名

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

HMAC signature in Kotlin

问题

以下是您要翻译的内容:

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

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

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

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

val decodedKey = Base64.getDecoder().decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=")
//val binKeyHex = Hex.encodeHexString(decodedKey)
val toSign = "some string to sign"
val hMacSHA256 = Mac.getInstance("HmacSHA256")
val secretKey = SecretKeySpec(decodedKey, "HmacSHA256")
hMacSHA256.init(secretKey)
val data = hMacSHA256.doFinal(toSign.toByteArray())

val builder = StringBuilder()
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:

decoded_key = Base64.decode64('YW55IGNhcm5hbCBwbGVhc3VyZS4=')
to_sign = 'some string to sign'
bin_key = Array(decoded_key).pack('H*')
digest = OpenSSL::Digest::SHA256.new
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:

  val decodedKey = Base64.getDecoder().decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=")
  //val binKeyHex = Hex.encodeHexString(decodedKey)
  val toSign = "some string to sign"
  val hMacSHA256 = Mac.getInstance("HmacSHA256")
  val secretKey = SecretKeySpec(decodedKey, "HmacSHA256")
  hMacSHA256.init(secretKey)
  val data = hMacSHA256.doFinal(toSign.toByteArray())

  val builder = StringBuilder()
  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."。

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

puts ['an'].pack('H*') --> 错误,从 ASCII-8BIT 转换为 UTF-8  "\xA7"

puts ['am'].pack('H*') --> 错误,从 ASCII-8BIT 转换为 UTF-8  "\xA6"

puts ['gm'].pack('H*') --> 无错误,一个空行

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

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

puts bin_key.unpack('H*') --> a720cab7a5095eacebee

观察一下这个:

**a**720**ca**b7**a**5095**ea**ceb**e**e

**a**ny **ca**rn**a**l pl**ea**sur**e**.

我认为,这段 Ruby 代码可能只是不良实践。

(第二次尝试)
关于 `pack` 函数,我在(某些)Ruby源代码中进行了检查

```c
for (i=0; i++ < len; ptr++) {
    if (ISALPHA(*ptr))
        byte |= ((*ptr & 15) + 9) & 15;
    else
        byte |= *ptr & 15;
    if (i & 1)
        byte <<= 4;
    else {
        char c = castchar(byte);
        rb_str_buf_cat(res, &c, 1);
        byte = 0;
    }
}

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

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

import java.util.Base64
import kotlin.math.abs
import kotlin.text.*
import javax.crypto.*
import javax.crypto.spec.*
import kotlin.experimental.and

fun main() {
  val decodedKey = Base64.getDecoder().decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=")
  val builder2 = StringBuilder()
  var key = ByteArray(decodedKey.size)
  var i=0;
  decodedKey.forEachIndexed { nr, it -> 
      var c:Char = it.toChar()
      val v = if (c.isLetter()) ((it and 15) + 9) and 15 else it and 15
      builder2.append(String.format("%01X", v ))
      if ( (nr and 1) > 0 ){
          key[i] = (key[i] + v.toByte()).toByte()
          i += 1
      }
      else
      {
          key[i] = ( key[i] + ((v.toInt()).shl(4)).toByte() ).toByte()
      }
  }
  println(builder2)
  
  val toSign = "some string to sign"
  val hMacSHA256 = Mac.getInstance("HmacSHA256")
  val secretKey = SecretKeySpec(key, "HmacSHA256")
  hMacSHA256.init(secretKey)
  val data = hMacSHA256.doFinal(toSign.toByteArray())

  val builder = StringBuilder()
  data.forEach { builder.append(String.format("%02X", it)) }
  println(builder)
}
英文:

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:

puts [&#39;an&#39;].pack(&#39;H*&#39;) --&gt; error &quot;\xA7&quot; from ASCII-8BIT to UTF-8

puts [&#39;am&#39;].pack(&#39;H*&#39;) --&gt; error &quot;\xA6&quot; from ASCII-8BIT to UTF-8

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:

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:

    for (i=0; i++ &lt; len; ptr++) {
	if (ISALPHA(*ptr))
	    byte |= ((*ptr &amp; 15) + 9) &amp; 15;
	else
	    byte |= *ptr &amp; 15;
	if (i &amp; 1)
	    byte &lt;&lt;= 4;
	else {
	    char c = castchar(byte);
	    rb_str_buf_cat(res, &amp;c, 1);
	    byte = 0;
	}
    }

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:

import java.util.Base64
import kotlin.math.abs
import kotlin.text.*
import javax.crypto.*
import javax.crypto.spec.*
import kotlin.experimental.and

fun main() {
  val decodedKey = Base64.getDecoder().decode(&quot;YW55IGNhcm5hbCBwbGVhc3VyZS4=&quot;)
  val builder2 = StringBuilder()
  var key = ByteArray(decodedKey.size)
  var i=0;
  decodedKey.forEachIndexed { nr, it -&gt; 
      var c:Char = it.toChar()
      val v = if (c.isLetter()) ((it and 15) + 9) and 15 else it and 15
      builder2.append(String.format(&quot;%01X&quot;, v ))
      if ( (nr and 1) &gt; 0 ){
          key[i] = (key[i] + v.toByte()).toByte()
          i += 1
      }
      else
      {
          key[i] = ( key[i] + ((v.toInt()).shl(4)).toByte() ).toByte()
      }
  }
  println(builder2)
  
  val toSign = &quot;some string to sign&quot;
  val hMacSHA256 = Mac.getInstance(&quot;HmacSHA256&quot;)
  val secretKey = SecretKeySpec(key, &quot;HmacSHA256&quot;)
  hMacSHA256.init(secretKey)
  val data = hMacSHA256.doFinal(toSign.toByteArray())

  val builder = StringBuilder()
  data.forEach { builder.append(String.format(&quot;%02X&quot;, it)) }
  println(builder)
}

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:

确定