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

Take a look at the last method in the accepted answer for: Converting NSData to SecKeyRef

The problem is that iOS handles public and private keys slightly differently where the identification header that usually exist and is expected by other security APIs (in Java, for instance) is not expected in iOS. So you have to strip them out.

于 2014-05-20T05:23:15.403 回答
1

也许不是将所有密钥存储在钥匙串中,您可以只在钥匙串中存储一个简单的字符串(作为 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 为您进行加密。

希望这可以帮助。

于 2014-02-19T22:19:34.953 回答
0

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

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