2012-10-28 6 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; 
} 

아래의 코드에 서명하는 데 사용됩니다. 코드의 일부는 내가 공개 키를 가져 와서 확인하고 실패 할 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

안녕하세요, 제발 어떻게 해야할지 자세히 설명해 주실 수 있습니까? 또한 개인 키를 저장하고 검색하는 방식에 문제가 있는지 확인하십시오. 제가 사용할 수있는 다른 라이브러리가 있다면 알려주십시오. –

+0

죄송합니다, 저는 전문가가 아닙니다. 이제는 암호화에 대해 꽤 많이 할 것이므로, 나는 당신에게 몇 가지 일반적인 권고안을 줄 것이라고 생각했다. 예 : 'kSecPaddingPKCS1'는 해시를 지정하지 않고 SHA-1이 기본값이 될 것입니다. 개인 키와 공개 키의 모듈러스가 일치하지 않으면 동일한 키 쌍에 속하지 않습니다. 두 경우 모두 서명 확인이 실패합니다. –

답변

1

아마도 키 체인에 모든 키를 저장하는 대신 키 체인에 간단한 문자열 (secret_hash)을 저장할 수 있습니다. 또한 널리 채택 된 AFNetworking 라이브러리를 사용하여 백엔드 웹 서비스에 대한 보안 호출을 수행하십시오.

개인 키를 사용하여 백엔드 서비스에 대한 요청을 서명해야하는 경우 (a) 서비스 호출 (AFNetworking)에 강력한 랩퍼 라이브러리를 사용하고 (b) 개인 키를 다음과 같이 저장하는 것이 좋습니다. (프로젝트 루트에 .pfx 파일을 유지하고 싶습니다.)

AFHTTPClient의 고유 한 하위 클래스를 만들고 하위 클래스를 사용하여 챌린지 블록을 AFHTTPRequestOperations로 설정합니다 .pfx에서 추출한 자격 증명을 사용하십시오.

이렇게하면 AFHTTPRequestOperation을 직접 만드는 대신 AFHTTPClient 하위 클래스의 MySignedAFHTTPRequestOperation 메서드를 사용하여 만듭니다. 이 방법을 사용하면 만들면

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

... 여기

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

는 위에서 사용 된 신원 추출 도우미 기능에 대한 좀 더 세부입니다 ... AFHTTPRequestOperation를 만든 다음이 같은 도전 블록을 설정해야합니다 AFHTTPRequest (즉, AFHTTPClient 서브 클래스의 MySignedAFHTTPRequestOperation 메소드를 통해), 실행을 위해 NSOperationsQueue에 추가하십시오 (물론 작업을 생성 할 때 성공 및 실패 핸들러 블록을 적절히 설정해야합니다)

  • 이 웹 서비스는
  • 스토어 응용 프로그램에서 .pfx 파일로 인증서 개인 키를 호출하기 위해 강력한 AFNetworking 프레임 워크를 사용하고
  • 이 AFNetworking 수 있도록 자격 증명 및 키를 생성하는 데 사용 요약생성하는 각 작업 개체 내에서 자격 증명을 설정하여 암호화를 수행 할 수 있습니다.

희망이 도움이됩니다.

+0

비밀 번호없이 일반 파일에 개인 키를 저장하는 것은 좋지 않은 생각입니다. 그것을 (모바일) 응용 프로그램으로 파일로 배포 할 것을 제안하면 속임수를 썼던 보안 목적을 무력화시키는 것 같습니다. KeyChain은 민감한 데이터를 저장하기위한 용도로 사용되며 사용해야합니다. –

+0

포인트가 찍혔습니다. 내 솔루션은 백엔드 서비스의 호출에 초점을 맞추고 해당 프레임 워크에 암호화 기능을 적용합니다. (AFNetworking) 키 자체가 아니라 키 체인의 키에 대한 암호를 저장하여 우려를 해결하기 위해 위의 솔루션을 업데이트했습니다 (특정 호출은 – CoolDocMan

+0

키 체인은 키와 같은 중요한 정보를 저장하는 데 더 좋은 장소이며, 제안 사항이 포스터를 어떻게 도울 지에 대해서는 명확하지 않습니다. 질문은 HTTP 요청을 만드는 것이 아니라 서명을 생성하는 메커니즘에 관한 것입니다. –

3

에 대한 허용 대답의 마지막 방법을 살펴 보자 Converting NSData to SecKeyRef

문제는 아이폰 OS가 약간 다르게 공개 키와 개인 키를 처리하는 곳 일반적으로 존재 및 기타 보안 API에서 예상되는 식별 헤더 (의 예를 들어, 자바)는 iOS에서 예상되지 않습니다. 그래서 당신은 그들을 제거해야합니다.

0

표준 PHP 호환 base64 문자열 유형 형식의 AES-256 RSA PEM을 시스템 키 체인에 저장하려면이 기능이 필요합니다.

장소가 많기 때문에 여기에 게시하고 있습니다. POM 파일을 iOS에 직접 꽂을 수는 없다는 것을 알았습니다. 그렇지 않으면 헤더에서 일부 헤더를 제거해야합니다. 그러나 적어도 iOS 9.3에서는 이제이 기능이 작동합니다.이 기능을 내가 본다면 어쩌면 많은 시간을 절약 할 수있었습니다. (참고 : Objective-C-RSAhttps://github.com/ideawu/Objective-C-RSA에서 상당 부분을 수정 한 버전이며 적용 가능한 라이센스를 참조하십시오. 여기에 Swift 버전이 있습니다 : 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; 
} 
관련 문제