验证RSA签名iOS [英] Verifying RSA Signature iOS
问题描述
在我的静态库我有一个许可证文件。我想确保是由自己生成的(并没有被改变)。所以想法是使用从我已经阅读的RSA签名。
In my static Library I have a licence file. Which I want to make sure has been generated by myself (and has not been altered). So the idea was to use an RSA Signature from what I've read.
我看过互联网,这是我想出的:
I've looked on the internet and this is what I came up with:
私钥和自签名证书与我在此处找到的信息。
First: Generating the private keys and self signed certificates with the information I found here.
// Generate private key
openssl genrsa -out private_key.pem 2048 -sha256
// Generate certificate request
openssl req -new -key private_key.pem -out certificate_request.pem -sha256
// Generate public certificate
openssl x509 -req -days 2000 -in certificate_request.pem -signkey private_key.pem -out certificate.pem -sha256
// Convert it to cer format so iOS kan work with it
openssl x509 -outform der -in certificate.pem -out certificate.cer -sha256
之后,我创建一个许可证文件(日期和应用程序标识符作为内容),并为该文件生成签名基于此处找到的信息:
After that, I create a licence file (with a date and app identifier as contents) and generate a signature for that file like so based on the information found here:
// Store the sha256 of the licence in a file
openssl dgst -sha256 licence.txt > hash
// And generate a signature file for that hash with the private key generated earlier
openssl rsautl -sign -inkey private_key.pem -keyform PEM -in hash > signature.sig
我认为一切都很好。我没有得到任何错误,并得到的钥匙和证书和其他文件,如预期。
Which I think all works fine. I don't get any errors and get the keys and certificates and other files as expected.
接下来我复制 certificate.cer
, signature.sig
和 license.txt
现在我想检查签名是否已经由我签名,并且对license.txt有效。我发现很难找到任何好的例子,但这是我目前的:
Now I want to check if the signature has been signed by me and is valid for the license.txt. I found it fairly hard to find any good examples but this is what I have currently:
Seucyrity.Framework
我发现使用 SecKeyRef
引用RSA密钥/证书和 SecKeyRawVerify
来验证签名。
The Seucyrity.Framework
I found out uses a SecKeyRef
to reference an RSA key / certificate and SecKeyRawVerify
to verify a signature.
我有以下方法从文件加载公共密钥。
I have the following method to load the public key from a file.
- (SecKeyRef)publicKeyFromFile:(NSString *) path
{
NSData *myCertData = [[NSFileManager defaultManager] contentsAtPath:path];
CFDataRef myCertDataRef = (__bridge CFDataRef) myCertData;
SecCertificateRef cert = SecCertificateCreateWithData (kCFAllocatorDefault, myCertDataRef);
CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **) &cert, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef trust;
SecTrustCreateWithCertificates(certs, policy, &trust);
SecTrustResultType trustResult;
SecTrustEvaluate(trust, &trustResult);
SecKeyRef pub_key_leaf = SecTrustCopyPublicKey(trust);
if (trustResult == kSecTrustResultRecoverableTrustFailure)
{
NSLog(@"I think this is the problem");
}
return pub_key_leaf;
}
这是基于此 SO post。
Which is based on this SO post.
对于签名验证,我找到以下函数
For the signature validation I found the following function
BOOL PKCSVerifyBytesSHA256withRSA(NSData* plainData, NSData* signature, SecKeyRef publicKey)
{
size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey);
const void* signedHashBytes = [signature bytes];
size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
uint8_t* hashBytes = malloc(hashBytesSize);
if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
return nil;
}
OSStatus status = SecKeyRawVerify(publicKey,
kSecPaddingPKCS1SHA256,
hashBytes,
hashBytesSize,
signedHashBytes,
signedHashBytesSize);
return status == errSecSuccess;
}
这取自这里
在我的项目中,我调用的代码如下:
In my project I call the code like so:
// Get the licence data
NSString *licencePath = [[NSBundle mainBundle] pathForResource:@"licence" ofType:@"txt"];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:licencePath];
// Get the signature data
NSString *signaturePath = [[NSBundle mainBundle] pathForResource:@"signature" ofType:@"sig"];
NSData *signature = [[NSFileManager defaultManager] contentsAtPath:signaturePath];
// Get the public key
NSString *publicKeyPath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"];
SecKeyRef publicKey = [self publicKeyFromFile:publicKeyPath];
// Check if the signature is valid with this public key for this data
BOOL result = PKCSVerifyBytesSHA256withRSA(data, signature, publicKey);
if (result)
{
NSLog(@"Alright All good!");
}
else
{
NSLog(@"Something went wrong!");
}
目前总是说:出错了!虽然我不知道什么。我发现在获取公钥的方法中的信任结果等于 kSecTrustResultRecoverableTrustFailure
,我认为是问题。在 Apple文档中,我发现可能是由于已过期的证书。虽然这似乎不是这里的情况。但也许我生成证书的方式有问题吗?
Currently it always says: "Something went wrong!" though I am not sure what. I found out that trust result in the method that fetches the public key equals kSecTrustResultRecoverableTrustFailure
which I think is the problem. In the Apple documentation I found that could be the result of a certificate that has been expired. Though that does not seem to be the case here. But maybe there is something wrong with the way I generate my certificate?
我的问题归结到,我做错了,我怎么解决这个问题?我发现这个文档很稀疏,很难阅读。
My question boils down to, what am I doing wrong, and how could I fix that? I find the documentation on this quite sparse and hard to read.
我有上传具有生成的证书和此处引用的代码的iOS项目。
I have uploaded an iOS project with generated certificates and the code referenced here. Maybe that could come in handy.
推荐答案
问题在于你创建签名文件的方式;在同一步骤后,我能够生成二进制等效的 signature.sig
文件。
The problem is lying on the way you create the signature file; following the same step I was able to produce the binary equivalent signature.sig
file.
code> hash 文件我们可以看到openssl添加一些前缀(和十六进制编码的哈希):
By looking inside the hash
file we can see openssl add some prefix (and hex encode the hash):
$ cat hash
SHA256(licence.txt)= 652b23d424dd7106b66f14c49bac5013c74724c055bc2711521a1ddf23441724
c $ c> signature.sig 是基于 license.txt
So signature.sig
is based on that and not on license.txt
通过使用您的示例并使用以下内容创建签名文件:
By using your sample and creating the signing file with:
openssl dgst -sha256 -sign certificates/private_key.pem licence.txt > signature.sig
签名步骤正确,并且示例输出:好了!
The hashing & signing step gets correct, and the sample outputs: Alright All good!
我的档案的最后状态,只是为了
The final state of my file, just in case
- (SecKeyRef)publicKeyFromFile:(NSString *) path
{
NSData * certificateData = [[NSFileManager defaultManager] contentsAtPath:path];
SecCertificateRef certificateFromFile = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData);
SecPolicyRef secPolicy = SecPolicyCreateBasicX509();
SecTrustRef trust;
SecTrustCreateWithCertificates( certificateFromFile, secPolicy, &trust);
SecTrustResultType resultType;
SecTrustEvaluate(trust, &resultType);
SecKeyRef publicKey = SecTrustCopyPublicKey(trust);
return publicKey;
}
BOOL PKCSVerifyBytesSHA256withRSA(NSData* plainData, NSData* signature, SecKeyRef publicKey)
{
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], digest))
return NO;
OSStatus status = SecKeyRawVerify(publicKey,
kSecPaddingPKCS1SHA256,
digest,
CC_SHA256_DIGEST_LENGTH,
[signature bytes],
[signature length]);
return status == errSecSuccess;
}
PS: malloc
是一个泄漏
编辑:
当前 signature.sig
文件按原样工作,您必须生成与openssl相同的步骤(添加前缀,十六进制哈希和换行符 \\ n
),然后使用 kSecPaddingPKCS1
将此数据传递到 SecKeyRawVerify
,而不是 kSecPaddingPKCS1SHA256
:
To make your current signature.sig
file work as-is, you have to produce the same step as openssl (add prefix, hex-hash, and a newline \n
), then pass this data to SecKeyRawVerify
with kSecPaddingPKCS1
and not kSecPaddingPKCS1SHA256
:
BOOL PKCSVerifyBytesSHA256withRSA(NSData* plainData, NSData* signature, SecKeyRef publicKey)
{
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], digest))
return NO;
NSMutableString *hashFile = [NSMutableString stringWithFormat:@"SHA256(licence.txt)= "];
for (NSUInteger index = 0; index < sizeof(digest); ++index)
[hashFile appendFormat:@"%02x", digest[index]];
[hashFile appendString:@"\n"];
NSData *hashFileData = [hashFile dataUsingEncoding:NSNonLossyASCIIStringEncoding];
OSStatus status = SecKeyRawVerify(publicKey,
kSecPaddingPKCS1,
[hashFileData bytes],
[hashFileData length],
[signature bytes],
[signature length]);
return status == errSecSuccess;
}
这篇关于验证RSA签名iOS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!