使用RSA私钥对字符串进行签名

时间:2013-10-04 13:14:27

标签: ios objective-c swift

在我的应用程序中,我必须使用我已经拥有的RSA私钥来签名。完成此操作后,我想稍后使用签名。 我检查了几个库,但是它们都做了我需要的更多。 这就是我想做的事情:

NSString *message = @"This is a message";
NSString *privateKey = ...;

NSString *signature = [self signMessage:message withPrivateKey:privateKey];

怎么会

(NSString *)signMessage:(NSString *)message withPrivateKey:(NSString *)privateKey {
}

看起来像?

2 个答案:

答案 0 :(得分:3)

iOS有一个用于执行此操作的C API。我自己从来没有使用它,但看起来你需要一个带有私钥的PKCS12格式文件,你用SecKeyPKCS12Imort导入它并获取私钥。然后该函数为SecKeyRawSign以对您的字符串进行签名。

首先应使用已知的字符编码将字符串转换为NSData对象 - 可能是UTF-8,并且NSData中的字节已签名。

如果在字符串中使用非ASCII字符,并确保使用定义良好的编码,则还需要注意如何表示某些字符。例如,é可以表示为单个Unicode数字或Unicode急性重音,后跟字母e。

http://en.wikipedia.org/wiki/Precomposed_character

答案 1 :(得分:3)

我已经尝试了数百万种签名方法,包括Keychain,提取SecKeyRef,证书(.p12),。pem密钥文件,但仍无法实现我的主要目标:

  • 使用PKCS#1 v1.5和SHA1
  • 对字符串摘要进行签名
  • 使用从服务器(JSON)收到的字符串base64密钥,而不是使用证书文件。

我的私钥是PKCS#1私钥,格式如下:

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCx9YWznzT3irAArr+INM5m0i6UCNICq4E8yrWwPbGh8/kdU/oh
.....   .....   .....   .....   .....   .....   .....   .....   
eF9lWooBNGgSh5vmkgECQGJwmDLKohSKEtVwGOIp3S3j+CHs0vVnznmtmC9sfrj4
ef48Sx1KFI8iQa3Nfv5bokaJkiIVVx/eMaa96Vracjc=
-----END RSA PRIVATE KEY-----

最终我搬到了OpenSSL,事情变得更加光明。 所以整个过程如下:

  1. 下载并构建iOS OpenSSL library
  2. 将lib添加到项目中。
  3. 使用OpenSSL最终签署消息。

  4. 第1步:OpenSSL库

    https://github.com/x2on/OpenSSL-for-iPhone

    下载图书馆

    将build-libssl.sh复制到项目文件夹 使用终端运行以下命令:

    cd [your project folder]
    /build-libssl.sh
    

    第2步:添加OpenSSL

    将“include”文件夹从OpenSSL复制到项目文件夹

    将libcrypto * .a和libss * .a文件拖放到XCode文件夹中

    打开“目标”的构建设置

    Library Search Paths更改为$(inherited) “$(SRCROOT)”

    User Header Search Paths更改为include

    激活Always Search User Paths

    第3步:签名

    #include <openssl/pem.h>
    #include <openssl/engine.h>
    #include <iomanip>
    
    - (NSString*) signHeader:(NSString*) pTextString withPrivateKey: (NSString*) pPrivateKey {
    
        int retEr;
        char* text = (char*) [pTextString UTF8String];
        unsigned char *data;
        unsigned int dataLen;
    
        // converting nsstring base64 private key to openssl RSA key
    
        BIO *mem = NULL;
        RSA *rsa_private = NULL;
        char *private_key = (char*)[pPrivateKey UTF8String];
    
        mem = BIO_new_mem_buf(private_key, strlen(private_key));
        if (mem == NULL)
        {
            char buffer[120];
            ERR_error_string(ERR_get_error(), buffer);
            fprintf(stderr, "OpenSSL error: %s", buffer);
            exit(0);
        }
    
        rsa_private = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL);
        BIO_free (mem);
        if (rsa_private == NULL)
        {
            char buffer[120];
            ERR_error_string(ERR_get_error(), buffer);
            fprintf(stderr, "OpenSSL error: %s", buffer);
            exit(0);
        }
        // end of convertion
    
        data = (unsigned char *) text;
        dataLen = strlen(text);
    
        //// creating signature
        // sha1
        unsigned char hash[SHA_DIGEST_LENGTH];
        unsigned char sign[128];
        unsigned int signLen;
    
        SHA1(data, dataLen, hash);
    
        //  signing
        retEr = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sign, &signLen, rsa_private);
    
        //  printf("Signature len gth = %d\n", signLen);
        printf("RSA_sign: %s\n", (retEr == 1) ? "RSA_sign success" : "RSA_sign error");
    
        //  convert unsigned char -> std:string
        std::stringstream buffer;
        for (int i = 0; i < 128; i++)
        {
            buffer << std::hex << std::setfill('0');
            buffer << std::setw(2)  << static_cast<unsigned>(sign[i]);
        }
        std::string signature = buffer.str();
    
        //  convert std:string -> nsstring
        NSString *signedMessage = [NSString stringWithCString:signature.c_str() encoding:[NSString defaultCStringEncoding]];
    
        RSA_free(rsa_private);
    
        return signedMessage;
    }
    

    如果您想使用SHA256或任何其他SHA,您必须更改以下内容:

    SHA_DIGEST_LENGTH => SHA256_DIGEST_LENGTH
    sign[128] => sign[256]
    SHA1(data, dataLen, hash) => SHA256(data, dataLen, hash)
    NID_sha1 => NID_sha256
    

    替代步骤3:签名(Swift)

    由于Swift语言不是C ++的超集,因此无法直接与C ++结合使用,因此需要创建一个C ++代码的Objective-C包装器,然后从Swift代码中调用它(Obj-C)。

    步骤3.1

    为您的C ++代码创建一个Obj-C类。 重要提示:该文件必须为.mm扩展名或设置类型为Objective-C++ Source

    <强> OpenSSLWrapper.h

    #import <Foundation/Foundation.h>
    
    @interface OpenSSLWrapper : NSObject
    + (NSString*) signHeader:(NSString*) pTextString withPrivateKey: (NSString*) pPrivateKey;
    @end
    

    <强> OpenSSLWrapper.mm

    #import "OpenSSLWrapper.h"
    
    #include <openssl/pem.h>
    #include <openssl/engine.h>
    #include <iomanip>
    
    @implementation OpenSSLWrapper
    
    + (NSString*) signHeader:(NSString*) pTextString withPrivateKey: (NSString*) pPrivateKey {
    
        int retEr;
        char* text = (char*) [pTextString UTF8String];
        unsigned char *data;
        unsigned int dataLen;
    
        // converting nsstring base64 private key to openssl RSA key
    
        BIO *mem = NULL;
        RSA *rsa_private = NULL;
        char *private_key = (char*)[pPrivateKey UTF8String];
    
        mem = BIO_new_mem_buf(private_key, strlen(private_key));
        if (mem == NULL)
        {
            char buffer[120];
            ERR_error_string(ERR_get_error(), buffer);
            fprintf(stderr, "OpenSSL error: %s", buffer);
            exit(0);
        }
    
        rsa_private = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL);
        BIO_free (mem);
        if (rsa_private == NULL)
        {
            char buffer[120];
            ERR_error_string(ERR_get_error(), buffer);
            fprintf(stderr, "OpenSSL error: %s", buffer);
            exit(0);
        }
        // end of convertion
    
        data = (unsigned char *) text;
        dataLen = strlen(text);
    
        //// creating signature
        // sha1
        unsigned char hash[SHA_DIGEST_LENGTH];
        unsigned char sign[128];
        unsigned int signLen;
    
        SHA1(data, dataLen, hash);
    
        //  signing
        retEr = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sign, &signLen, rsa_private);
    
        //  printf("Signature len gth = %d\n", signLen);
        printf("RSA_sign: %s\n", (retEr == 1) ? "RSA_sign success" : "RSA_sign error");
    
        //  convert unsigned char -> std:string
        std::stringstream buffer;
        for (int i = 0; i < 128; i++)
        {
            buffer << std::hex << std::setfill('0');
            buffer << std::setw(2)  << static_cast<unsigned>(sign[i]);
        }
        std::string signature = buffer.str();
    
        //  convert std:string -> nsstring
        NSString *signedMessage = [NSString stringWithCString:signature.c_str() encoding:[NSString defaultCStringEncoding]];
    
        RSA_free(rsa_private);
    
        return signedMessage;
    }
    
    @end
    

    步骤3.2

    创建一个Bridging-Header文件。

    <强> YourProject-桥接-Header.h

    #import "OpenSSLWrapper.h"
    

    步骤3.3

    在Swift文件中使用Obj-C中的方法。

    <强> DigestSignature.swift

    import Cocoa
    
    class DigestSignature: NSObject {
        let privateKey = "-----BEGIN RSA PRIVATE KEY-----MIICXAIBAAKBgQCx9YWznzT3irAArr+INM5m0i6UCNICq4E8yrWwPbGh8/kdU/oh  .....   .....  eF9lWooBNGgSh5vmkgECQGJwmDLKohSKEtVwGOIp3S3j+CHs0vVnznmtmC9sfrj4ef48Sx1KFI8iQa3Nfv5bokaJkiIVVx/eMaa96Vracjc=-----END RSA PRIVATE KEY-----"
        var digest: String = OpenSSLWrapper.signHeader("Hello World", withPrivateKey: privateKey) ;
    
    }
    

    在结果中,您有一个十六进制NSString *签名摘要。

    希望有所帮助