我有一个私钥。以"---开始私钥…"开头的文本文件
我想用那个密钥加密一个NSString。既然它是私钥,最好称之为NSString签名。
这能在没有任何外部框架的情况下实现吗?
结果应该相当于phpopensslsign函数。
您需要使用的iOS SDK框架称为CommonCrypto
。这是一篇非常好的文章,描述了正确的方法。
编辑:我错过了与PHP函数openssl_sign
兼容的部分。下面的解决方案解决了这个问题
要使其与PHP函数openssl_sign
兼容,方法是使用OpenSSL库。openssl_sign
函数在内部使用OpenSSL的EVP API来使用私钥加密输入字符串,并计算该加密字符串的SHA-1哈希摘要。通常将这个散列摘要转换为Base64编码的字符串。
不幸的是,iOS SDK不包括OpenSSL,但构建它很容易。以下构建iOS版OpenSSL的说明摘自本博客文章,并在此处转载,以提供该问题的完整解决方案。
在终端中,按照以下步骤构建适用于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
这将需要几分钟的时间,但一旦完成,您可以使用以下命令验证构建库是否是可以在iOS设备和iOS模拟器中使用的通用库:
lipo -info ~/openssl-ios/lib/*.a
现在已经构建了OpenSSL库,让我们继续编写对字符串进行签名的代码。
首先,我们需要设置Xcode项目以链接到OpenSSL库。拖动&将libcrypto.a
和libssl.a
都放到iOS项目的项目导航器中的Frameworks组中。在项目的构建设置中,将以下内容添加到标题搜索路径设置中:
~/openssl-ios/include/include
接下来,在NSString
类上创建一个名为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
在将对字符串进行签名的类中,现在可以导入NSString+openssl_sign.h
并对字符串进行如下签名:
NSData *privateKey = ...; // Read the .pem file into a NSData variable
NSString *helloSignature = [@"hello" signStringWithPrivateKey:privateKey];
您可以在终端中使用以下命令验证签名是否相同:
echo -n "hello" | openssl dgst -sha1 -sign priv.pem | openssl enc -base64 | tr -d 'n'
您可以在没有外部源或组件的情况下更容易地解决此问题。
我发现了如何分享它,这样我就可以帮助别人。
- 您需要加载密钥文件SecKeyRef,并保护maxPlainLen
NSString*resourcePath=[[NSBundle mainBundle]pathForResource:privateKeyResourceType:@"p12"];NSData*p12Data=[NSData数据WithContentsOfFile:resourcePath];NSMutableDictionary*选项=[[NSMutaableDictionary alloc]init];SecKeyRef privateKeyRef=NULL;//更改为您在此处使用的实际密码[options setObject:@"_YOURPASSWORDHERE_"forKey:(__bridge id(kSecImportExportPassphrase];CFArrayRef项=CFArrayCreate(NULL,0,0,NULL(;OSStatus securityError=SecPKCS12Import(__bridge CFDataRef(p12数据,(__bridge CFDictionaryRef(选项,&items(;if(securityError==noErr&&CFArrayGetCount(items(>0({CFDictionaryRef identityDict=CFArrayGetValueAtIndex(items,0(;SecIdentityRef identityApp=(SecIdentityRef(CFDictionaryGetValue(identityDict,kSecImportItemIdentity(;securityError=SecIdentityCopyPrivateKey(identityApp,&privateKeyRef(;if(securityError!=noErr({privateKeyRef=NULL;}}CF发布(项(;privateKey=privateKeyRef;maxPlainLen=SecKeyGetBlockSize(私钥(-12;
- 您可以使用category方法将NSString转换为SHA1
-(NSData*(到Sha1AsData{//PHP使用ASCII编码,而不是UTFconst char*s=[self-cStringUsingEncoding:NSASIIStringEncoding];NSData*keyData=[NSData dataWithBytes:s length:strlen(s(];//这是目的地uint8_t摘要[CC_SHA1_IGEST_LENGTH]={0};//这一个函数对您的哈希数据进行未加密的SHA1哈希CC_SHA1(keyData.bytes,keyData.length,摘要(;//现在转换为NSData结构,使其再次可用NSData*out=[NSData dataWithBytes:摘要长度:CC_SHA_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;
}
它工作得很好,没有任何外部libs。没有必要编译一些wierd-openssl的东西。