2

In my app i have to sign a message with an RSA private key i already have. 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?

Roman Slyepko
  • 1,393
  • 2
  • 8
  • 15
Zoltan Varadi
  • 2,468
  • 2
  • 34
  • 51

2 Answers2

3

iOS has a C API for doing this. 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. Then the function is SecKeyRawSign to sign your string.

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.

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. For instance é can be represented as a single Unicode number or as the Unicode acute accent followed by the letter e.

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

JeremyP
  • 84,577
  • 15
  • 123
  • 161
3

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:

  • Sign a string-digest using PKCS#1 v1.5 and SHA1
  • Use string base64 key received from server (JSON) instead of using certificate-files.

My private key is a PKCS#1 private key formatted like this:

-----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. So the whole process looks like the following:

  1. Download and build iOS OpenSSL library.
  2. Add the lib to the project.
  3. Use OpenSSL to finally sign the message.

Step 1: OpenSSL library

Download the library from https://github.com/x2on/OpenSSL-for-iPhone

Copy build-libssl.sh to your project folder Using Terminal run the following:

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

Step 2: adding OpenSSL

Copy the “include” folder from OpenSSL into your project-folder

Drag-copy the libcrypto*.a and libss*.a files into your XCode Folder

Open the Build Settings of your “Target”

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

Change User Header Search Paths to include

Activate Always Search User Paths

Step 3: Signing

#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:

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)

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.

Step 3.1

Create an Obj-C class for your C++ code. IMPORTANT: the file must be either with .mm extension or set type to 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

Step 3.2

Create a Bridging-Header file.

YourProject-Bridging-Header.h

#import "OpenSSLWrapper.h"

Step 3.3

Use the method from Obj-C in your Swift file.

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.

Hope that helps

Community
  • 1
  • 1
Roman Slyepko
  • 1,393
  • 2
  • 8
  • 15
  • Basically this.. OpenSSL was for me the only way to go as well.. That said, it is not a easy job to set up correctly. Only thing I can recommend, are links. This one is to build lib -> https://gist.github.com/foozmeat/5154962 and to use it, do something like this https://gist.github.com/dam5s/3444704 – Yanchi Jan 14 '15 at 14:34
  • What would the Swift version of Step3. be? – Geoff H Mar 08 '16 at 12:07
  • 1
    @GeoffH Swift isn't a superset of C++, therefore you can't mix Swift and C++ code in one .swift file. You should create a separate Obj-C file-wrapper with this method and import it to your .swift files – Roman Slyepko Mar 09 '16 at 11:09
  • Thanks Roma, is there any chance you would be able to show how this should be wrapped (.h &.mm ?) :/ – Geoff H Mar 09 '16 at 11:44
  • 2
    @GeoffH done! Step 3 updated with a Swift workaround – Roman Slyepko Mar 09 '16 at 16:19
  • @RomanSlyepko, can you provide a sample project for this.It will be so helpful – Thiha Aung Nov 08 '16 at 07:26
  • @RomanSlyepko , I am getting this error : https://puu.sh/sauUT/b09dc776b1.png . Any Help? – Thiha Aung Nov 08 '16 at 08:41
  • @ThihaAung did you write the includes? #include #include #include – Roman Slyepko Nov 08 '16 at 13:24