簡體   English   中英

使用Objective C在iOS中使用公鑰加密或簽名字符串

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

我有一個私鑰。 以“ ---開始私鑰...”開頭的文本文件

我想使用該密鑰來加密NSString。 由於它是私鑰,因此最好將其簽名為NSString。

沒有任何外部框架就能做到嗎?

結果應等於php openssl_sign函數。

您將需要使用的iOS SDK框架稱為 CommonCrypto 這是一篇很好的 文章 ,描述了正確的解決方法。

編輯:我錯過了有關與PHP函數openssl_sign兼容性的部分。 下面的解決方案可以解決該問題。

使其與PHP函數openssl_sign兼容的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 Simulator中使用它:

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

現在已經建立了OpenSSL庫,讓我們繼續編寫代碼以對字符串進行簽名。


首先,我們需要設置Xcode項目以鏈接到OpenSSL庫。 libcrypto.alibssl.alibssl.a到iOS項目的項目導航器中的Frameworks組中。 在項目的Build Settings中 ,將以下內容添加到Header Search Paths設置:

~/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'

您無需外部資源或組件即可輕松解決此問題。

我發現了方法並想與他人分享,以便我可以幫助他人。

  1. 您需要將密鑰文件加載為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. 您可以將帶有類別方法的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. 現在您可以使用此方法對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東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM