iOS verify digital signature

2019-02-19 09:44发布

In my application, I have a public key (represented as string), plain message and digital signature, represented as base64 encoded string, hashed with SHA256 and encrypted with RSA). Now, I need to verify digital signature. I was trying to do as follows:

  1. create SecKeyRef from NSString (taken from here)
  2. create SHA256 digest from original message
  3. verify signature using SecKeyRawVerify function

(I am trying to avoid using OpenSSL function)

Additionally, my digital signature was created using Java's SHA256withRSA method. I was reading here that SHA256WithRSA appends algorithm identifier with the actual hash. Now, I am not sure whether or not I need to append it to the hash.

Anyway, in both cases I get error -50, which according to Apple's documentations means One or more parameters passed to a function were not valid.

Here is my code:

-(BOOL) verifySignature:(NSString*) rawData andKey:(NSString*) key andSignature:(NSString*)signature {

    NSData* originalData = [rawData dataUsingEncoding:NSUTF8StringEncoding];
    NSData *signatureData = [NSData dataFromBase64String:signature];

    SecKeyRef publicKey = [self generatePublicKey:key];

    uint8_t sha2HashDigest[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256([originalData bytes], [originalData length], sha2HashDigest);

    //DO I NEED THIS?
    NSString *algIdentifier = @"1.3.14.3.2.26";
    NSData *algData = [algIdentifier dataUsingEncoding:NSUTF8StringEncoding];
    NSData* d_hash = [NSData dataWithBytes:sha2HashDigest length:CC_SHA256_DIGEST_LENGTH];

    NSMutableData *concatenatedData = [NSMutableData data];
    [concatenatedData appendData:algData];
    [concatenatedData appendData:d_hash];

    OSStatus verficationResult =  SecKeyRawVerify (publicKey,
                     kSecPaddingPKCS1SHA256,
                     (const uint8_t *)[d_hash bytes],
                     (size_t)[d_hash length],
                     (const uint8_t *)[signatureData bytes],
                     (size_t)[signatureData length]
                     );


    CFRelease(publicKey);

    if (verficationResult == errSecSuccess){
        NSLog(@"Verified");
        return YES;
    }
    return NO;

}

- (SecKeyRef)generatePublicKey:(NSString *)key
{

    // This will be base64 encoded, decode it.
    NSData *d_key = [NSData dataFromBase64String:key];
    d_key = [self stripPublicKeyHeader:d_key];
    if (d_key == nil) return(nil);

    NSData *d_tag = [NSData dataWithBytes:[@"pubKey" UTF8String] length:[@"pubKey" length]];

    NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
    [publicKey setObject:(id) kSecClassKey forKey:(id)kSecClass];
    [publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [publicKey setObject:d_tag forKey:(id)kSecAttrApplicationTag];
    SecItemDelete((CFDictionaryRef)publicKey);

    CFTypeRef persistKey = nil;

    // Add persistent version of the key to system keychain
    [publicKey setObject:d_key forKey:(id)kSecValueData];
    [publicKey setObject:(id) kSecAttrKeyClassPublic forKey:(id)
     kSecAttrKeyClass];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)
     kSecReturnPersistentRef];

    OSStatus secStatus = SecItemAdd((CFDictionaryRef)publicKey, &persistKey);
    if (persistKey != nil) CFRelease(persistKey);

    if ((secStatus != noErr) && (secStatus != errSecDuplicateItem)) {
        [publicKey release];
        return(nil);
    }

    // Now fetch the SecKeyRef version of the key
    SecKeyRef keyRef = nil;

    [publicKey removeObjectForKey:(id)kSecValueData];
    [publicKey removeObjectForKey:(id)kSecReturnPersistentRef];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef
     ];

    [publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    secStatus = SecItemCopyMatching((CFDictionaryRef)publicKey,
                                    (CFTypeRef *)&keyRef);

    [publicKey release];
    return keyRef;

}

1条回答
做自己的国王
2楼-- · 2019-02-19 10:41

Maybe this answer is a little late but I had the same problem.

It turns out that Java handles the hashing for you, but iOS does not.

So if you have a plaintext called plainText you might generate a signature on it in Java doing this:

public static byte[] sign(PrivateKey key, byte[] plainText) {
    try {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(key);
        signature.update(plainText);
        return signature.sign();
    } catch (Exception e) {
        return null;
    }
}

But then to verify it in iOS you need to manually take a hash of the plaintext like so:

+ (BOOL)verifySignature:(uint8_t*)signature signatureLen:(size_t)sLen
            withPlainText:(uint8_t*)plainText plainTextLen:(size_t)pLen
            andKey:(SecKeyRef)key {
    uint8_t hash[32];
    CC_SHA256(plainText, pLen, hash);
    OSStatus returnCode = SecKeyRawVerify(key,
                                          kSecPaddingPKCS1SHA256,
                                          hash,
                                          32,
                                          signature,
                                          sLen);
    return returnCode == 0;
}

In the above method, signature is the bytes generated by the Java method.

Of course, you may not want to hardcode parameters such as the the hash function used (and length of hash).

查看更多
登录 后发表回答