2015-04-27 22 views
6

Tôi đang cố triển khai sơ đồ khóa cấp phép đơn giản cho ứng dụng của mình và tôi đang gặp phải những rào cản đáng kể. Tôi đang theo dõi ví dụ tại OpenSSL for License Keys.Sử dụng Chuyển đổi bảo mật để xác minh chữ ký RSA được tạo bằng Ruby/OpenSSL

Vì bài đăng trên blog đó được viết vào năm 2004 và OpenSSL đã không được dùng nữa trên OS X Tôi đang cố sử dụng API chuyển đổi bảo mật để thực hiện xác minh khóa cấp phép thay vì OpenSSL. Tôi đang tạo ra các khóa riêng và công khai với OpenSSL, tuy nhiên; khóa cấp phép được tạo bằng khóa riêng của ứng dụng web Ruby bằng cách sử dụng thư viện trình bao bọc Ruby OpenSSL từ thông báo SHA-256 của địa chỉ email của người mua.

Vấn đề là không có gì tôi làm dường như tạo ra một chữ ký từ Ruby sử dụng OpenSSL rằng Security Transforms API sẽ xác minh.

Các mã Ruby Tôi đang làm việc tắt của là:

require('openssl') 

# The email address used as the content of the license key. 
license = '[email protected]' 

# 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 [email protected] | 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 [email protected] | 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)) 

Và mã xác nhận tương ứng trong 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); 

Có ai biết làm thế nào tôi có thể làm công việc này? Tôi đã dành nhiều ngày để đạt được điểm mà tôi đang ở đây và tôi đã cạn kiệt khả năng của tôi để gỡ lỗi này nữa, vì vậy nếu có ai có bất kỳ cái nhìn sâu sắc nó sẽ được đánh giá rất cao!

+0

* "Kể từ bài đăng blog đó được viết vào năm 2004 và OpenSSL đã không còn được sử dụng trên OS X Tôi đang cố gắng sử dụng API chuyển đổi bảo mật để thực hiện xác minh khóa cấp phép thay vì OpenSSL. "* - Một tùy chọn khác là xây dựng phiên bản OpenSSL 1.0.2 trên OS X và sử dụng nó thay thế. Để cấu hình và xây dựng trên OS X, xem [Biên soạn và cài đặt] (https://wiki.openssl.org/index.php/Compilation_and_Installation#Mac) trên wiki OpenSSL. – jww

+0

Cảm ơn bạn đã nhập! Nếu tôi biết những gì tôi biết bây giờ và đã làm điều này để làm hơn một lần nữa, tôi chắc chắn sẽ chỉ liên kết với OpenSSL. Tuy nhiên, Hướng dẫn dịch vụ mã hóa mạnh mẽ khuyến khích bạn sử dụng API chuyển đổi bảo mật và tôi đã cố gắng tránh liên kết một thư viện tĩnh khác với ứng dụng của mình. Tại thời điểm này kể từ khi tôi phần lớn có mã đã được viết với Security Transforms, tôi rất thích tìm ra những gì các mảnh còn thiếu hơn là đi xuống một lỗ thỏ chuyển sang OpenSSL. –

+0

* "Tuy nhiên, Hướng dẫn dịch vụ mã hóa mạnh mẽ khuyến khích bạn sử dụng API chuyển đổi bảo mật ..." * - Vâng, hãy cân nhắc ... Apple không sửa tất cả các lỗi bảo mật của họ, nhưng OpenSSL thực hiện. Ví dụ: một số phiên bản * Giao thông an toàn * của Apple vẫn có lỗi [ECDHE-ECDSA] (https://wiki.openssl.org/index.php/SSL_OP_SAFARI_ECDHE_ECDSA_BUG). Và [CVE-2015-1130 (Hidden Backdoor with Root)] (http://apple.stackexchange.com/q/180396/83961) chỉ được sửa trong một phiên bản nhỏ của hệ điều hành mới nhất. Theo như tôi biết, OpenSSL sửa tất cả các lỗi của chúng (và có rất nhiều lỗi). – jww

Trả lời

7

Tóm lại, bạn đang trộn lẫn một số khái niệm chính. Đây là một mồi nhanh về cách thức hoạt động của nó.

  1. Một tài liệu (dữ liệu giấy phép của bạn/email) được băm với một tiêu hóa (SHA256)
  2. Private key mã hóa băm. Đây là chữ ký số nhị phân.
  3. Số nhị phân chữ ký cần được mã hóa thành định dạng thuận tiện cho việc vận chuyển, thường là văn bản với base64 hoặc một cái gì đó tương tự.
  4. Các mã hóa chữ ký được truyền qua với tài liệu dự tiệc (ứng dụng objc của bạn) xác minh để xác minh
  5. Document (giấy phép của bạn) được băm một lần nữa với cùng tiêu hóa (SHA256)
  6. Encoded chữ ký được giải mã trở lại thành nhị phân
  7. Khóa công khai giải mã chữ ký cho biết mã băm gốc
  8. Băm được giải mã này được so sánh với giá trị được tính toán, nếu chúng khớp với tài liệu được xác minh.

Trên mặt ruby, bạn đang nhầm lẫn chữ ký và tài liệu. Bạn cần phải băm giấy phép với SHA256 và sau đó mã hóa bằng khóa riêng để tạo chữ ký. Bạn chỉ cần lưu một băm của tài liệu làm chữ ký. Hãy thử điều này trên mặt ruby ​​của sự vật:

require 'openssl' 
require 'base64' 

license = '[email protected]' 
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 

Tài liệu ruby ​​xung quanh này có thể được tìm thấy here.

Tôi không quen thuộc với thư viện bạn đang sử dụng trên mục tiêu C, nhưng mẹo ở đây là đảm bảo bạn đang sử dụng cùng một thuật toán Digest cho băm trên cả hai đầu (SHA256), kiểm tra cùng một thuật toán mã hóa (RSA) và khóa công khai và khóa bí mật tương thích (kết hợp RSA mô đun và số mũ công cộng), và cùng mã hóa cho dữ liệu chữ ký nhị phân thông qua qua lại (base64, hex, v.v ..

Trên mặt ruby ​​bạn đang tạo một chữ ký với SHA256 và trên mục tiêu-c nó trông giống như bạn đang xác minh nó với kích thước SHA-2 256 để trông ok.

Giải mã chữ ký (nếu bạn đang viết nhị phân từ ruby, bạn có thể bỏ qua)

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); } 

Đối với việc xác minh bạn muốn một cái gì đó như thế này, lấp lánh từ 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. */ 
} 
+1

Cảm ơn bạn rất nhiều vì câu trả lời chi tiết và hữu ích của bạn! Vì vậy, dài và ngắn của nó đã thực sự là tôi đã trộn chữ ký và thông điệp. Khi tôi đổi chỗ hai thứ đó, mọi thứ đã hoạt động hoàn hảo! Và cảm ơn bạn đã sửa tôi về thuật ngữ. Nếu không rõ ràng, tôi không nắm vững tất cả các khái niệm cơ bản về những gì đang diễn ra trong việc thực hiện kế hoạch này. Định nghĩa của bạn làm mọi thứ rõ ràng hơn rất nhiều cho tôi :) –

Các vấn đề liên quan