2012年1月3日星期二

Saving RSA keys in files from iPhone application

Saving RSA keys in files from iPhone application
This is my first post which I wanted to write after I get some experience in generating RSA keys on iPhone. I hope it can help some people to avoid troubles which we got while working with RSA keys.

This post describes how to create RSA-keys in iPhone-application. In Apple documentation we can find how to make key pair with usage of standard frameworks (Security framework and CommonCrypto library). Also it has an good example with implementation of SecKeyWrapper class, which represents interface to all functionality that necessary for symmetric and asymmetric keys creation in application running under iOS.

However this functionality does not cover cases of getting and saving public and private keys in separate files, how it does keygen utility and some others.

We have a task to authorize on ssh-server with RSA keys from iPhone application, using libssh2 library for iPhone. This library allows such authorization in case if keys are written in files. Googling has no any efficient result. Even Apple documentation does not give complete answer for question: "How do we save generated RSA keys in files?".

From SecKeyWrapper class we can get bits for both keys, but if we just simply save them into files authorization won't be successful. When we checked keys with usage PUTTY utility we found out that private key is correct. However public key we were saving incorrectly. During following investigation of the problem we find the solution. Public key consists from Module and Exponent which we can get from key bits. And Module is the part that must be written in public key file.
Nowhere was paid attention for this moment so task took so long time for implementation.

The following code shows base moments of creating files with RSA keys from iPhone application:

1. Key generation:
   SecKeyWrapper * keyWrapper = [SecKeyWrapper sharedWrapper];
    [keyWrapper generateKeyPair:1024]; // length might be 512, 1024 and 2048.

2. Creating paths for key files:
    NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString * pathForPublicKey = [paths objectAtIndex:0];
    NSString * pathForPrivateKey = [paths objectAtIndex:0];
    [[NSFileManager defaultManager] createDirectoryAtPath:[pathForPrivateKey stringByAppendingPathComponent:@".ssh"]
                              withIntermediateDirectories:YES
                                               attributes:nil
                                                    error:nil];
    pathForPublicKey = [pathForPublicKey stringByAppendingPathComponent:@".ssh/id_rsa.pub"];
    pathForPrivateKey = [pathForPrivateKey stringByAppendingPathComponent:@".ssh/id_rsa"];

3. Getting bits for public key and generating a string to write:
    char length[4] = {0,0,0,7};
    NSMutableData * data = [NSMutableData dataWithBytes:length length:4];
    NSString * stringToWriteInFile = @"ssh-rsa";
    [data appendData:[stringToWriteInFile dataUsingEncoding:NSUTF8StringEncoding]];
    length[3] = 3;
    [data appendBytes:length length:4];
    char version[3] = {1,0,1};
    [data appendBytes:version length:3];
    length[3] = [[keyWrapper getPublicKeyMod] length];
    [data appendBytes:length length:4];
    [data appendData:[keyWrapper getPublicKeyMod]];


4. Generating string to write into file:
    stringToWriteInFile = @"ssh-rsa ";
    stringToWriteInFile = [stringToWriteInFile stringByAppendingString:[data base64EncodingWithLineLength:0]];
    stringToWriteInFile = [stringToWriteInFile stringByAppendingString:@" some.hostaddress.com\n"];

5. Write key into file:
[stringToWriteInFile writeToFile:pathForPublicKey atomically:YES encoding:NSUTF8StringEncoding error:nil];

6. The same for private key (but more simple):
    stringToWriteInFile = @"-----BEGIN RSA PRIVATE KEY-----\n";
    stringToWriteInFile = [stringToWriteInFile stringByAppendingString:[[keyWrapper getPrivateKeyBits] base64EncodingWithLineLength:64]];
    stringToWriteInFile = [stringToWriteInFile stringByAppendingString:@"\n-----END RSA PRIVATE KEY-----\n"];
    [stringToWriteInFile writeToFile:pathForPrivateKey atomically:YES encoding:NSUTF8StringEncoding error:nil];

NSNumber * permission = [NSNumber numberWithLong:0x0700]; // change attributes for sending via sftp.
    NSDictionary * dict = [NSDictionary dictionaryWithObject:permission forKey:NSFilePosixPermissions];
    [[NSFileManager defaultManager] setAttributes:dict ofItemAtPath:pathForPrivateKey error:nil];


7. Transferring of public key via sftp:
8. And authorization with RSA keys:

Points 7 and 8 are well described in libssh2 documentation's example http://www.libssh2.org/examples/sftp_append.html

Also for successful key file creation you should:
1. Add Security and other necessary framework to your project.
2. Add SecKeyWrapper class sources from Apple's example to your project.
   Here is a link: http://developer.apple.com/library/ios/#samplecode/CryptoExercise/Introduction/Intro.html
3. Add the following code to SecKeyWrapper:
prototypes into SecKeyWrapper.h file:

- (NSData*)getPrivateKeyBits;
- (NSData *)getPublicKeyExp;
- (NSData *)getPublicKeyMod;

stubs for simulator into SecKeyWrapper.m file:

- (NSData *)getPublicKeyBits { return nil; }
- (NSData *)getPublicKeyExp { return nil; }
- (NSData *)getPublicKeyMod { return nil; }
- (int)derEncodingGetSizeFrom:(NSData*)buf at:(int*)iterator { return 0; }

and implementation of methods:

#pragma mark -
#pragma mark improvements for creation key files

- (NSData *)getPrivateKeyBits {
OSStatus sanityCheck = noErr;
NSData * privateKeyBits = nil;
NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];

// Set the private key query dictionary.
[queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPrivateKey setObject:privateTag forKey:(id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
 
// Get the key bits.
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyBits);
if (sanityCheck != noErr)
{
privateKeyBits = nil;
}
[queryPrivateKey release];

return privateKeyBits;
}

- (int)derEncodingGetSizeFrom:(NSData*)buf at:(int*)iterator
{
  const uint8_t* data = [buf bytes];
  int itr = *iterator;
  int num_bytes = 1;
  int ret = 0;
 
  if (data[itr] > 0x80) {
    num_bytes = data[itr] - 0x80;
    itr++;
  }
 
  for (int i = 0 ; i < num_bytes; i++) ret = (ret * 0x100) + data[itr + i];
 
  *iterator = itr + num_bytes;
  return ret;
}

- (NSData *)getPublicKeyExp
{
  NSData* pk = [self getPublicKeyBits];
  if (pk == NULL) return NULL;

  int iterator = 0;

  iterator++; // TYPE - bit stream - mod + exp
  [self derEncodingGetSizeFrom:pk at:&iterator]; // Total size

  iterator++; // TYPE - bit stream mod
  int mod_size = [self derEncodingGetSizeFrom:pk at:&iterator];
  iterator += mod_size;

  iterator++; // TYPE - bit stream exp
  int exp_size = [self derEncodingGetSizeFrom:pk at:&iterator];

  return [pk subdataWithRange:NSMakeRange(iterator, exp_size)];
}

- (NSData *)getPublicKeyMod
{
  NSData* pk = [self getPublicKeyBits];
  if (pk == NULL) return NULL;

  int iterator = 0;

  iterator++; // TYPE - bit stream - mod + exp
  [self derEncodingGetSizeFrom:pk at:&iterator]; // Total size

  iterator++; // TYPE - bit stream mod
  int mod_size = [self derEncodingGetSizeFrom:pk at:&iterator];

  return [pk subdataWithRange:NSMakeRange(iterator, mod_size)];
}



That's all.

没有评论:

发表评论