11
votes

I have a TCP socket server and I want to do the following w/o using SSL:

  1. On server, make RSA key pair (I know how to do this using openssl's crypto library)
  2. On server, send the public key to iphone and keep the private key.
  3. On client(iphone), want to encrypt a message using the public key, using SecKeyEncrypt.
  4. On server, decrypt the message.

The message is short enough so that the PKCS1 padded result fits into 128 bytes.

I don't know how to do 2~4. Anyone knows?

2
Not a question as far as I can see. - President James K. Polk
Sorry Greg. I just added a question. - Stanley 시크 Yeo
This won't be secure without SSL; it will be subject to a man in the middle attack. - cobbal
Thanks cobbal. I know about that. I am not trying to do those above for security related reason. - Stanley 시크 Yeo
If it's not for security reasons, then why are you encrypting data? Sounds like an XY problem: you think that you should be using RSA encryption, and have a problem implementing it, but from the information that you provide later it becomes clear that you really have another problem. - MSalters

2 Answers

18
votes

This should do what you're asking - it encrypts data with the server's public key. It's not subject to MITM attacks, unless the attacker has a copy of your private key and its password (communicating via non-SSL, however, still is, but the data you encrypt with the server's legit public key will be nearly impossible to decrypt).

I cobbled this together from Apple's docs, this site, the Apple developer forums and probably elsewhere. So thanks to everyone I cribbed code from! This code assumes several things:

  1. You've already generated your RSA key pairs (I'm using a 4096-bit key and it seems speedy enough) and, using the private key, created a DER-encoded certificate called "cert.cer" that you put in your resource bundle of your app (obviously, you can also download the cert from your server, but then you're open to MITM attacks again). By default, OpenSSL generates a PEM encoded cert, so you have to convert it with "openssl x509 -in cert.pem -inform PEM -out cert.cer -outform DER". iOS will barf on PEM. The reason I use a cert is it's actually easier to work with, and is supported in iOS. Using just the public key isn't (though it can be done).

  2. You've added Security.framework to your project and you #import <Security/Security.h>.

/* Returns an NSData of the encrypted text, or nil if encryption was unsuccessful.
Takes the X.509 certificate as NSData (from dataWithContentsOfFile:, for example) */

+(NSData *)encryptString:(NSString *)plainText withX509Certificate:(NSData *)certificate {

    SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificate);
    SecPolicyRef policy = SecPolicyCreateBasicX509();
    SecTrustRef trust;
    OSStatus status = SecTrustCreateWithCertificates(cert, policy, &trust);

    SecTrustResultType trustResult;
    if (status == noErr) {
        status = SecTrustEvaluate(trust, &trustResult);
    }

    SecKeyRef publicKey = SecTrustCopyPublicKey(trust);

    const char *plain_text = [plainText UTF8String];
    size_t blockSize = SecKeyGetBlockSize(publicKey);
    NSMutableData *collectedCipherData = [NSMutableData data];

    BOOL success = YES;
    size_t cipherBufferSize = blockSize;
    uint8_t *cipherBuffer = malloc(blockSize);

    int i;
    for (i = 0; i < strlen(plain_text); i += blockSize-11) {
        int j;
        for (j = 0; j < blockSize-11 && plain_text[i+j] != '\0'; ++j) {
            cipherBuffer[j] = plain_text[i+j];
        }

        int result;
        if ((result = SecKeyEncrypt(publicKey, kSecPaddingPKCS1, cipherBuffer, j, cipherBuffer, &cipherBufferSize)) == errSecSuccess) {
            [collectedCipherData appendBytes:cipherBuffer length:cipherBufferSize];
        } else {
            success = NO;
            break;
        }
    }

    /* Free the Security Framework Five! */
    CFRelease(cert);
    CFRelease(policy);
    CFRelease(trust);
    CFRelease(publicKey);
    free(cipherBuffer);

    if (!success) {
        return nil;
    }

    return [NSData dataWithData:collectedCipherData];
}
1
votes

Well, I guess you would need to publish your public key to the outside world (the iPhone in your case). To do the best way is to publish a certificate conatining your public key, and the iPhone app can download it. Then the iPhone application can utilize the principle of PGP to encrypt the data with a symmetric algo (like AES) and encrypt the symmetric with the public key. The application in the server would receive the message, decrypt the symmetric key with its private key, and the then decrypt the encrypted data with the symmetric key thus obtained.

But as cobbal said, anyone can intercept the message in between the server and the iPhone and can change it, and the server would not know if its has 'actually' recieved the data from the iPHone, unless you sign it using an SSL certificate (i.e. encrypt the hash of the method with the private key of the iPhone).

My suggestion is, use availale third party application rather than doing it yourself, as they might be some falw in tghe implementation. PGP is a public available library, u can use.