Question

I'm trying to generate a CSR in iOS. Since apparently the Apple security framework for iOS doesn't include methods for CSR generation I had to compile the OpenSSL source code for my project.

Now I want to know how to use these methods with the keys I've generated in the Keychain previously. That is, I need to convert SecKeyRef type into OpenSSL types like EVP_PKEY. That will allow me to call the OpenSSL method X509_REQ_set_pubkey.

Does anyone know a way to achieve this?

Was it helpful?

Solution

Found the solution myself.

First of all you need to extract the key from the Keychain as NSData.

- (NSData *) getKeyDataWithIdentifier:(NSString *) identifier
{
    NSData * keyBits = nil;
    NSMutableDictionary * keyQuery = [[NSMutableDictionary alloc] init];
    NSData * encodedId = [identifier dataUsingEncoding:NSUTF8StringEncoding];
    [keyQuery setObject:encodedId forKey:kSecAttrApplicationTag];
    [keyQuery setObject:kSecClassKey forKey:kSecClass];
    [keyQuery setObject:[NSNumber numberWithBool:YES] forKey:kSecReturnData];
    [keyQuery setObject:kSecAttrKeyTypeRSA forKey:kSecAttrKeyType];

    OSStatus sanityCheck = SecItemCopyMatching((CFDictionaryRef)keyQuery, (CFTypeRef *)&keyBits);

    if (sanityCheck != noErr) {
        NSLog(@"Error: %ld", sanityCheck);
    }

    return keyBits;
}

Now we need to cast this data as unsigned char and give it to the method d2i_RSAPublicKey

- (void) generateCSR:(NSData *) keyData
{
    X509_REQ *req = NULL;
    X509_NAME *name= NULL;
    EVP_PKEY *key;
    const unsigned char * bits = (unsigned char *) [keyData bytes];
    int length = [keyData length];

    if ((req=X509_REQ_new()) == NULL) {
        NSLog(@"big error");
        return;
    }

    RSA * rsa = NULL;
    key=EVP_PKEY_new();
    d2i_RSAPublicKey(&rsa, &bits, length);
    EVP_PKEY_assign_RSA(key,rsa);
    name = X509_REQ_get_subject_name(req);
    X509_REQ_set_pubkey(req, key);

    /* This function creates and adds the entry, working out the
     * correct string type and performing checks on its length.
     * Normally we'd check the return value for errors...
             */
    X509_NAME_add_entry_by_txt(name,"CN",
                               MBSTRING_ASC, "My certificate request", -1, -1, 0);
    X509_REQ_print_fp(stdout, req);
}

That generates a simple CSR in OpenSSL (not signed) with a public key and a common name and prints it to the standard out.

OTHER TIPS

Well, the keystore stuff wasn't working out right for us, so we generated them and stored them as files. If anyone ever needs this, I'll leave it here.

+ (void)generateCsrAndKeyAtPath:(NSString *)csrPath KeyPath:(NSString *)keyPath Username:(NSString *)username {
    int i;
    RSA *rsakey;
    X509_REQ *req;
    X509_NAME *subj;
    EVP_PKEY *pkey;
    EVP_MD *digest;
    FILE *fp;

    structentry[ENTRIES - 1].value = [username UTF8String];

    // standard set up for OpenSSL
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();

    // Generate the RSA key; we don't assign a callback to monitor progress
    // since generating keys is fast enough these days
    rsakey = RSA_generate_key(2048, RSA_F4, NULL, NULL);

    // Create evp obj to hold our rsakey
    if (!(pkey = EVP_PKEY_new()))
        NSLog(@"Could not create EVP object");

    if (!(EVP_PKEY_set1_RSA(pkey, rsakey)))
        NSLog(@"Could not assign RSA key to EVP object");

    // create request object
    if (!(req = X509_REQ_new()))
        NSLog(@"Failed to create X509_REQ object");
    X509_REQ_set_pubkey(req, pkey);

    // create and fill in subject object
    if (!(subj = X509_NAME_new()))
        NSLog(@"Failed to create X509_NAME object");

    for (i = 0; i < ENTRIES; i++) {
        int nid;                  // ASN numeric identifier
        X509_NAME_ENTRY *ent;

        if ((nid = OBJ_txt2nid(structentry[i].key)) == NID_undef) {
            fprintf(stderr, "Error finding NID for %s\n", structentry[i].key);
            NSLog(@"Error on lookup");
        }
        if (!(ent = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC,
                structentry[i].value, -1)))
            NSLog(@"Error creating Name fewfwefewf from NID");

        if (X509_NAME_add_entry(subj, ent, -1, 0) != 1)
            NSLog(@"Error adding fewfwefewf to Name");
    }
    if (X509_REQ_set_subject_name(req, subj) != 1)
        NSLog(@"Error adding subject to request");

    // request is filled in and contains our generated public key;
    // now sign it
    digest = (EVP_MD *) EVP_sha1();

    if (!(X509_REQ_sign(req, pkey, digest)))
        NSLog(@"Error signing request");

    // write output files
    if (!(fp = fopen([csrPath UTF8String], "wb")))
        NSLog(@"Error writing to request file");
    if (PEM_write_X509_REQ(fp, req) != 1)
        NSLog(@"Error while writing request");
    fclose(fp);

    if (!(fp = fopen([keyPath UTF8String], "w")))
        NSLog(@"Error writing to private key file");
    if (PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, 0, NULL) != 1)
        NSLog(@"Error while writing private key");
    fclose(fp);

    EVP_PKEY_free(pkey);
    X509_REQ_free(req);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top