使用安全性转换来验证使用Ruby/OpenSSL创建的RSA签名

Edw*_*oks 8 ruby cocoa licensing openssl rsa

我正在尝试为我的应用程序实现一个简单的许可证密钥方案,而且我遇到了很大的障碍.我在OpenSSL上关注许可证密钥的示例.

由于该博客文章是在2004年编写的,而OpenSSL已在OS X上弃用,我试图使用安全转换API来完成许可证密钥验证而不是OpenSSL.但是,我正在使用OpenSSL生成私钥和公钥; 许可证密钥是由Ruby Web应用程序使用私钥生成的,使用来自购买者电子邮件地址的SHA-256摘要的Ruby OpenSSL包装程序库.

问题是,我所做的任何事情似乎都不会使用OpenSSL从Ruby生成签名,而安全转换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))
Run Code Online (Sandbox Code Playgroud)

以及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);
Run Code Online (Sandbox Code Playgroud)

有谁知道我怎么能做这个工作?我已经花了好几天才到达我现在所处的位置,而且我已经筋疲力尽了我的调试能力,所以如果有任何人有任何见解,我将非常感激!

Kyr*_*kos 11

简而言之,你正在混淆一些关键概念.这是一个关于它如何工作的快速入门.

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

在红宝石方面,你会混淆签名和文件.您需要使用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
Run Code Online (Sandbox Code Playgroud)

围绕这个的ruby文档可以在这里找到.

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

在红宝石方面,你正在使用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); }
Run Code Online (Sandbox Code Playgroud)

对于验证你想要这样的东西,从这里闪闪发光:

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. */
}
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢您提供详尽而有用的答案!因此,总而言之,我确实在混淆签名和消息。一旦我交换了这两个,一切就完美了!并感谢您对我的术语进行更正。如果不清楚,那么我对实现该方案所基于的所有概念没有最坚定的把握。您的定义使事情变得更加清晰:) (2认同)