简体   繁体   English

使用RSA私钥对字符串签名

[英]Sign a string with an RSA private key

In my app i have to sign a message with an RSA private key i already have. 在我的应用程序中,我必须使用已经拥有的RSA私钥对消息签名。 After this done, i want to use the signature later. 完成此操作后,我想稍后使用签名。 I've checked several libraries, but all of them do much more that i need. 我已经检查了几个库,但是它们都做了很多我需要做的事情。 This is what i'd like to do: 这就是我想做的:

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

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

How would 怎么会

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

look like? 看起来像?

iOS has a C API for doing this. iOS具有执行此操作的C API。 I've never used it myself, but it looks like you need a PKCS12 formatted file with the private key in it which you import with SecKeyPKCS12Imort and get the private key out of. 我自己从未使用过它,但是看起来您需要一个包含私钥的PKCS12格式的文件,您可以使用SecKeyPKCS12Imort导入该文件并取出私钥。 Then the function is SecKeyRawSign to sign your string. 然后,该函数是SecKeyRawSign以对您的字符串进行签名。

The string should first be converted to an NSData object using a known character encoding - probably UTF-8 and the bytes in the NSData signed. 首先应使用已知的字符编码将字符串转换为NSData对象-可能为UTF-8,并对NSData中的字节进行签名。

If you use non-ASCII characters in your string, as well as making sure you use a well defined encoding, you also need to be careful of how some characters are represented. 如果在字符串中使用非ASCII字符,并确保使用定义良好的编码,则还需要注意某些字符的表示方式。 For instance é can be represented as a single Unicode number or as the Unicode acute accent followed by the letter e. 例如,é可以表示为单个Unicode数字,也可以表示为Unicode重音符号,后跟字母e。

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

I've tried millions of signing methods, that include Keychain, extracting SecKeyRef's, certificates (.p12), .pem key-files, but still didn't manage to achieve my main goal: 我已经尝试了数百万种签名方法,包括钥匙串,提取SecKeyRef,证书(.p12)、. pem密钥文件,但仍然无法实现我的主要目标:

  • Sign a string-digest using PKCS#1 v1.5 and SHA1 使用PKCS#1 v1.5和SHA1签署字符串摘要
  • Use string base64 key received from server (JSON) instead of using certificate-files. 使用从服务器(JSON)接收到的字符串base64密钥,而不使用证书文件。

My private key is a PKCS#1 private key formatted like this: 我的私钥是一个格式如下的PKCS#1私钥:

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

Eventually I moved to OpenSSL and things got much brighter. 最终,我搬到了OpenSSL,事情变得更加光明。 So the whole process looks like the following: 因此,整个过程如下所示:

  1. Download and build iOS OpenSSL library . 下载并构建iOS OpenSSL库
  2. Add the lib to the project. 将库添加到项目。
  3. Use OpenSSL to finally sign the message. 使用OpenSSL最终对消息签名。

Step 1: OpenSSL library 步骤1:OpenSSL库

Download the library from https://github.com/x2on/OpenSSL-for-iPhone https://github.com/x2on/OpenSSL-for-iPhone下载该库

Copy build-libssl.sh to your project folder Using Terminal run the following: 将build-libssl.sh复制到您的项目文件夹中使用Terminal运行以下命令:

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

Step 2: adding OpenSSL 步骤2:添加OpenSSL

Copy the “include” folder from OpenSSL into your project-folder 将OpenSSL中的“ include”文件夹复制到您的项目文件夹中

Drag-copy the libcrypto*.a and libss*.a files into your XCode Folder 将libcrypto * .a和libss * .a文件拖放到XCode文件夹中

Open the Build Settings of your “Target” 打开“目标”的构建设置

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

Change User Header Search Paths to include 更改User Header Search Pathsinclude

Activate Always Search User Paths 激活Always Search User Paths

Step 3: Signing 步骤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;
}

If you want to use SHA256 or any other SHA you must change the following: 如果要使用SHA256或任何其他SHA,则必须更改以下内容:

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

Alternative Step 3: Signing (Swift) 替代步骤3:签名(快速)

Since Swift language isn't a superset of C++ it can't be combined with C++ directly, therefore you need to create a Objective-C wrapper of C++ code and then call it (Obj-C) from your Swift code. 由于Swift语言不是C ++的超集,因此不能直接与C ++组合,因此您需要创建一个C ++代码的Objective-C包装器,然后从您的Swift代码中调用它(Obj-C)。

Step 3.1 步骤3.1

Create an Obj-C class for your C++ code. 为您的C ++代码创建一个Obj-C类。 IMPORTANT: the file must be either with .mm extension or set type to Objective-C++ Source 重要说明:该文件必须具有.mm扩展名或将类型设置为Objective-C++ Source

OpenSSLWrapper.h OpenSSLWrapper.h

#import <Foundation/Foundation.h>

@interface OpenSSLWrapper : NSObject
+ (NSString*) signHeader:(NSString*) pTextString withPrivateKey: (NSString*) pPrivateKey;
@end

OpenSSLWrapper.mm 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

Step 3.2 步骤3.2

Create a Bridging-Header file. 创建一个桥接头文件。

YourProject-Bridging-Header.h YourProject桥接,Header.h

#import "OpenSSLWrapper.h"

Step 3.3 步骤3.3

Use the method from Obj-C in your Swift file. 在您的Swift文件中使用Obj-C中的方法。

DigestSignature.swift 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) ;

}

In the result, you have a hex NSString* signed digest. 结果,您得到一个十六进制的NSString *带符号的摘要。

Hope that helps 希望能有所帮助

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM