使用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.alibssl.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' 

无需外部资源或组件,您可以更轻松地解决这个问题。

我发现如何和想要分享它,所以我可以帮助别人。

  1. 您需要将密钥文件加载到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;
  1. 您可以使用类别方法将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]

        退出;
     }
  1. 现在你可以用这个方法来签名你的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的东西。