iOS验证数字签名 [英] iOS verify digital signature
问题描述
在我的应用程序中,我有一个公共密钥(表示为字符串),纯消息和数字签名,表示为base64编码字符串,与SHA256哈希和加密与RSA)。
现在,我需要验证数字签名。我试图做如下:
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:
- 创建
SecKeyRef
从NSString
(摘自 here ) - 使用原始邮件创建SHA256摘要
- 使用
验证签名SecKeyRawVerify
function
- create
SecKeyRef
fromNSString
(taken from here) - create SHA256 digest from original message
- verify signature using
SecKeyRawVerify
function
(我想避免使用OpenSSL函数)
(I am trying to avoid using OpenSSL function)
此外,我的数字签名是使用Java的SHA256withRSA方法创建的。我在阅读这里 SHA256WithRSA将算法标识符附加到实际的散列。现在,我不知道是否需要将它附加到散列。
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.
无论如何,在这两种情况下,我得到错误-50,根据苹果的文档意味着
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.
这是我的代码:
-(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;
}
推荐答案
回答有点晚,但我有同样的问题。
Maybe this answer is a little late but I had the same problem.
事实证明,Java处理哈希为你,但iOS不。
It turns out that Java handles the hashing for you, but iOS does not.
因此,如果你有一个名为 plainText
的纯文本,你可以在Java上生成一个签名,这样做:
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;
}
}
但是要在iOS中验证它,使用如下的明文散列:
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;
}
在上述方法中,签名
是由Java方法生成的字节。
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).
这篇关于iOS验证数字签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!