【问题标题】:Importing private key into Keychain is not working as expected in iphone将私钥导入钥匙串在 iphone 中无法正常工作
【发布时间】:2023-03-23 18:33:01
【问题描述】:

在将请求发送到后端服务器之前,我需要对其进行签名。但是私钥给了我。所以我需要导入它,然后用它来签名。我能够导入和登录,但该数据与我使用 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/中的代码示例导入公钥并验证失败。

【问题讨论】:

  • kSecPaddingPKCS1 的值是多少?你能尝试比较一下公钥和私钥的模数吗?
  • 嗨@owlstead,你能详细说明该怎么做吗?您还看到我保存和检索私钥的方式有任何问题吗?如果有其他可用的库可供我使用,请告诉我。
  • 对不起,我不是ios专家。不过,我现在对加密做了很多,所以我想我给你一些一般性的建议。例如。 kSecPaddingPKCS1 没有指定哈希值,SHA-1 很可能是默认值。如果私钥和公钥的模数不匹配,则它们不属于同一密钥对。在这两种情况下,签名验证都会失败。
  • 嗨@owlstead,iOS 不支持我想要的 SHA256 填充。所以我实际上使用 SHA256 输入,然后使用 kSecPaddingPKCS1 假设它不会再做任何散列。
  • 还有,你有什么进展吗,或者你还在努力吗?

标签: iphone ios cryptography rsa keychain


【解决方案1】:

查看接受的答案中的最后一种方法:Converting NSData to SecKeyRef

问题在于 iOS 处理公钥和私钥的方式略有不同,其中通常存在且其他安全 API(例如 Java 中)所期望的标识标头在 iOS 中不期望。所以你必须把它们去掉。

【讨论】:

    【解决方案2】:

    也许您可以在钥匙链中存储一个简单的字符串(作为 secret_hash),而不是将所有密钥存储在钥匙串中。此外,使用广泛采用的通用 AFNetworking 库对后端 Web 服务进行安全调用。

    因此,如果您需要使用私钥对后端服务的请求进行签名,我建议您通过 (a) 使用强大的包装库进行服务调用 (AFNetworking) 和 (b) 将私钥存储为.pfx 文件位于应用程序可访问的位置(我会将 .pfx 文件保留在项目根目录中。)

    因此,创建您自己的 AFHTTPClient 子类,并使用该子类创建 AFHTTPRequestOperations,并将质询块设置为使用从 .pfx 中提取的凭据。

    这种方式,而不是直接创建 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);
        } 
    

    这里是关于上面使用的身份提取辅助函数的更多细节......

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

    一旦您以这种方式创建 AFHTTPRequest(即通过 AFHTTPClient 子类的 MySignedAFHTTPRequestOperation 方法),将其添加到 NSOperationsQueue 以执行(当然,您需要在创建操作时适当地设置成功和失败处理程序块)

    总结:

    • 使用强大的 AFNetworking 框架进行 Web 服务调用
    • 将证书私钥作为 .pfx 文件存储在应用中,并使用它来创建凭据和密钥
    • 允许 AFNetworking 通过在您创建的每个操作对象中设置凭据来为您进行加密。

    希望这会有所帮助。

    【讨论】:

    • 将私钥存储在没有密码的普通文件中听起来是个坏主意。建议将其作为文件与(移动)应用程序一起分发似乎违背了它被欺骗的任何安全目的。 KeyChain 是专门用于存储敏感数据的,应该使用。
    • 点了。我的解决方案更多地关注后端服务的调用并将加密融入该框架(AFNetworking)我已经更新了上面的解决方案,通过在钥匙串中存储的不是密钥本身而是密钥的密码短语来解决您的问题(具体调用从钥匙串中获取密码短语需要在获取 mySecretHash 的行中替换。
    • 即使进行了这种更改,钥匙串仍然是存储钥匙等敏感信息的更好地方。也不清楚你的建议如何帮助发帖者,他的问题似乎是关于生成签名的机制,而不是发出 HTTP 请求。
    • 这回应了发帖人询问的部分“如果有办法我可以避免导入钥匙串,那也很棒。”
    • 生成签名的机制(提取 id 和信任)就在示例代码中...
    【解决方案3】:

    如果您只是想将标准 PHP 兼容的 base64 字符串类型格式的 AES-256 RSA PEM 存储到系统钥匙串中,这对我有用。

    我在这里发布这个是因为很多地方,我读到你根本不能将 PEM 文件直接粘贴到 iOS 中;否则你必须从中删除一些标题;等等。但是,至少在 iOS 9.3 上,这现在可以工作了,如果我在某个地方看到过这个,那么它会为我节省很多时间。 (注意:以下是来自https://github.com/ideawu/Objective-C-RSAObjective-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;
    }
    

    【讨论】:

      猜你喜欢
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-30
      • 2013-09-09
      • 1970-01-01
      • 1970-01-01
      • 2022-01-19
      相关资源
      最近更新 更多