【问题标题】:Encrypting a private key in Ruby, using aes-128-ctr + scrypt在 Ruby 中加密私钥,使用 aes-128-ctr + scrypt
【发布时间】:2017-03-17 12:46:15
【问题描述】:

我需要为以太坊构建一个私钥加密,它应该与go-ethereum 实现兼容(Ruby 加密的密钥也应该与以太坊实现兼容)。

以太坊使用 32 位私钥,例如这个(十六进制编码):

1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef

如果我将此密钥导入go-ethereum 实现并使用密码“password”对其进行加密,它会生成以下输出:

{
    "address":"1be31a94361a391bbafb2a4ccd704f57dc04d4bb",
    "crypto":{
        "cipher":"aes-128-ctr",
        "ciphertext":"62bbf1a5a93b8ba8c66b70b3381f9f5badf44b35287614d309d760ebeec47139",
        "cipherparams":{
            "iv":"a4a6638ea73872c07d62fa065f37f790"
        },
        "kdf":"scrypt",
        "kdfparams":{
            "dklen":32,
            "n":262144,
            "p":1,
            "r":8,
            "salt":"69ccd8c258bb50ac2effd65837e09e45b8bd9a747a1a1f3558b65a16e2f46f1a"
        },
        "mac":"68ca6bc011d4d656e12a34cefd28005dbf76d9cfac15db2eaa83920eec5b38a9"
    },
    "id":"9863070b-6c16-4aef-8188-2a34660192bf",
    "version":3
}

所以使用所有的kdf(key derivation function)参数,生成密文

62bbf1a5a93b8ba8c66b70b3381f9f5badf44b35287614d309d760ebeec47139

我现在尝试使用 Ruby 重现相同的密文,同时查看 Go 实现。这是我的代码:

# hard coded password
password = "password"

# hard coded test private key
plain_private_key = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
puts "------------ Encryption input ------------ "
puts "Clear private key = " + plain_private_key

# Scrypt params, same as in Geth/Ethereum
n = 262144
r = 8
p = 1
dklen = 32

# using same salt as Ethereum used
salt = "69ccd8c258bb50ac2effd65837e09e45b8bd9a747a1a1f3558b65a16e2f46f1a"
# using same iv as Ethereum used
iv = "a4a6638ea73872c07d62fa065f37f790"

puts "------------ Scrypt parameters ------------ "
puts "Salt str = " + salt
puts "Iv str = " + iv
puts "n = " + n.to_s
puts "r = " + r.to_s
puts "p = " + p.to_s
puts "dklen = " + dklen.to_s

# Generate derived key
derived_key = SCrypt::Engine.scrypt(password, salt, n, r, p, dklen)
puts "------------ Scrypt output ------------ "
puts "Derived key from password = " + derived_key.unpack("H*")[0]

# Encrypt with derived key
cipher_name = "aes-128-ctr"
cipher = OpenSSL::Cipher.new cipher_name
cipher.encrypt
cipher.iv = iv
cipher.key = derived_key
encrypted = cipher.update([plain_private_key].pack("H*")) + cipher.final
puts "------------ Encryption output ------------ "
puts "Cipher text = " + encrypted.unpack("H*")[0]

# Decrypt with derived key
decipher = OpenSSL::Cipher.new cipher_name
decipher.decrypt
decipher.iv = iv
decipher.key = derived_key
decrypted = decipher.update(encrypted) + decipher.final
decrypted_str = decrypted.unpack("H*")[0]
puts "------------ Decryption output ------------ "
puts "Decrypted: " + decrypted_str
puts "Decryption worked: " + (plain_private_key == decrypted_str).to_s

这是输出:

------------ Encryption input ------------
Clear private key = 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
------------ Scrypt parameters ------------
Salt str = 69ccd8c258bb50ac2effd65837e09e45b8bd9a747a1a1f3558b65a16e2f46f1a
Iv str = a4a6638ea73872c07d62fa065f37f790
n = 262144
r = 8
p = 1
dklen = 32
------------ Scrypt output ------------
Derived key = b6e4410aa658f21213c7e55bacbbd8093e67f7f1738e7235335b58a2b690dcf5
------------ Encryption output ------------
Cipher text = 6fddd3d2199edf65a17d9277d2328f5357e70a5be2e173d17681883ef5a3a27e
------------ Decryption output ------------
Decrypted: 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
Decryption worked: true

但密文与go-ethereum 生成的不同,使用相同的输入和参数。

6fddd3d2199edf65a17d9277d2328f5357e70a5be2e173d17681883ef5a3a27e

谁能帮帮我?

【问题讨论】:

  • 如何找到私钥?如果我有你上面列出的所有数据
  • @Proton 在我的示例中,我已经拥有原始的 64 位私钥,但想创建一个密钥文件。您需要做相反的事情,解密您的密钥文件(使用您的密码)以获取原始 64 位密钥文件。我不认为像 geth 这样的标准工具为此提供了命令(可能是出于安全原因,因此没有经验的用户不会公开他们的密钥),但您可以编写自己的脚本。我通常在 Node.js 中使用github.com/ethereumjs/keythereum,查看keythereum.recover(password, keyObject)

标签: ruby encryption cryptography aes scrypt


【解决方案1】:

密钥派生的 salt 和加密的 iv 都需要从十六进制转换为二进制字符串,与私钥相同:

# using same salt as Ethereum used
salt = ["69ccd8c258bb50ac2effd65837e09e45b8bd9a747a1a1f3558b65a16e2f46f1a"].pack('H*')
# using same iv as Ethereum used
iv = ["a4a6638ea73872c07d62fa065f37f790"].pack('H*')

这给出了与 go 实现相同的加密密钥结果:

------------ Encryption output ------------
Cipher text = 62bbf1a5a93b8ba8c66b70b3381f9f5badf44b35287614d309d760ebeec47139

我注意到的另一件事与您的直接问题无关:加密和解密仅使用派生密钥的前 16 个字节。目前,Ruby OpenSSL 绑定只是将密钥截断为正确的长度,因此目前一切正常,但这将change in future releases。这意味着您的代码在升级后将无法正常工作。您需要提供正确的密钥长度:

cipher.key = derived_key[0...16]

派生密钥的其他 16 个字节用作身份验证密钥,因此您可以检查是否有任何内容被篡改(您需要 Keccak 哈希函数的 Ruby 实现来实现它)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-26
    • 1970-01-01
    • 2013-08-24
    • 1970-01-01
    • 2011-09-26
    相关资源
    最近更新 更多