11

Objective C と iOS プログラミングは初めてです。

サーバーとクライアント間で交換する必要があるデータを暗号化および復号化するために、openssl を使用して生成された単純な公開/秘密キー (PEM 形式) を使用しています。これをJavaサーバーとクライアントで正常に機能させました。

Java で公開鍵を使用してデータを暗号化し、Objective C / iOS で秘密鍵を使用して復号化していたときに問題が発生しました。いくつかの例を調べていくつかのコードをまとめましたが、秘密鍵から SecKeyRef を作成する一環として常に SecItemCopyMatching を呼び出すと、エラー -25300 が発生します。

ところで、ここには証明書は含まれておらず、単なるキーです。これが私がやっていることです:

  1. PEM 秘密鍵と Base64 デコードを読み取ります。
  2. SecItemCopyMatching を使用して、デコードされた文字列から SecKeyRef を生成します。
  3. SecKeyDecrypt を使用して復号化します。

私の問題は、-25300 のステータスを返すステップ #2 です (errSecItemNotFound –25300
The item cannot be found. iOS 2.0 以降で使用可能)。

SecKeyRef を生成するためのコードは次のとおりです。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString *challenge = @"2KFqc46DNSWrizzv69lJN25o62xEYQw/QLcMiT2V1XLER9uJbOu+xH2qgTuNWa1HZ9SW3Lq+HovtkhFmjmf08QkVQohHmxCJXVyCgVhPBleScAgQ8AoP3tmV0RqGb2mJrb19ybeYP7uZ2piVtF4cRwU1gO3VTooCUK3cX4wS7Tc=";
NSLog(@"challenge, %@", challenge);

NSData *incomingData = [self base64DataFromString:challenge];
uint8_t *challengeBuffer = (uint8_t*)[incomingData bytes];
NSLog(@"challengeBuffer: %s", challengeBuffer);

[self decryptWithPrivateKey:challengeBuffer];

free(challengeBuffer);

return YES;
}

// Generate a SecKeyRef from the private key in the private.pem file.
- (SecKeyRef)getPrivateKeyRef {
NSString *startPrivateKey = @"-----BEGIN RSA PRIVATE KEY-----";
NSString *endPrivateKey = @"-----END RSA PRIVATE KEY-----";
NSString* path = [[NSBundle mainBundle] pathForResource:@"private"
                                                 ofType:@"pem"];
NSString* content = [NSString stringWithContentsOfFile:path
                                              encoding:NSUTF8StringEncoding
                                                 error:NULL];
NSLog(@"Private Key: %@", content);

NSString *privateKey;
NSScanner *scanner = [NSScanner scannerWithString:content];
[scanner scanUpToString:startPrivateKey intoString:nil];
[scanner scanString:startPrivateKey intoString:nil];
[scanner scanUpToString:endPrivateKey intoString:&privateKey];

NSData *privateTag = [self dataWithBase64EncodedString:privateKey];
NSLog(@"Decoded String: %@", privateTag);

OSStatus status = noErr;
SecKeyRef privateKeyReference = NULL;

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

// Set the private key query dictionary.
[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
//[queryPrivateKey setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnRef];


// Get the key.
status = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference);
NSLog(@"status: %ld", status);

if(status != noErr)
{
    privateKeyReference = NULL;
}

return privateKeyReference;
}

// Decrypt data
- (void)decryptWithPrivateKey:(uint8_t *)cipherBuffer {
OSStatus status = noErr;

SecKeyRef privateKeyRef = [self getPrivateKeyRef];

size_t plainBufferSize = SecKeyGetBlockSize(privateKeyRef);
uint8_t *plainBuffer = malloc(plainBufferSize);

size_t cipherBufferSize = strlen((char *)cipherBuffer);
NSLog(@"decryptWithPrivateKey: length of input: %lu", cipherBufferSize);

//  Error handling
status = SecKeyDecrypt(privateKeyRef,
                       PADDING,
                       cipherBuffer,
                       cipherBufferSize,
                       &plainBuffer[0],
                       &plainBufferSize
                       );
NSLog(@"decryption result code: %ld (size: %lu)", status, plainBufferSize);
NSLog(@"FINAL decrypted text: %s", plainBuffer);
}

私はここ数日頭を悩ませていて、ここで助けが必要だと感じました. 任意のポインタはありますか?iOS が提供する Crypto ドメインの知識とサポートを得るためにもっと時間を費やすこともできますが、私は iOS プログラミングをまったく行っておらず、これは 1 回限りのことです。

方向性が必要なだけで、それを機能させるのに苦労することができます。

ティア。

4

3 に答える 3

8

残念ながら、iOS のセキュリティ フレームワークでは、秘密鍵がパスフレーズ付きの PKCS12 形式である必要があります。公開鍵は X509 Armored DER または PKCS12 にすることができますが、秘密鍵は PKCS12 である必要があります。使用しようとしている秘密鍵は、PEM 形式の RSA キーです。

キーにアクセスできる場合は、openssl コマンド ライン ツールを使用して変換できます。

openssl pkcs12 -export -nocerts -inkey privatekey.pem -out privatekey.p12

これにより、秘密鍵を含む PKCS12 ファイルが作成され、パスフレーズが必要になります。秘密鍵を制御できない場合 (たとえば、サーバーなどの外部ソースからのものである場合)、うまくいきません。

しかし、上記の手順を実行して、厄介な PEM RSA 秘密鍵を PKCS12 に変換できたと仮定しましょう。PKCS12 データから秘密鍵を抽出することはそれほど難しくありません。

  1. PKCS12 を としてロードしNSDataます。dataWithContentsOfURL:これがファイルシステム上のリソースである場合、これを行うことができます。
  2. SecPKCS12Importパスフレーズを含む PKCS12 データをインポートするために使用します。
  3. SecIdentityRefインポートされたアイテムから を抽出します。
  4. から秘密鍵をコピーします。SecIdentityRef

そのための関数は次のようになります。

OSStatus    SecKeyPrivatePKCS12Import(CFDataRef keyData, CFStringRef passphrase, SecKeyRef *privateKey){
    OSStatus        status              = errSecSuccess;
    CFDictionaryRef secImportOptions    = NULL;
    CFArrayRef      secImportItems      = NULL;

    if ((keyData != NULL) && (CFStringGetLength(passphrase) > 0) ){
        const void *keys[] = { kSecImportExportPassphrase };
        const void *values[] = { passphrase };

        secImportOptions = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

        status = SecPKCS12Import((CFDataRef) keyData, (CFDictionaryRef)secImportOptions, &secImportItems);
        if (CFArrayGetCount(secImportItems) > 0){
            CFDictionaryRef identityDict = CFArrayGetValueAtIndex(secImportItems, 0);
            SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
            SecIdentityCopyPrivateKey(identityApp, privateKey);
        }
    }

    return status;
}

Objective-C から呼び出すと、次のようになります。

OSStatus status = errSecSuccess;

status = SecKeyPrivatePKCS12Import((_bridge CFDataRef)data, (_bridge CFStringRef)passphrase, &privateKey);
if (privateKey == NULL){
   // Check the status value for why it failed
}

NSData「データ」はPKCS12 データを含む のインスタンスであり、「パスフレーズ」はパスフレーズを表すインスタンスであると仮定しますNSString。成功すると、PKCS12 データからインポートされた秘密鍵が「privateKey」に入力されます。

于 2014-09-11T20:31:08.310 に答える
2

JavaサーバーとiPhoneアプリケーションを使用していたときに同じ問題に直面しましたが、回避策は次のとおりです。

  1. Java サーバーで p12 を生成します。【パスワードは必ずメモしてください。】
  2. p12 ファイルの raw バイトを base 64 文字列に変換します。
  3. それらのデータは、必要に応じて iOS アプリケーションに送信します。

    3.1 Base 64 をテキスト ファイルに入れて iOS に送信できます。[私の場合、最も安全な方法で正常に動作します。]

    3.2 JSON 文字列を使用してその文字列を送信できます。[これにより、データが破損する可能性があります。]

  4. iPhone アプリケーションでデータを取得したら、base 64 文字列を NSData に変換します。NSData+Base64
  5. 次のメソッドを使用して、秘密鍵の SecKeyRef を取得します。

    - (SecKeyRef)getPrivateKeyFromData:(NSData *)p12Data withPassword:(NSString *)password {
        NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
        SecKeyRef privateKey = NULL;
        [options setObject:password forKey:(__bridge id)kSecImportExportPassphrase];
        CFArrayRef items = NULL;// = CFArrayCreate(NULL, 0, 0, NULL);
        OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)p12Data,
                                              (__bridge CFDictionaryRef)options, &items);
        if (securityError == noErr && CFArrayGetCount(items) > 0) {
             CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
             SecIdentityRef identityApp =
             (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                          kSecImportItemIdentity);
             securityError = SecIdentityCopyPrivateKey(identityApp, &privateKey);
        if (securityError != noErr) {
                privateKey = NULL;
            }
        }
        //NSLog(@"-------------------- Private Key Error %d",(int)securityError);
        CFRelease(items);
        options = nil;
        p12Data = nil;
        password = nil;
        return privateKey;
    }
    

お役に立てれば!!!!!

于 2014-09-09T13:21:58.417 に答える
0

秘密鍵と証明書をキーチェーンに保存しました。そうでなければSecItemCopyMatching何もしません。これを一度だけインポートする必要があります。

/* importing client identity (private key) */
NSData* certificateData = ... ; // decoded pkcs21 certificate from base64 pem
NSString* passcode = @"passphrased used to encrypt the private key";
CFDictionaryRef optionsDictionary = (__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys: passcode,  kSecImportExportPassphrase, nil];
CFArrayRef certificates;
OSStatus error = SecPKCS12Import((__bridge CFDataRef) certificateData, optionsDictionary, &certificates);
CFDictionaryRef myIDs = CFArrayGetValueAtIndex(certificates, 0); 

SecIdentityRef identity = (SecIdentityRef) CFDictionaryGetValue(myIDs, kSecImportItemIdentity);

NSDictionary* clientCertificateQuery = @{(__bridge id)kSecValueRef        : identity,
                                         (__bridge id)kSecAttrLabel       : @"some label you can use to find the item again with SecItemCopyMatching"};
OSStatus err = SecItemAdd((__bridge CFDictionaryRef) clientCertificateQuery, NULL);

その後SecItemCopyMatching、ID のSecIdentityCopyPrivateKey取得と秘密鍵の取得に使用できます。

NSDictionary* clientCertificateQuery = @{(__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitAll,
                                         (__bridge id)kSecClass      : (__bridge id)kSecClassIdentity,
                                         (__bridge id)kSecReturnRef   : (__bridge id)kCFBooleanTrue};
SecIdentityRef identity = NULL;
OSStatus errorCode = SecItemCopyMatching((__bridge CFDictionaryRef) clientCertificateQuery, &identity);
SecKeyRef privateKeyRef;
OSStatus err = SecIdentityCopyPrivateKey (identity, &privateKeyRef);

OSStatus エラーは必ずチェックしてくださいerrSecDuplicateItem

Apple のCertificate, Key, and Trust Services Reference を必ずお読みください。

于 2014-09-05T15:55:47.523 に答える