2012-05-14 6 views
17

मुझे अपने आईफोन ऐप को सिस्टम के साथ एकीकृत करने की आवश्यकता है, और उन्हें किसी दिए गए सार्वजनिक कुंजी द्वारा डेटा एन्क्रिप्ट करने की आवश्यकता है, 3 अलग-अलग प्रारूप में 3 फाइलें हैं .xml .der और। पेम, मैंने शोध किया है और डीईआर/पीईएम से सेककेयरफ प्राप्त करने के बारे में कुछ लेख प्राप्त किए हैं, लेकिन वे हमेशा शून्य वापस आते हैं। नीचे मेरी कोड है:मैं डीईआर/पीईएम फ़ाइल से SecKeyRef कैसे प्राप्त कर सकता हूं

NSString *pkFilePath = [[NSBundle mainBundle] pathForResource:@"PKFile" ofType:@"der"]; 
NSData *pkData = [NSData dataWithContentsOfFile:pkFilePath]; 

SecCertificateRef cert; 
cert = SecCertificateCreateWithData(NULL, (CFDataRef) pkData); 
assert(cert != NULL); 

OSStatus err; 

    if (cert != NULL) { 
     err = SecItemAdd(
         (CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys: 
              (id) kSecClassCertificate, kSecClass, 
              (id) cert,     kSecValueRef, 
              nil 
              ], 
         NULL 
         ); 
     if ((err == errSecSuccess) || (err == errSecDuplicateItem)) { 
      CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **) &cert, 1, NULL); 
      SecPolicyRef policy = SecPolicyCreateBasicX509(); 
      SecTrustRef trust; 
      SecTrustCreateWithCertificates(certs, policy, &trust); 
      SecTrustResultType trustResult; 
      SecTrustEvaluate(trust, &trustResult); 
      if (certs) { 
       CFRelease(certs); 
      } 
      if (trust) { 
       CFRelease(trust); 
      } 
      return SecTrustCopyPublicKey(trust); 
     } 
    } 
return NULL; 

समस्या SecCertificateCreateWithData में क्या होता है, यह हमेशा शून्य लौट भी पढ़ फ़ाइल के माध्यम से ठीक है। किसी ने यह किया है कृपया मेरी मदद करें, धन्यवाद!

संपादित करें: प्रमाणपत्र फ़ाइल MD5 हस्ताक्षर थी।

+2

मुझे लगता है कि आपको यहां अपना उत्तर मिल जाएगा: http://stackoverflow.com/questions/1595013/iphone-how-to-create-a-seckeyref-from-a-public-key-file-pem –

उत्तर

50

मैंने एक ही समस्या के साथ बहुत संघर्ष किया और अंत में एक समाधान मिला। मेरी समस्या यह थी कि मुझे आईओएस ऐप में डेटा एन्क्रिप्ट करने/डिक्रिप्ट करने के लिए बाहरी निजी और सार्वजनिक कुंजी दोनों का उपयोग करने की आवश्यकता थी और वह चाबी का उपयोग नहीं करना चाहता था। यह पता चला है कि आपको महत्वपूर्ण डेटा पढ़ने में सक्षम होने के लिए आईओएस सुरक्षा लाइब्रेरी के लिए एक हस्ताक्षरित प्रमाणपत्र की भी आवश्यकता है और निश्चित रूप से फ़ाइलों को सही प्रारूप में होना चाहिए। प्रक्रिया मूल रूप से निम्नानुसार है:

कहें कि आपके पास पीईएम प्रारूप में एक निजी कुंजी है (----- BEGIN आरएसए निजी कुंजी ----- और ----- अंत आरएसए निजी कुंजी - --- मार्कर): rsaCert.der (सार्वजनिक कुंजी) और rsaPrivate.p12 (निजी कुंजी): rsaPrivate.pem

//Create a certificate signing request with the private key 
openssl req -new -key rsaPrivate.pem -out rsaCertReq.csr 

//Create a self-signed certificate with the private key and signing request 
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey rsaPrivate.pem -out rsaCert.crt 

//Convert the certificate to DER format: the certificate contains the public key 
openssl x509 -outform der -in rsaCert.crt -out rsaCert.der 

//Export the private key and certificate to p12 file 
openssl pkcs12 -export -out rsaPrivate.p12 -inkey rsaPrivate.pem -in rsaCert.crt 

अब आपके पास दो फ़ाइलें जो आईओएस सुरक्षा ढांचे के साथ संगत कर रहे हैं की है। नीचे दिए गए कोड फ़ाइल संभालने सार्वजनिक कुंजी में पढ़ता अपने बंडल में जोड़ा जाता है:

- (SecKeyRef)getPublicKeyRef { 

    NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"rsaCert" ofType:@"der"]; 
    NSData *certData = [NSData dataWithContentsOfFile:resourcePath]; 
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)certData); 
    SecKeyRef key = NULL; 
    SecTrustRef trust = NULL; 
    SecPolicyRef policy = NULL; 

    if (cert != NULL) { 
     policy = SecPolicyCreateBasicX509(); 
     if (policy) { 
      if (SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust) == noErr) { 
       SecTrustResultType result; 
       OSStatus res = SecTrustEvaluate(trust, &result); 

       //Check the result of the trust evaluation rather than the result of the API invocation. 
       if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) { 
        key = SecTrustCopyPublicKey(trust); 
       } 
      } 
     } 
    } 
    if (policy) CFRelease(policy); 
    if (trust) CFRelease(trust); 
    if (cert) CFRelease(cert); 
    return key; 
} 

निजी कुंजी उपयोग में पढ़ने के लिए निम्न कोड:

SecKeyRef getPrivateKeyRef() { 
    NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"rsaPrivate" ofType:@"p12"]; 
    NSData *p12Data = [NSData dataWithContentsOfFile:resourcePath]; 

    NSMutableDictionary * options = [[NSMutableDictionary alloc] init]; 

    SecKeyRef privateKeyRef = NULL; 

    //change to the actual password you used here 
    [options setObject:@"password_for_the_key" forKey:(id)kSecImportExportPassphrase]; 

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); 

    OSStatus securityError = SecPKCS12Import((CFDataRef) p12Data, 
              (CFDictionaryRef)options, &items); 

    if (securityError == noErr && CFArrayGetCount(items) > 0) { 
     CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0); 
     SecIdentityRef identityApp = 
     (SecIdentityRef)CFDictionaryGetValue(identityDict, 
              kSecImportItemIdentity); 

     securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef); 
     if (securityError != noErr) { 
      privateKeyRef = NULL; 
     } 
    } 
    [options release]; 
    CFRelease(items); 
    return privateKeyRef; 
} 
+2

धन्यवाद! मेरा दिन बचाओ! – mientus

+0

धन्यवाद, धन्यवाद, धन्यवाद !!! तुम भगवान हो! –

+0

पहला समाधान मैंने पाया कि चाबी का उपयोग किये बिना इसे करने का प्रबंधन करता है। अति उत्कृष्ट। – fishinear

3

आईओएस 10 के साथ शुरू, यह पीईएम निजी कुंजी आयात करने के लिए वास्तव में संभव है w/o उन्हें पीकेसीएस # 12 (जो क्रिप्टोग्राफी से संबंधित सब कुछ के लिए एक बहुत ही सार्वभौमिक कंटेनर प्रारूप है) और इस प्रकार ओपनएसएसएल का उपयोग कमांड लाइन पर या इसके साथ स्थिर रूप से लिंक करने वाले ऐप्स । मैकोज़ पर यहां उल्लेख किए गए लोगों की तुलना में एक अलग फ़ंक्शन का उपयोग करके 10.7 के बाद भी संभव है (लेकिन अब तक यह आईओएस के लिए मौजूद नहीं है)। वास्तव में नीचे वर्णित तरीका मैकोज़ 10.12 और बाद में भी काम करेगा।

एक प्रमाण पत्र आयात करने के लिए, यह सिर्फ,

-----BEGIN CERTIFICATE----- 

और

-----END CERTIFICATE----- 

लाइनों पट्टी तो डेटा छोड़ दिया से अधिक बेस 64 डिकोडिंग चलाने के लिए पर्याप्त है, परिणाम मानक डीईआर प्रारूप में एक प्रमाणपत्र , जिसे SecCertificateRef प्राप्त करने के लिए केवल SecCertificateCreateWithData() पर खिलाया जा सकता है। आईओएस 10 से पहले यह हमेशा काम कर रहा है।

एक निजी कुंजी आयात करने के लिए, थोड़ा अतिरिक्त काम की आवश्यकता हो सकती है। यदि निजी कुंजी

-----BEGIN RSA PRIVATE KEY----- 

के साथ लपेटा गया है तो यह बहुत आसान है। दोबारा, पहली और आखिरी पंक्ति को छीनने की जरूरत है, शेष डेटा को बेस 64 डीकोड किया जाना चाहिए और परिणाम पीकेसीएस # 1 प्रारूप में आरएसए कुंजी है।यह प्रारूप केवल आरएसए कुंजी रख सकता है और यह सीधे पठनीय है, SecKeyRef प्राप्त करने के लिए केवल डीकोड किए गए डेटा को SecKeyCreateWithData() में फ़ीड करें। तो कुंजी में बिट्स की संख्या (उदाहरण के लिए 1024, 2048, आदि) के साथ CFNumberRef:

  • kSecAttrKeyType: kSecAttrKeyTypeRSA
  • kSecAttrKeyClass: kSecAttrKeyClassPrivate
  • kSecAttrKeySizeInBitsattributes शब्दकोश सिर्फ निम्नलिखित कुंजी/मान जोड़े की जरूरत है यदि ज्ञात नहीं है, तो यह जानकारी वास्तव में कच्चे कुंजी डेटा से पढ़ी जा सकती है, जो एएसएन .1 डेटा है (यह इस उत्तर के दायरे से थोड़ा सा है, लेकिन मैं उस प्रारूप को कैसे पार्स करने के बारे में नीचे कुछ सहायक लिंक प्रदान करूंगा)। यह मान शायद वैकल्पिक है! मेरे परीक्षणों में यह मूल्य निर्धारित करने के लिए वास्तव में आवश्यक नहीं था; अगर अनुपस्थित है, तो एपीआई ने अपने मूल्य को निर्धारित किया है और इसे हमेशा बाद में सही ढंग से सेट किया गया था।

मामले में निजी कुंजी -----BEGIN PRIVATE KEY----- से लपेटा जाता है, तो बेस 64 एन्कोडेड डेटा PKCS # 1 स्वरूप में नहीं है लेकिन PKCS # 8 प्रारूप, तथापि, यह एक बस एक अधिक सामान्य कंटेनर है वह भी गैर RSA कुंजियों पकड़ सकते हैं, लेकिन आरएसए चाबी के लिए कि कंटेनर के भीतरी डेटा, PKCS # 1 के बराबर है तो एक आरएसए चाबी के लिए कह सकते हैं PKCS # 8 है PKCS # 1 एक अतिरिक्त हेडर के साथ और आपको बस इतना करना है कि वह अतिरिक्त हेडर अलग कर रहा है। बस बेस 64 डीकोडेड डेटा के पहले 26 बाइट्स को पट्टी करें और आपके पास PKCS # 1 फिर से है। हाँ, यह वास्तव में इतना आसान है।

पीईएम एन्कोडिंग में पीकेसीएस # एक्स प्रारूपों के बारे में अधिक जानने के लिए, have a look at this site। ASN.1 प्रारूप, here's a good site for that के बारे में अधिक जानने के लिए। और यदि आपको विभिन्न प्रारूपों के साथ खेलने के लिए एक सरल, अभी तक शक्तिशाली और इंटरैक्टिव ऑनलाइन ASN.1 पार्सर की आवश्यकता है, जो कि पीईएम डेटा को सीधे पढ़ सकता है, साथ ही आधार 64 और हेक्सडंप में ASN.1, try this site

बहुत महत्वपूर्ण: जब कीचेन पर एक निजी कुंजी जोड़ना जो आप के रूप में ऊपर बनाया, ध्यान रखें कि इस तरह के एक निजी कुंजी एक सार्वजनिक कुंजी हैश शामिल नहीं है, अभी तक एक सार्वजनिक कुंजी हैश महत्वपूर्ण है के लिए वे चाबी का गुच्छा कृपया एक कुंजी बनाने के लिए एपीआई (SecIdentityRef), सार्वजनिक कुंजी हैश का उपयोग करने के रूप में एपीआई को एक आयातित प्रमाणपत्र से संबंधित सही निजी कुंजी मिलती है (SecIdentityRef एक निजी कुंजी के SecKeyRef और संयुक्त रूप से बनाए गए प्रमाण के SecCertificateRef है ऑब्जेक्ट और यह सार्वजनिक कुंजी हैश है, जो उन्हें एक साथ बांधता है)। तो जब आप कीचेन में निजी कुंजी जोड़ने की योजना बनाते हैं, तो सार्वजनिक कुंजी हैश मैन्युअल रूप से सेट करना सुनिश्चित करें, अन्यथा आप इसके लिए कभी भी पहचान प्राप्त नहीं कर पाएंगे और इसके बिना आप साइनइन या डिक्रिप्टिंग जैसे कार्यों के लिए कीचेन एपीआई का उपयोग नहीं कर सकते डेटा। सार्वजनिक कुंजी हैश को kSecAttrApplicationLabel नामक एक विशेषता में संग्रहीत किया जाना चाहिए (बेवकूफ नाम, मुझे पता है, लेकिन यह वास्तव में एक लेबल नहीं है और उपयोगकर्ता कभी भी देख सकता है, दस्तावेज़ीकरण की जांच करें)। उदा।

OSStatus error = SecItemAdd(
    (__bridge CFDictionaryRef)@{ 
     (__bridge NSString *)kSecClass: 
      (__bridge NSString *)kSecClassKey, 
     (__bridge NSString *)kSecAttrApplicationLabel: 
      hashOfPublicKey, // hashOfPublicKey is NSData * 
#if TARGET_OS_IPHONE 
     (__bridge NSString *)kSecValueRef: 
      (__bridge id)privateKeyToAdd, // privateKeyToAdd is SecKeyRef 
#else 
     (__bridge NSString *)kSecUseItemList: 
       @[(__bridge id)privateKeyToAdd], // privateKeyToAdd is SecKeyRef 
#endif 
    }, 
    &outReference // Can also be NULL, 
        // otherwise reference to added keychain entry 
        // that must be released with CFRelease() 
); 
+0

अलग करने के अलावा ----- BEGIN और ----- END लाइनों कोलाइनलाइन डिकोडिंग से पहले नएलाइन वर्णों को भी अलग किया जाना चाहिए। – mbonness

+0

@mbonness प्रत्येक मानक अनुरूप आधार 64 डीकोडर मानक द्वारा आवश्यक न्यूलाइन को अनदेखा करने में सक्षम होना चाहिए, अन्यथा यह अपने स्वयं के एन्कोडिंग आउटपुट को भी डीकोड नहीं कर सकता है जिसमें अक्सर न्यूलाइन होती है। 'InitWithBase64EncodedString का उपयोग करते समय: विकल्प: 'बस' NSDataBase64DecodingIgnore अज्ञात Characters 'विकल्प का उपयोग करें। अधिकांश तृतीय पक्ष बेस 64 डिकोडर्स डिफ़ॉल्ट रूप से न्यूलाइन को अनदेखा करते हैं और इसे केवल एक विकल्प से बदला जा सकता है। – Mecki

2

इस पोस्ट की सहायता से ऑनलाइन शोध करने के प्रयासों के घंटों के बाद, मैं अंततः इसे पूरी तरह से काम कर रहा हूं। सबसे वर्तमान संस्करण के स्विफ्ट कोड के साथ नोट्स यहां दिए गए हैं। मुझे उम्मीद है कि यह किसी की मदद कर सकता है!

// remove the header string 
let offset = ("-----BEGIN CERTIFICATE-----").characters.count 
let index = certStr.index(cerStr.startIndex, offsetBy: offset+1) 
cerStr = cerStr.substring(from: index) 

// remove the tail string 
let tailWord = "-----END CERTIFICATE-----" 
if let lowerBound = cerStr.range(of: tailWord)?.lowerBound { 
cerStr = cerStr.substring(to: lowerBound) 
} 
रूप

-----BEGIN CERTIFICATE----- 
-----END CERTIFICATE----- 
  • शीर्षक और पूंछ बाहर पट्टी, इस तरह:

    1. बेस 64 इनकोडिंग स्ट्रिंग में एक प्रमाण पत्र इस (PEM प्रारूप) जैसे शीर्षक और पूंछ के बीच sandwiched प्राप्त

    2. एनएसडीटा को बेस 64 स्ट्रिंग डीकोड:

      let data = NSData(base64Encoded: cerStr, 
          options:NSData.Base64DecodingOptions.ignoreUnknownCharacters)! 
      
    3. SecCertificate को NSdata प्रारूप से कन्वर्ट:

      let cert = SecCertificateCreateWithData(kCFAllocatorDefault, data) 
      
    4. अब

      , इस प्रमाणपत्र तुलना करने के लिए इस्तेमाल किया जा सकता प्रमाण पत्र के साथ urlSession ट्रस्ट की ओर से प्राप्त किया:

      certificateFromUrl = SecTrustGetCertificateAtIndex(...) 
      if cert == certificate { 
      } 
      
  • संबंधित मुद्दे