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

以下のコードは署名に使用されます。コードの一部は Apple の例からのものです ( 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;

}

http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/のコード サンプルを使用して公開鍵をインポートし、その失敗を確認しました。

4

3 に答える 3

3

受け入れられた回答の最後の方法を見てください: Converting NSData to SecKeyRef

問題は、通常は存在し、他のセキュリティ API (Java など) によって期待される ID ヘッダーが iOS では予期されない場合に、iOS が公開鍵と秘密鍵をわずかに異なる方法で処理することです。したがって、それらを取り除く必要があります。

于 2014-05-20T05:23:15.403 に答える
1

おそらく、すべてのキーをキーチェーンに格納する代わりに、単純な文字列をキーチェーンに (secret_hash として) 格納することができます。また、広く採用されている一般的な AFNetworking ライブラリを使用して、バックエンド Web サービスへの安全な呼び出しを行います。

そのため、秘密鍵を使用してバックエンド サービスへのリクエストに署名する必要がある場合は、(a) サービス呼び出し用の堅牢なラッパー ライブラリ (AFNetworking) を使用し、(b) 秘密鍵を .pfx ファイルとして保存することをお勧めします。アプリからアクセスできる場所 (.pfx ファイルはプロジェクト ルートに保持します)。

したがって、AFHTTPClient の独自のサブクラスを作成し、サブクラスを使用して、.pfx から抽出された資格情報を使用するように設定されたチャレンジ ブロックで AFHTTPRequestOperations を作成します。

このように、AFHTTPRequestOperation を直接作成する代わりに、AFHTTPClient サブクラスの 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);
    } 

上記で使用した ID 抽出ヘルパー関数の詳細を以下に示します ...

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

この方法で (つまり、AFHTTPClient サブクラスの MySignedAFHTTPRequestOperation メソッドを介して) AFHTTPRequest を作成したら、それを実行のために NSOperationsQueue に追加します (もちろん、操作を作成するときに、成功および失敗のハンドラー ブロックを適切に設定する必要があります)。

要約すれば:

  • 堅牢な AFNetworking フレームワークを使用して Web サービスの呼び出しを行う
  • 証明書の秘密キーを .pfx ファイルとしてアプリに保存し、それを使用して資格情報とキーを作成します
  • 作成する各操作オブジェクト内に資格情報を設定することにより、AFNetworking が暗号化を実行できるようにします。

お役に立てれば。

于 2014-02-19T22:19:34.953 に答える