Go 和 Ruby 之间的互操作性:验证 RSA 签名

问题描述 投票:0回答:1

美好的一天!

我有一个 ruby 中的 sinatra 应用程序,它需要一条以 base64 编码的消息和消息签名,然后使用公钥来验证它。我们正在运行 ruby 3.2 并使用 openssl。该应用程序运行良好,许多用户已经使用它多年。现在我正在用 Go 编写一个新客户端,因此我为消息定义了一个结构,将其编组为 JSON,以 Base64 进行编码,使用我的私钥对其进行哈希处理和签名。没有什么花哨。但 ruby 应用程序中的验证总是失败。

希望有人可以分享任何提示,非常感谢任何帮助。

这里有一些重现问题的示例代码。我在带有 M1 芯片的 Mac 上运行这个程序(不认为这有什么关系,但如果发现它是苹果的魔法在起作用,也不会感到惊讶)。 Go 版本 1.22.2 和 Ruby 3.2.3。

红宝石部分:

def validate(public_key, message, signature)
  p_key = OpenSSL::PKey::RSA.new(public_key)
  return p_key.verify('sha256', Base64.strict_decode64(signature), message)
end

其中

public_key
是带有pem编码公钥的字符串,
message
是base64编码的有效负载,
signature
是签名。

去部分:

func Test_RequestSignature(t *testing.T) {
  const pemKey = `-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
`

  data := []byte(pemKey)
  block, _ := pem.Decode(data)
  key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)

  rq := request{
    CreatedAt: time.Now().Unix(),
    AppId:     1,
    Message:   "something we'd like to sign of course",
    Uuid:      "030D842B-700D-41F6-BFB6-5CB10ADCA4EF",
  }

  encoded := rq.ToBase64()
  signature := rq.Signature(key)

  // Print stuff so I can copy and paste in my ruby snippet
  fmt.Println("message: ", encoded)
  fmt.Println("signature: ", signature)

  // Validate here, as sanity check
  publicKey := key.PublicKey

  decoded, _ := base64.StdEncoding.Strict().DecodeString(encoded)
  hashed := sha256.Sum256(decoded)

  sig, _ := base64.StdEncoding.Strict().DecodeString(signature)
  err := rsa.VerifyPKCS1v15(&publicKey, crypto.SHA256, hashed[:], sig)
  assert.Nil(t, err)
}

type request struct {
  CreatedAt int64  `json:"created_at"`
  AppId     int    `json:"app_id"`
  Message   string `json:"message"`
  Uuid      string `json:"uuid"`
}

func (r request) ToBase64() string {
  data, err := json.Marshal(&r)
  if err != nil {
    panic(err)
  }

  return base64.StdEncoding.Strict().EncodeToString(data)
}

func (r request) Signature(key *rsa.PrivateKey) string {
  data, err := json.Marshal(&r)
  if err != nil {
    panic(err)
  }

  hashed := sha256.Sum256(data)

  signature, err := rsa.SignPKCS1v15(nil, key, crypto.SHA256, hashed[:])
  if err != nil {
    panic(err)
  }

  return base64.StdEncoding.Strict().EncodeToString(signature)
}

Go 中的测试顺利通过,但在 ruby 代码片段中使用相同的密钥、消息和签名进行验证总是返回 false。我已经检查了两个应用程序上的公钥是否匹配,我已经使用了严格的、原始的、base64 编码的 url 选项。我还尝试在 Ruby 中签署请求并在 Go 中验证,结果相同。我已经没有主意了。

ruby go rsa
1个回答
0
投票

在你的Go代码中,你正在对json有效负载进行签名之前你对它进行base 64编码,但看起来你正在尝试在Ruby端验证base 64编码有效负载上的签名。

假设 Ruby 代码是您想要保留的内容,您需要确保签署的是 Base 64 编码的有效负载,而不是原始 json。

您的代码还会重复 json 编组,因此最简单的解决方案可能是将

Signature
ToBase64
合并为:

func (r request) EncodeAndSign(key *rsa.PrivateKey) (string, string) {

    data, err := json.Marshal(&r)
    if err != nil {
        panic(err)
    }

    // Base 64 encode the data before signing.
    data_b64 := base64.StdEncoding.Strict().EncodeToString(data)

    hashed := sha256.Sum256([]byte(data_b64))

    signature, err := rsa.SignPKCS1v15(nil, key, crypto.SHA256, hashed[:])

    if err != nil {
        panic(err)
    }

    signature_b64 := base64.StdEncoding.Strict().EncodeToString(signature)

    // Return both the base 64 encoded json data and the base 64 encoded
    // signature.
    return data_b64, signature_b64
}

那就改变吧

encoded := rq.ToBase64()
signature := rq.Signature(key)

encoded, signature := rq.EncodeAndSign(key)

这应该会给你在 Ruby 端验证的值。

© www.soinside.com 2019 - 2024. All rights reserved.