【问题标题】:Using Security Transforms to verify an RSA signature created with Ruby/OpenSSL使用安全转换验证使用 Ruby/OpenSSL 创建的 RSA 签名
【发布时间】:2015-04-27 05:08:28
【问题描述】:

我正在尝试为我的应用实施一个简单的许可证密钥方案,但遇到了重大障碍。我正在关注OpenSSL for License Keys 的示例。

自从那篇博文写于 2004 年并且 OpenSSL 在 OS X 上已被弃用以来,我正在尝试使用安全转换 API 来完成许可证密钥验证,而不是 OpenSSL。但是,我正在使用 OpenSSL 生成私钥和公钥;许可证密钥由 Ruby Web 应用使用 Ruby OpenSSL 包装库从购买者电子邮件地址的 SHA-256 摘要中的私钥生成。

问题是我所做的任何事情似乎都没有使用 OpenSSL 从 Ruby 生成签名,Security Transforms API 将验证该签名。

我正在处理的 Ruby 代码是:

require('openssl')

# The email address used as the content of the license key.
license = 'test@example.com'

# Generate the public/private keypair.
`openssl genrsa -out private_key.pem 2048`
`openssl rsa -in conductor.pem -out public_key.data -pubout`

# Get the private key and a hash of the license.
private_key = OpenSSL::PKey::RSA.new(File.read('private_key.pem'))
signature   = OpenSSL::Digest::SHA256.digest(license)

# The signature passed to SecVerifyTransformCreate in the OS X app. I'm not sure which of these SecVerifyTransformCreate is expecting (the binary digest, a hex representation of the digest, or the original un-digested content), but none of them work.
signature_out = signature
#signature_out = OpenSSL::Digest::SHA256.hexdigest(license)
#signature_out = license

File.write('signature.data', signature_out)

# Sign the email address to generate the license key. Using the OpenSSL::PKey::PKey#sign method produces a license key that can only be verified on the command line by running:
#
#   echo -n test@example.com | openssl dgst -sha256 -sign test.pem
#
# while using the #private_encrypt method produces a key that can only be verified on the command line by running:
#
#   echo -n test@example.com | openssl dgst -sha256 -binary | openssl rsautl -sign -inkey test.pem
#
# I'm not sure what the exact difference between the two commands above is and why they correspond to the two different Ruby signing methods below. Neither approach produces something that SecVerifyTransformCreate will verify, however.
File.write('license_key.data',
           private_key.sign(OpenSSL::Digest::SHA256.new, license))
#           private_key.private_encrypt(signature))

以及Objective-C中对应的验证码:

// Get the data.
NSData *publicKeyData  = [NSData dataWithContentsOfFile:@"public_key.data"];
NSData *signatureData  = [NSData dataWithContentsOfFile:@"signature.data"];
NSData *licenseKeyData = [NSData dataWithContentsOfFile:@"license_key.data"];

// Import the public key.
SecItemImportExportKeyParameters keyParameters = {};
SecExternalFormat format = kSecFormatOpenSSL;
SecExternalItemType type = kSecItemTypePublicKey;
CFArrayRef publicKeys;

SecItemImport((__bridge CFDataRef)publicKeyData,
              NULL,
              &format,
              &type,
              0,
              &keyParameters,
              NULL,
              &publicKeys);

NSArray *publicKeysArray = (__bridge_transfer NSArray *)publicKeys;
SecKeyRef publicKey = (__bridge SecKeyRef)publicKeysArray[0]; // TODO: How do we need to bridge this return value?

CFErrorRef error = NULL;

SecTransformRef verifier = SecVerifyTransformCreate(publicKey, (__bridge CFDataRef)signatureData, &error);

SecTransformSetAttribute(verifier, kSecTransformDebugAttributeName, kCFBooleanTrue, &error);
SecTransformSetAttribute(verifier, kSecTransformInputAttributeName, (__bridge CFDataRef)licenseKeyData, &error);
SecTransformSetAttribute(verifier, kSecDigestTypeAttribute, kSecDigestSHA2, &error);
SecTransformSetAttribute(verifier, kSecDigestLengthAttribute, (__bridge CFNumberRef)@256, &error);

// I'm not sure if one of these transform attributes is necessary, but neither of them produces a verified result anyways.
//  SecTransformSetAttribute(verifier, kSecInputIsAttributeName, kSecInputIsDigest, &error);
//  SecTransformSetAttribute(verifier, kSecInputIsAttributeName, kSecInputIsRaw, &error);

NSNumber *result = (__bridge NSNumber *)SecTransformExecute(verifier, &error);

NSLog(@"Result: %@", result);

有谁知道我怎样才能做到这一点?我确实花了几天时间才达到现在的水平,并且已经用尽了我的能力来进一步调试它,因此,如果有人有任何见解,将不胜感激!

【问题讨论】:

  • “自从那篇博文写于 2004 年并且 OpenSSL 在 OS X 上已被弃用,我正在尝试使用安全转换 API 来完成许可证密钥验证,而不是 OpenSSL。” - 另一种选择是在 OS X 上构建 OpenSSL 1.0.2 版本并使用它。有关在 OS X 上配置和构建的信息,请参阅 OpenSSL wiki 上的 Compilation and Installation
  • 感谢您的意见!如果我知道我现在所知道的并重新做一遍,我肯定会链接到 OpenSSL。但是,加密服务指南强烈建议您使用安全转换 API,我试图避免将另一个静态库链接到我的应用程序。在这一点上,由于我的代码大部分已经用 Security Transforms 编写了,我很想弄清楚缺少的部分是什么,而不是陷入切换到 OpenSSL 的兔子洞。
  • “但是,加密服务指南强烈建议您使用安全转换 API...” - 好吧,请考虑... Apple 并未修复所有安全问题错误,但 OpenSSL 有。例如,Apple 的某些版本的 Secure Transport 仍然存在ECDHE-ECDSA 错误。而CVE-2015-1130 (Hidden Backdoor with Root) 仅在最新操作系统的一个小版本中得到修复。据我所知,OpenSSL 修复了他们所有的错误(有时有很多错误)。

标签: ruby cocoa openssl licensing rsa


【解决方案1】:

简而言之,您混淆了一些关键概念。这是有关其工作原理的快速入门。

  1. 文档(您的许可证数据/电子邮件)使用 digest (SHA256) 进行哈希处理
  2. 私钥加密散列。这是二进制签名
  3. 二进制签名需要编码成便于传输的格式,通常是base64或类似的文本。
  4. 编码后的签名document一起传递给验证方(您的objc应用程序)进行验证
  5. 文档(您的许可证)再次使用相同的 digest (SHA256) 进行哈希处理
  6. 编码的签名被解码回二进制
  7. 公钥解密签名,揭示原始哈希
  8. 将此解密的哈希与计算的哈希进行比较,如果它们匹配 文档 进行验证。

在 ruby​​ 方面,您混淆了签名和文档。您需要使用 SHA256 对许可证进行哈希处理,然后使用私钥对其进行加密以生成签名。您只是将文档的哈希值保存为签名。在红宝石方面试试这个:

require 'openssl'
require 'base64'

license = 'test@example.com'
private_key = OpenSSL::PKey::RSA.new(File.read('private_key.pem'))
digest  = OpenSSL::Digest::SHA256.new
signature = private_key.sign digest, license
signature_out = Base64.encode64(signature)

File.write('signature.data', signature_out)
File.write('license_key.data', license) # no hash, no signing

可以在 here 找到与此相关的 ruby​​ 文档。

我对您在 Objective-C 方面使用的库不是很熟悉,但这里的技巧是确保您在两端使用相同的 Digest 算法进行散列(SHA256 ),检查相同的加密算法 (RSA) 和公钥和私钥是否兼容(匹配 RSA 模数和公共指数),以及 二进制 的相同编码> 来回传递的签名数据(base64hex等)

在 ruby​​ 方面,您正在使用 SHA256 生成签名,而在 Objective-c 上,您似乎正在使用 SHA-2 大小 256 进行验证,因此看起来没问题。

解码签名(如果你是用 ruby​​ 编写二进制文件,你可以跳过这个)

SecTransformRef decoder = SecDecodeTransformCreate(kSecBase64Encoding, &error);
if (error) { CFShow(error); exit(-1); }

SecTransformSetAttribute(decoder, 
                         kSecTransformInputAttributeName,
                         signatureData, 
                         &error);
if (error) { CFShow(error); exit(-1); }

CFDataRef signature = SecTransformExecute(decoder, &error);
if (error) { CFShow(error); exit(-1); }

你想要这样的验证,来自here

verifier = SecVerifyTransformCreate(publicKey, signature, &error);
if (error) { CFShow(error); exit(-1); } // show your errors!

SecTransformSetAttribute(verifier,
                         kSecTransformInputAttributeName,
                         cfLicense,  // Converted from NSData
                         &error);
if (error) { CFShow(error); exit(-1); }

SecTransformSetAttribute(verifier, 
                         kSecDigestTypeAttribute, 
                         kSecDigestSHA2, 
                         &error);
if (error) { CFShow(error); exit(-1); }

SecTransformSetAttribute(verifier, 
                         kSecDigestLengthAttribute, 
                         (__bridge CFNumberRef)@256, 
                         &error);
if (error) { CFShow(error); exit(-1); }

result = SecTransformExecute(verifier, &error);
if (error) { CFShow(error); exit(-1); }

if (result == kCFBooleanTrue) {
  /* Signature was valid. */
} else {
  /* Signature was invalid. */
}

【讨论】:

  • 非常感谢您提供详细且非常有帮助的答案!所以,总而言之,我确实混淆了签名和信息。一旦我交换了这两个,一切都很完美!感谢您在术语上纠正我。如果不清楚,我对实施该计划的所有基本概念没有最牢固的把握。你的定义让我更清楚了:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
相关资源
最近更新 更多