2012-10-28 27 views
26

में अपेक्षित के रूप में काम नहीं कर रहा है मुझे इसे बैकएंड सर्वर पर भेजने से पहले अनुरोधों पर हस्ताक्षर करने की आवश्यकता है। हालांकि निजी कुंजी मुझे दी गई है। इसलिए मुझे इसे आयात करने की आवश्यकता है और फिर इसे साइन करने के लिए उपयोग करें। मैं आयात और साइन इन करने में सक्षम हूं लेकिन यह डेटा openssl का उपयोग करके हस्ताक्षर करके मुझे जो मिलता है उससे अलग है। मुझे पता है कि यह गलत कर रहा है क्योंकि जब मैं सार्वजनिक कुंजी आयात करता हूं, तो मैं इसे भी सत्यापित नहीं कर सकता। यदि कोई तरीका है तो मैं कीचेन पर आयात करने से बच सकता हूं, यह भी बहुत अच्छा होगा। कुछ दिनों के लिए इस पर कड़ी मेहनत कर रहे हैं और यह हमारे लिए एक उच्च प्राइ काम है। क्या कोई मदद कर सकता है। कीचेन में निजी कुंजी आयात करना आईफोन

- (SecKeyRef) getPrivateKey { 
//RSA KEY BELOW IS DUMMY. 

key = @"-----BEGIN RSA PRIVATE KEY-----\nORtMei3ImKI2ZKI636I4+uNCwFfZv9pyJzXyfr1ZNo7iaiW7A0NjLxikNxrWpr/M\n6HD8B2j/CSjRPW3bhsgDXAx/AI1aSfJFxazjiTxx2Lk2Ke3jbhE=\n-----END RSA PRIVATE KEY-----\n"; 

NSString * tag = @"adpPrivateKey"; 

    NSString *s_key = [NSString string]; 
    NSArray *a_key = [key componentsSeparatedByString:@"\n"]; 
    BOOL  f_key = FALSE; 

    for (NSString *a_line in a_key) { 
     if ([a_line isEqualToString:@"-----BEGIN RSA PRIVATE KEY-----"]) { 
      f_key = TRUE; 
     } 
     else if ([a_line isEqualToString:@"-----END RSA PRIVATE KEY-----"]) { 
      f_key = FALSE; 
     } 
     else if (f_key) { 
      s_key = [s_key stringByAppendingString:a_line]; 
     } 
    } 
    if (s_key.length == 0) return(nil); 

    // This will be base64 encoded, decode it. 
    NSData *d_key = [NSData dataFromBase64String:s_key]; 

if(d_key == nil) return nil; 

    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]]; 

    // Delete any old lingering key with the same tag 
    NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init]; 
    [privateKey setObject:(id) kSecClassKey forKey:(id)kSecClass]; 
    [privateKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; 
    [privateKey setObject:d_tag forKey:(id)kSecAttrApplicationTag]; 
    SecItemDelete((CFDictionaryRef)privateKey); 

    CFTypeRef persistKey = nil; 

    // Add persistent version of the key to system keychain 
    [privateKey setObject:d_key forKey:(id)kSecValueData]; 
    [privateKey setObject:(id) kSecAttrKeyClassPrivate forKey:(id) 
    kSecAttrKeyClass]; 
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(id) 
    kSecReturnPersistentRef]; 

    OSStatus secStatus = SecItemAdd((CFDictionaryRef)privateKey, &persistKey); 
    if (persistKey != nil) CFRelease(persistKey); 

    if ((secStatus != noErr) && (secStatus != errSecDuplicateItem)) { 
     [privateKey release]; 
     return(nil); 
    } 

    // Now fetch the SecKeyRef version of the key 
    SecKeyRef keyRef = nil; 

    [privateKey removeObjectForKey:(id)kSecValueData]; 
    [privateKey removeObjectForKey:(id)kSecReturnPersistentRef]; 
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef 
    ]; 
    [privateKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; 
    secStatus = SecItemCopyMatching((CFDictionaryRef)privateKey, 
            (CFTypeRef *)&keyRef); 

    if(secStatus != noErr) 
     return nil; 

    [privateKey release]; 

    return keyRef; 
} 

नीचे कोड पर हस्ताक्षर करने के लिए किया जाता है। कोड के भाग I सार्वजनिक कुंजी आयात और सत्यापित करने और अपने असफल करने के लिए http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/ में कोड उदाहरण का इस्तेमाल किया एप्पल उदाहरण (http://developer.apple.com/library/ios/#samplecode/CryptoExercise/Listings/Classes_SecKeyWrapper_m.html#//apple_ref/doc/uid/DTS40008019-Classes_SecKeyWrapper_m-DontLinkElementID_17)

- (NSData *)getSignatureBytes:(NSString *)plainText { 

OSStatus sanityCheck = noErr; 
NSData * signedHash = nil; 
uint8_t * signedHashBytes = NULL; 
size_t signedHashBytesSize = 0; 
SecKeyRef privateKey = NULL; 

privateKey = [self getPrivateKey]; 

signedHashBytesSize = SecKeyGetBlockSize(privateKey); 

//Create a SHA Encoded 
NSString * shaEncoded = [self sha256:plainText]; 
NSLog(@"%@", shaEncoded); 


// Malloc a buffer to hold signature. 

signedHashBytes = malloc(signedHashBytesSize * sizeof(uint8_t)); 
memset((void *)signedHashBytes, 0x0, signedHashBytesSize); 


NSData *inputData = [self getHashBytes:[plainText dataUsingEncoding:NSUTF8StringEncoding]]; 
int bytesLengthUINT8 = [inputData length]; 

sanityCheck = SecKeyRawSign (privateKey, kSecPaddingPKCS1, (const uint8_t *)inputData, CC_SHA256_DIGEST_LENGTH,(uint8_t *)signedHashBytes, &signedHashBytesSize); 


if(sanityCheck != noErr) 
    return nil; 


signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];  
NSString *string = [signedHash base64EncodedString]; 

NSLog(@"%@", string); 


if (signedHashBytes) free(signedHashBytes); 
return signedHash; 

} 

से है।

+0

'kSecPaddingPKCS1' का मूल्य क्या है? क्या आप सार्वजनिक और निजी कुंजी के मॉडुलि की कोशिश और तुलना कर सकते हैं? –

+0

हाय @owlstead, क्या आप कृपया बता सकते हैं कि क्या करना है? मैं निजी कुंजी को सहेजने और पुनर्प्राप्त करने के तरीके में भी कोई समस्या देखता हूं। अगर मुझे अन्य पुस्तकालय उपलब्ध हैं तो कृपया मुझे बताएं कि मैं उपयोग कर सकता हूं। –

+0

क्षमा करें, मैं एक आईओएस विशेषज्ञ नहीं हूँ। मैं अब क्रिप्टो के बारे में काफी कुछ करता हूं, इसलिए मैंने सोचा कि मैं आपको कुछ सामान्य सिफारिशें देता हूं। जैसे 'kSecPaddingPKCS1' हैश निर्दिष्ट नहीं करता है, और SHA-1 डिफ़ॉल्ट होने की संभावना है। यदि निजी कुंजी और सार्वजनिक कुंजी का मॉड्यूल मेल नहीं खाता है, तो वे एक ही कुंजी जोड़ी से संबंधित नहीं हैं। दोनों मामलों में हस्ताक्षर सत्यापन विफल हो जाएगा। –

उत्तर

1

शायद एक कीचेन में अपनी सभी कुंजी को संग्रहीत करने के बजाय आप केवल मुख्य श्रृंखला (एक secret_hash के रूप में) में एक साधारण वर्ण स्ट्रिंग स्टोर कर सकते हैं। साथ ही, सामान्य, व्यापक रूप से अपनाए गए AFNetworking लाइब्रेरी का उपयोग करके बैकएंड वेब सेवाओं को सुरक्षित कॉल करें।

इसलिए यदि आपको निजी कुंजी का उपयोग करके बैकएंड सेवा में अपने अनुरोधों पर हस्ताक्षर करने की आवश्यकता है, तो मेरा सुझाव है कि आप इसे (ए) सेवा आमंत्रण (AFNetworking) के लिए एक मजबूत रैपर लाइब्रेरी का उपयोग करके और (बी) निजी कुंजी को संग्रहीत करते हुए करें एक स्थान पर एक .pfx फ़ाइल एप्लिकेशन के लिए सुलभ तो AFHTTPClient की अपनी खुद की उपवर्ग बनाने (मैं परियोजना जड़ में .pfx फ़ाइल रखना चाहते हैं।)

और चुनौती ब्लॉक करने के लिए सेट के साथ AFHTTPRequestOperations बनाने के लिए उपवर्ग का उपयोग .pfx से निकाले गए क्रेडेंशियल्स का उपयोग करें।

इस तरह, AFHTTPRequestOperation को सीधे बनाने के बजाय - उन्हें अपने AFHTTP क्लाइंट सबक्लास के MySignedAFHTTPRequestOperation विधि का उपयोग करके बनाएं। इस विधि AFHTTPRequestOperation बनाना होगा और इस तरह चुनौती ब्लॉक सेट ...

[myOperationObject setAuthenticationChallengeBlock:^(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge) 
    { 
      NSString * pfxPath = [[NSBundle mainBundle] 
          pathForResource:@“pvtKeyFile” ofType:@"pfx"]; 

      NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile: pfxPath]; 
      CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;  
      SecIdentityRef identity; 
      SecTrustRef trust; 
      myIdentityAndTrustExtractionHelper(inPKCS12Data, &identity, &trust); 

      SecCertificateRef certificate = NULL; 
      SecIdentityCopyCertificate (identity, &certificate); 

      const void *certs[] = {certificate}; 
      CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL); 

      NSURLCredential *credential = [NSURLCredential 
              credentialWithIdentity:identity 
              certificates:(__bridge NSArray*)certArray 
              persistence:NSURLCredentialPersistencePermanent]; 

      [[challenge sender] useCredential:credential 
       forAuthenticationChallenge:challenge]; 
      CFRelease(certArray); 
    } 

यहां पहचान निष्कर्षण सहायक ऊपर उपयोग समारोह पर कुछ और अधिक विस्तार है ...

OSStatus myIdentityAndTrustExtractionHelper(CFDataRef inPKCS12Data,   
           SecIdentityRef *mySecIdentityRef, 
           SecTrustRef *myTrustRef) 
{ 
    //modify to get secret-hash from keychain 
    CFStringRef mySecretHash = CFSTR(secret_hash); 
    const void *keys[] = { kSecImportExportPassphrase }; 
    const void *values[] = { mySecretHash }; 


    CFArrayRef pkscItems = CFArrayCreate(NULL, 0, 0, NULL); 
    OSStatus mySecurityError = SecPKCS12Import(inPKCS12Data, 
           CFDictionaryCreate(NULL,keys, values, 1, 
           NULL, NULL), 
           &pkscItems); 
    if (mySecurityError == 0) 
    {         
     CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (pkscItems, 0); 
     const void *tempIdentity = NULL; 
     tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, 
              kSecImportItemIdentity); 
     *mySecIdentityRef = (SecIdentityRef)tempIdentity; 
     const void *tempTrust = NULL; 
     tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust); 
     *myTrustRef = (SecTrustRef)tempTrust; 
    } 

    return mySecurityError; 
} 

एक बार जब आप बनाने AFHTTP इस तरह से पूछताछ करें (यानी आपके AFHTTP क्लाइंट सबक्लास के MySignedAFHTTPRequestOperation विधि के माध्यम से), निष्पादन के लिए इसे NSOperationsQueue में जोड़ें (निश्चित रूप से आपको ऑपरेशन बनाने पर सफलतापूर्वक सेट करने और विफल हैंडलर ब्लॉक विफल होने की आवश्यकता है)

सारांश में:

  • अपने वेब सेवा कॉल
  • स्टोर एप्लिकेशन में एक .pfx फ़ाइल के रूप में प्रमाणपत्र निजी कुंजी बनाने के लिए मजबूत AFNetworking ढांचे का उपयोग और इसका इस्तेमाल साख और चाबी
  • AFNetworking अनुमति देते हैं बनाने के लिए आपके द्वारा बनाए गए प्रत्येक ऑपरेशन ऑब्जेक्ट के भीतर प्रमाण-पत्र सेट करके आपके लिए एन्क्रिप्टिंग करने के लिए।

आशा इस मदद करता है।

+0

पासवर्ड के बिना एक सादे फ़ाइल में एक निजी कुंजी को खराब विचार की तरह ध्वनि लगाना। इसे (मोबाइल) एप्लिकेशन के साथ फ़ाइल के रूप में वितरित करने का सुझाव देने के लिए किसी भी सुरक्षा प्रयोजन को हराने के लिए लगता है जिसके लिए इसे धोखा दिया गया था। KeyChain बिल्कुल संवेदनशील डेटा संग्रहीत करने के उद्देश्य से है और इसका उपयोग किया जाना चाहिए। –

+0

प्वाइंट लिया गया। मेरा समाधान बैकएंड सेवाओं के आविष्कार पर और उस ढांचे में एन्क्रिप्शन को फ़िट करने पर अधिक केंद्रित है (AFNetworking) मैंने कुंजी को स्वयं संग्रहित करके अपनी चिंता का समाधान करने के लिए ऊपर दिए गए समाधान को अद्यतन किया है लेकिन कुंजीचैन में कुंजी के लिए पासफ्रेज (विशिष्ट कॉल कीचेन से पासफ्रेज लाने के लिए लाइन में प्रतिस्थापित किया जाना चाहिए जहां मेरा सेक्रेटशैश लाया जाता है। – CoolDocMan

+0

उस परिवर्तन के साथ भी, चाबी की तरह संवेदनशील जानकारी स्टोर करने के लिए कीचेन अभी भी एक बेहतर जगह है। यह भी स्पष्ट नहीं है कि आपके सुझाव पोस्टर की मदद कैसे करते हैं, जिनके सुझाव प्रश्न HTTP अनुरोधों को नहीं बनाने के हस्ताक्षर उत्पन्न करने के यांत्रिकी के बारे में प्रतीत होता है। –

3

के लिए स्वीकार किए जाते हैं जवाब में पिछले विधि पर एक नज़र डालें: Converting NSData to SecKeyRef

समस्या

कि आईओएस सार्वजनिक और निजी कुंजियों संभालती है कुछ अलग ढंग से वह जगह है जहाँ पहचान हैडर (में है कि आमतौर पर मौजूद हैं और अन्य सुरक्षा एपीआई द्वारा की उम्मीद है उदाहरण के लिए जावा, आईओएस में अपेक्षित नहीं है। तो आपको उन्हें बाहर निकालना होगा।

0

यदि आप केवल एक एईएस -256 आरएसए पीईएम स्टोर करना चाहते हैं जो आपको मानक PHP- संगत बेस 64 स्ट्रिंग प्रकार प्रारूप में मिला है, तो सिस्टम कीचेन में, यह मेरे लिए काम करता है।

मैं इसे यहां पोस्ट कर रहा हूं क्योंकि बहुत से स्थान, मैंने पढ़ा है कि आप केवल पीईएम फ़ाइल को सीधे आईओएस में नहीं रख सकते हैं; या फिर आपको कुछ हेडर इसे से पट्टी करना है; इत्यादि। हालांकि, कम से कम आईओएस 9.3 पर, यह अब काम करता है, और अगर मैंने इसे कहीं देखा था तो यह मुझे बहुत समय बचा सकता। (नोट: के उद्देश्य का एक बहुत ही संशोधित संस्करण https://github.com/ideawu/Objective-C-RSA से उद्देश्य-सी-आरएसए लागू करें, लागू लाइसेंस देखें, मुझे कोई समर्थन नहीं है। उनके पास एक स्विफ्ट संस्करण भी है: https://github.com/btnguyen2k/swift-rsautils जो अधिक सुविधा पूर्ण दिखाई देता है और हल करेगा बहुत से लोगों के लिए समस्याएं।)

#define BR (__bridge id) 
#define BRD (__bridge CFDictionaryRef) 

+ (SecKeyRef)storePrivateKey:(NSString *)key inSystemKeychainWithTag:(NSString *)tag { 
    NSRange spos; 
    NSRange epos; 
    spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"]; 
    if(spos.length > 0) { 
     epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"]; 
    } 
    else { 
     spos = [key rangeOfString:@"-----BEGIN PRIVATE KEY-----"]; 
     epos = [key rangeOfString:@"-----END PRIVATE KEY-----"]; 
    } 
    if(spos.location != NSNotFound && epos.location != NSNotFound){ 
     NSUInteger s = spos.location + spos.length; 
     NSUInteger e = epos.location; 
     NSRange range = NSMakeRange(s, e-s); 
     key = [key substringWithRange:range]; 
    } 
    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""]; 
    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""]; 
    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""]; 
    key = [key stringByReplacingOccurrencesOfString:@" " withString:@""]; 

    // This will be base64 encoded, decode it. 
    NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:0]; 

    if(data == nil){ 
     return nil; 
    } 

    //a tag to read/write keychain storage 
    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]]; 

    // Delete any old lingering key with the same tag 
    NSMutableDictionary *options = [ 
    @{ 
     BR kSecClass: BR kSecClassKey, 
     BR kSecAttrKeyType: BR kSecAttrKeyTypeRSA, 
     BR kSecAttrApplicationTag: d_tag, 
    } 
    mutableCopy]; 

    SecItemDelete(BRD options); 

    // Add persistent version of the key to system keychain 
    [options addEntriesFromDictionary: 
    @{ 
     BR kSecValueData:data, 
     BR kSecAttrKeyClass: BR kSecAttrKeyClassPrivate, 
     BR kSecReturnPersistentRef: @YES, 
    }]; 

    CFTypeRef persistKey = nil; 
    OSStatus status = SecItemAdd(BRD options, &persistKey); 
    if (persistKey != nil){ 
     CFRelease(persistKey); 
    } 
    if ((status != noErr) && (status != errSecDuplicateItem)) { 
     return nil; 
    } 

    [options removeObjectForKey:BR kSecValueData]; 
    [options removeObjectForKey:BR kSecReturnPersistentRef]; 

    [options addEntriesFromDictionary: 
    @{ 
     BR kSecReturnRef:@YES, 
     BR kSecAttrKeyType:BR kSecAttrKeyTypeRSA, 
    }]; 

    // Now fetch the SecKeyRef version of the key 
    SecKeyRef keyRef = nil; 
    status = SecItemCopyMatching(BRD options, (CFTypeRef *)&keyRef); 
    if(status != noErr){ 
     return nil; 
    } 
    return keyRef; 
}