简体   繁体   English

使用Objective C在iOS中使用公钥加密或签名字符串

[英]encrypt or sign string with public key in iOS with Objective C

i have a private key. 我有一个私钥。 Text File that begins like "--- begin private key..." 以“ ---开始私钥...”开头的文本文件

i want to use that key to encrypt an NSString. 我想使用该密钥来加密NSString。 since its the private key, better call it sign an NSString. 由于它是私钥,因此最好将其签名为NSString。

can this be done without any external frameworks? 没有任何外部框架就能做到吗?

the result should be equivalent to the php openssl_sign function. 结果应等于php openssl_sign函数。

The iOS SDK framework you will need to use is called CommonCrypto . 您将需要使用的iOS SDK框架称为 CommonCrypto Here's a very good article that describes the right way to go about it. 这是一篇很好的 文章 ,描述了正确的解决方法。

Edit: I missed the part about compatibility with the PHP function openssl_sign . 编辑:我错过了有关与PHP函数openssl_sign兼容性的部分。 The solution below resolves that. 下面的解决方案可以解决该问题。

The way to do this so that it's compatible with the PHP function openssl_sign is to use the OpenSSL library. 使其与PHP函数openssl_sign兼容的openssl_sign是使用OpenSSL库。 The openssl_sign function uses OpenSSL's EVP API internally to encrypt the input string using the private key and compute the SHA-1 hash digest of that encrypted string. openssl_sign函数在内部使用OpenSSL的EVP API来使用私钥对输入字符串进行加密,并计算该加密字符串的SHA-1哈希摘要。 It's common then to convert this hash digest into a Base64-encoded string. 然后,通常将此哈希摘要转换为Base64编码的字符串。


Unfortunately, the iOS SDK does not include OpenSSL, but it's easy to build it. 不幸的是,iOS SDK不包含OpenSSL,但构建起来很容易。 The following instructions for building OpenSSL for iOS are taken from this blog post and are reproduced here to provide a complete solution to the question. 以下关于为iOS构建OpenSSL的说明摘自该博客文章,并在此处转载以提供该问题的完整解决方案。

In Terminal, follow those steps to build the OpenSSL library for iOS: 在终端中,按照以下步骤构建适用于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

This will take a few minutes but once it's complete you can verify that the build library is a universal library that you can use on iOS devices and in the iOS Simulator using the following command: 这将需要几分钟,但是一旦完成,您可以使用以下命令验证构建库是否为通用库,您可以在iOS设备和iOS Simulator中使用它:

lipo -info ~/openssl-ios/lib/*.a

Now that the OpenSSL library has been built, let's got on with writing the code to sign a string. 现在已经建立了OpenSSL库,让我们继续编写代码以对字符串进行签名。


First, we need to setup the Xcode project to link against the OpenSSL library. 首先,我们需要设置Xcode项目以链接到OpenSSL库。 Drag & drop both libcrypto.a and libssl.a to the Frameworks group in the Project Navigator of your iOS project. libcrypto.alibssl.alibssl.a到iOS项目的项目导航器中的Frameworks组中。 In your project's Build Settings , add the following to the Header Search Paths setting: 在项目的Build Settings中 ,将以下内容添加到Header Search Paths设置:

~/openssl-ios/include/include

Next, create a new Objective-C Category file called openssl_sign on the NSString class. 接下来,在NSString类上创建一个名为openssl_sign的新Objective-C Category文件。 In NSString+openssl_sign.h , define the following interface: NSString+openssl_sign.h ,定义以下接口:

@interface NSString (openssl_sign)

- (NSString *)signStringWithPrivateKey:(NSData *)privateKey;

@end

In NSString+openssl_sign.m , add the following header imports: NSString+openssl_sign.m ,添加以下标头导入:

#import <openssl/evp.h>
#import <openssl/pem.h>

And add the following implementation of signStringWithPrivateKey: : 并添加以下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

In the class that will be signing the string, you can now import NSString+openssl_sign.h and sign the string like so: 在将对字符串进行签名的类中,您现在可以导入NSString+openssl_sign.h并对字符串进行签名,如下所示:

NSData *privateKey = ...; // Read the .pem file into a NSData variable
NSString *helloSignature = [@"hello" signStringWithPrivateKey:privateKey];

You can verify that the signatures are the same using the following command in Terminal: 您可以在终端中使用以下命令来验证签名是否相同:

echo -n "hello" | openssl dgst -sha1 -sign priv.pem | openssl enc -base64 | tr -d '\n'

You can solve this much easier with no external sources or components. 您无需外部资源或组件即可轻松解决此问题。

I found out how and wanted to share it so i may help others. 我发现了方法并想与他人分享,以便我可以帮助他人。

  1. You need to load the key file a SecKeyRef and safe the maxPlainLen as well 您需要将密钥文件加载为SecKeyRef并同时保护maxPlainLen
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:privateKeyResourceName ofType:@"p12"];
    NSData *p12Data = [NSData dataWithContentsOfFile:resourcePath];

    NSMutableDictionary * options = [[NSMutableDictionary alloc] init];

    SecKeyRef privateKeyRef = NULL;

    //change to the actual password you used here
    [options setObject:@"_YOURPASSWORDHERE__" forKey:(__bridge id)kSecImportExportPassphrase];

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);

    OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) p12Data,
                                             (__bridge CFDictionaryRef)options, &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;
        }
    }
    CFRelease(items);

    privateKey = privateKeyRef;
    maxPlainLen = SecKeyGetBlockSize(privateKey) - 12;
  1. You can convert NSString with a category method to SHA1 您可以将带有类别方法的NSString转换为SHA1
- (NSData*)toSha1AsData {
        // PHP uses ASCII encoding, not UTF
        const char *s = [self cStringUsingEncoding:NSASCIIStringEncoding];
        NSData *keyData = [NSData dataWithBytes:s length:strlen(s)];

        // This is the destination
        uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0};
        // This one function does an unkeyed SHA1 hash of your hash data
        CC_SHA1(keyData.bytes, keyData.length, digest);

        // Now convert to NSData structure to make it usable again
        NSData *out = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]

        return out;
    }
  1. Now you can sign your SHA1 with this method 现在您可以使用此方法对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; }

It works very well and without any external libs. 它运行良好,没有任何外部库。 There is no need to compile some wierd openssl stuff. 无需编译一些奇怪的openssl东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在ios中使用RSA公钥加密和解密字符串(纯文本),swift - how to encrypt and decrypt a String(Plain Text) with RSA public key in ios, swift 使用Objective-C签名文件(iOS / Mac) - Sign file with objective-c (ios/mac) iOS:使用公钥(具有模数和指数)进行RSA加密 - iOS: RSA Encrypt using public key (with modulus and exponent) 如何在iOS下使用公钥加密数据 - How to encrypt data using public key under iOS 在iOS Swift / Objective C中实现Diffie Hellman生成公钥和私钥的方式有哪些?(Chilkat除外) - What are the ways to implement Diffie Hellman to generate public and private key in iOS swift/Objective C?(Other than Chilkat) 使用IOS进行公钥加密和使用C#进行解密 - Public key encryption with IOS and decryption with C# 如何在 iOS Objective-C 中集成“使用 Apple 登录”流程? - How to integrate 'Sign in with Apple' flow in iOS Objective-C? Objective C Google Maps API公钥功能 - Objective C Google Maps API public key function 如何在 IOS 中使用 GPG(创建密钥对、列出密钥、导入公钥、加密、解密)? - How to use GPG with IOS (Create keypair, List Keys, Import public key, encrypt, decrypt)? 如何使用iOS上的服务器公钥txt文件加密数据 - How to encrypt data using server's public key txt file on iOS
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM