使用Objective C在iOS中使用公钥对string进行encryption或签名
我有一个私钥。 以“—开始私人密钥…”开头的文本文件
我想用这个密钥来encryption一个NSString。 因为它的私钥,最好把它称为一个NSString。
这可以做到没有任何外部框架?
结果应该相当于php的openssl_sign函数。
您需要使用的iOS SDK框架称为 CommonCrypto
。这里有一篇很好的文章 ,描述了正确的方法。
编辑:我错过了与PHP函数openssl_sign
兼容的部分。 以下解决scheme可以解决此问题。
做到这一点,以便它与PHP函数openssl_sign
兼容的openssl_sign
是使用OpenSSL库。 openssl_sign
函数在内部使用OpenSSL的EVP API来使用私钥encryptioninputstring,并计算该encryptionstring的SHA-1哈希摘要。 将这个哈希摘要转换成Base64编码的string是很常见的。
不幸的是,iOS SDK不包含OpenSSL,但构build起来很简单。 以下有关构buildiOS版OpenSSL的说明摘自本博客文章,并在此转载以提供完整的问题解答。
在terminal中,按照以下步骤构build适用于iOS的OpenSSL库:
# Make a directory in which to run the build mkdir ~/openssl-ios cd ~/openssl-ios # Download the openssl source (verify the file before using it in production!) curl -O http://www.openssl.org/source/openssl-1.0.1e.tar.gz # Download the openssl iOS build script curl -O https://raw.github.com/Raphaelios/raphaelios-scripts/master/openssl/build-openssl.sh # Make the build script executable chmod +x build-openssl.sh # Run the script (takes about 3min on an Intel Core i5) ./build-openssl.sh
这将需要几分钟的时间,但是一旦完成,您可以使用以下命令validation构build库是可用于iOS设备和iOS模拟器的通用库:
lipo -info ~/openssl-ios/lib/*.a
现在OpenSSL库已经build好了,让我们开始编写代码来签名一个string。
首先,我们需要设置Xcode项目来链接到OpenSSL库。 将libcrypto.a
和libssl.a
到iOS项目的Project Navigator中的Frameworks组中。 在您的项目的“ 生成设置”中 ,将以下内容添加到“ 标题searchpath”设置中:
~/openssl-ios/include/include
接下来,在NSString
类上创build一个名为openssl_sign
的新的Objective-C Category
文件。 在NSString+openssl_sign.h
,定义下面的接口:
@interface NSString (openssl_sign) - (NSString *)signStringWithPrivateKey:(NSData *)privateKey; @end
在NSString+openssl_sign.m
,添加下列标题导入:
#import <openssl/evp.h> #import <openssl/pem.h>
并添加以下signStringWithPrivateKey:
实现:
@implementation NSString (openssl_sign) - (NSString *)signStringWithPrivateKey:(NSData *)privateKeyData { BIO *publicBIO = NULL; EVP_PKEY *privateKey = NULL; if ((publicBIO = BIO_new_mem_buf((unsigned char *)[privateKeyData bytes], [privateKeyData length])) == NO) { NSLog(@"BIO_new_mem_buf() failed!"); return nil; } if (PEM_read_bio_PrivateKey(publicBIO, &privateKey, NULL, NULL) == NO) { NSLog(@"PEM_read_bio_PrivateKey() failed!"); return nil; } const char * cString = [self cStringUsingEncoding:NSUTF8StringEncoding]; unsigned int stringLength = [self length]; unsigned char * signatureBuffer[EVP_MAX_MD_SIZE]; int signatureLength; EVP_MD_CTX msgDigestContext; const EVP_MD * msgDigest = EVP_sha1(); EVP_MD_CTX_init(&msgDigestContext); EVP_SignInit(&msgDigestContext, msgDigest); EVP_SignUpdate(&msgDigestContext, cString, stringLength); if (EVP_SignFinal(&msgDigestContext, (unsigned char *)signatureBuffer, (unsigned int *)&signatureLength, privateKey) == NO) { NSLog(@"Failed to sign string."); return nil; } EVP_MD_CTX_cleanup(&msgDigestContext); EVP_PKEY_free(privateKey); NSData *signatureData = [NSData dataWithBytes:signatureBuffer length:signatureLength]; NSString *signature = [signatureData base64EncodedStringWithOptions:0]; return signature; } @end
在将要对string进行签名的类中,现在可以导入NSString+openssl_sign.h
并像这样对string进行签名:
NSData *privateKey = ...; // Read the .pem file into a NSData variable NSString *helloSignature = [@"hello" signStringWithPrivateKey:privateKey];
您可以在terminal中使用以下命令validation签名是否相同:
echo -n "hello" | openssl dgst -sha1 -sign priv.pem | openssl enc -base64 | tr -d '\n'
无需外部资源或组件,您可以更轻松地解决这个问题。
我发现如何和想要分享它,所以我可以帮助别人。
- 您需要将密钥文件加载到SecKeyRef中,并安全地使用maxPlainLen
NSString * resourcePath = [[NSBundle mainBundle] pathForResource:privateKeyResourceName ofType:@“p12”]; NSData * p12Data = [NSData dataWithContentsOfFile:resourcePath]; NSMutableDictionary * options = [[NSMutableDictionary alloc] init]; SecKeyRef privateKeyRef = NULL; //改为您在此使用的实际密码 [options setObject:@“_ YOURPASSWORDHERE__”forKey:(__ bridge id)kSecImportExportPassphrase]; CFArrayRef items = CFArrayCreate(NULL,0,0,NULL); OSStatus securityError = SecPKCS12Import((__ bridge CFDataRef)p12Data, (__bridge CFDictionaryRef)选项,&项目); if(securityError == noErr && CFArrayGetCount(items)> 0){ CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items,0); SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity); securityError = SecIdentityCopyPrivateKey(identityApp,&privateKeyRef); 如果(securityError!= noErr){ privateKeyRef = NULL; } } CFRelease(项目); privateKey = privateKeyRef; maxPlainLen = SecKeyGetBlockSize(privateKey) - 12;
- 您可以使用类别方法将NSString转换为SHA1
- (NSData *)toSha1AsData { // PHP使用ASCII编码,而不是UTF const char * s = [self cStringUsingEncoding:NSASCIIStringEncoding]; NSData * keyData = [NSData dataWithBytes:s length:strlen(s)]; //这是目的地 uint8_t digest [CC_SHA1_DIGEST_LENGTH] = {0}; //这个函数执行散列数据的无键SHA1散列 CC_SHA1(keyData.bytes,keyData.length,digest); //现在转换为NSData结构,使其再次可用 NSData * out = [NSData dataWithBytes:摘要长度:CC_SHA1_DIGEST_LENGTH] 退出; }
- 现在你可以用这个方法来签名你的SHA1
(NSData *)signSha1Data:(NSData *)data { size_t plainLen = [data length]; if (plainLen > maxPlainLen) { NSLog(@"content(%ld) is too long, must < %ld", plainLen, maxPlainLen); return nil; } void *plain = malloc(plainLen); [data getBytes:plain length:plainLen]; size_t cipherLen = 128; // currently RSA key length is set to 128 bytes void *cipher = malloc(cipherLen); OSStatus returnCode = SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1, plain, plainLen, cipher, &cipherLen); NSData *result = nil; if (returnCode != 0) { NSLog(@"SecKeyEncrypt fail. Error Code: %ld", returnCode); } else { result = [NSData dataWithBytes:cipher length:cipherLen]; } free(plain); free(cipher); return result; }
它工作得很好,没有任何外部库。 没有必要编译一些更奇怪的openssl的东西。