英文:
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 ['an'].pack('H*') --> error "\xA7" from ASCII-8BIT to UTF-8
puts ['am'].pack('H*') --> error "\xA6" from ASCII-8BIT to UTF-8
puts ['gm'].pack('H*') --> 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('H*') --> 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++ < 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;
}
}
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("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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论