How to Encrypt and Decrypt a String(Plain Text) with Rsa Public Key in iOS, Swift

how to encrypt and decrypt a String(Plain Text) with RSA public key in ios, swift

Hope this will help you:

let serverPublicKey = "Some text with key"

let data2 = Data.init(base64Encoded: serverPublicKey)

let keyDict:[NSObject:NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: NSNumber(value: 2048),
kSecReturnPersistentRef: true as NSObject
]

let publickeysi = SecKeyCreateWithData(data2! as CFData, keyDict as CFDictionary, nil)

//Encrypt a string with the public key
let message = "This is my message."
let blockSize = SecKeyGetBlockSize(publickeysi!)
var messageEncrypted = [UInt8](repeating: 0, count: blockSize)
var messageEncryptedSize = blockSize

var status: OSStatus!

status = SecKeyEncrypt(publickeysi!, SecPadding.PKCS1, message, message.characters.count, &messageEncrypted, &messageEncryptedSize)

if status != noErr {
print("Encryption Error!")
return
}

RSA Encryption function in Swift 4 from Public Key string

You can do like this way...

static func encrypt(string: String, publicKey: String?) -> String? {
guard let publicKey = publicKey else { return nil }

let keyString = publicKey.replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----\n", with: "").replacingOccurrences(of: "\n-----END PUBLIC KEY-----", with: "")
guard let data = Data(base64Encoded: keyString) else { return nil }

var attributes: CFDictionary {
return [kSecAttrKeyType : kSecAttrKeyTypeRSA,
kSecAttrKeyClass : kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits : 2048,
kSecReturnPersistentRef : kCFBooleanTrue] as CFDictionary
}

var error: Unmanaged<CFError>? = nil
guard let secKey = SecKeyCreateWithData(data as CFData, attributes, &error) else {
print(error.debugDescription)
return nil
}
return encrypt(string: string, publicKey: secKey)
}

static func encrypt(string: String, publicKey: SecKey) -> String? {
let buffer = [UInt8](string.utf8)

var keySize = SecKeyGetBlockSize(publicKey)
var keyBuffer = [UInt8](repeating: 0, count: keySize)

// Encrypto should less than key length
guard SecKeyEncrypt(publicKey, SecPadding.PKCS1, buffer, buffer.count, &keyBuffer, &keySize) == errSecSuccess else { return nil }
return Data(bytes: keyBuffer, count: keySize).base64EncodedString()
}

Encrypt using RSA Certificate in Swift

The code provided in question doesn't extract the Key from certificate. Rather, it tries to create the SecKey using the certificate string itself. The correct way to do the same is to create a SecCertificate object from the certificate data, then create a SecTrust using the certificate data. Then using the trust, copy the public key to make SecKey object.

The final code will look something like this:

import Foundation
import Security

struct RSA {

static func encrypt(string: String, certificate: String?) -> String? {
guard let certificate = certificate else { return nil }

let certificateString = certificate.replacingOccurrences(of: "-----BEGIN CERTIFICATE-----", with: "").replacingOccurrences(of: "-----END CERTIFICATE-----", with: "").replacingOccurrences(of: "\r", with: "").replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: "\t", with: "").replacingOccurrences(of: " ", with: "")
print(certificateString)

// Convert the certificate string to Data
guard let data = Data(base64Encoded: certificateString) else { return nil }
print(data)

// Create SecCertificate object using certificate data
guard let cer = SecCertificateCreateWithData(nil, data as NSData) else { return nil }

var trust: SecTrust?

// Retrieve a SecTrust using the SecCertificate object. Provide X509 as policy
let status = SecTrustCreateWithCertificates(cer, SecPolicyCreateBasicX509(), &trust)

// Check if the trust generation is success
guard status == errSecSuccess else { return nil }

// Retrieve the SecKey using the trust hence generated
guard let secKey = SecTrustCopyPublicKey(trust!) else { return nil }

return encrypt(string: string, publicKey: secKey)
}

static func encrypt(string: String, publicKey: SecKey) -> String? {
let buffer = [UInt8](string.utf8)

var keySize = SecKeyGetBlockSize(publicKey)
var keyBuffer = [UInt8](repeating: 0, count: keySize)

// Encrypto should less than key length
guard SecKeyEncrypt(publicKey, SecPadding.PKCS1, buffer, buffer.count, &keyBuffer, &keySize) == errSecSuccess else { return nil }
return Data(bytes: keyBuffer, count: keySize).base64EncodedString()
}
}

var pemString = "-----BEGIN CERTIFICATE-----##Base 64 encoded certificate string##-----END CERTIFICATE-----"

let password = "abcde"
let encryptedPassword = RSA.encrypt(string: password, certificate: pemString)
print(encryptedPassword as Any)

The only change is in the static func encrypt(string: String, certificate: String?) -> String? function.

Swift Load RSA Public Key from String (MacOS)

The key data you have is PEM encoded. However, Apple supports DER encoding for their security API. So first we'll have to transform your key data to the correct format. The example below is created with a random RSA key exporting the public key to PEM format. PEM headers can differ from library to library, so be sure you remove the tags, before continuing to the DER transformation.

var pem = "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAs6AofVx+UAXcVjnIU0Z5SAGO/LPlTunA9zi7jNDcIrZTR8ULHTrm\naSAg/ycNR1/wUeac617RrFeQuoPSWjhZPRJrMa3faVMCqTgV2AmaPgKnPWBrY2ir\nGhnCnIAvD3sitCEKultjCstrTA71Jo/BuVaj6BVgaA/Qn3U9mQ+4JiEFiTxy4kOF\nes1/WwTLjRQYVf42oG350bTKw9F0MklTTZdiZKCQtc3op86A7VscFhwusY0CaZfB\nlRDnTgTMoUhZJpKSLZae93NVFSJY1sUANPZg8TzujqhRKt0g5HR/Ud61icvBbcx8\n+a3NzmuwPylvp5m6hz/l14Y7UZ8UT5deywIDAQAB\n-----END RSA PUBLIC KEY-----\n"

// Remove headers and footers from the PEM, leaving us with DER encoded data split by new lines
[
"-----BEGIN RSA PUBLIC KEY-----", "-----END RSA PUBLIC KEY-----",
"-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----"
].forEach { pem = pem.replacingOccurrences(of: $0, with: "") }

// Construct DER data from the remaining PEM data
let der = Data(base64Encoded: pem, options: .ignoreUnknownCharacters)!

Now that we have our DER encoded data, it's time to construct our key. Firstly, create the attributes describing the key, after that create the key from the DER data. Note here how der is of type Data not of type String. Generally speaking, crypto operations occur on Data. The security API however uses CFData, but one can easily exchange them (as CFData or as Data). The same goes for the attribute dictionary.

// Key generation attributes
let attributes: [String: Any] = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeyClass): kSecAttrKeyClassPublic,
String(kSecAttrKeySizeInBits): der.count * 8
]

// For simplicity I force unwrap here. The nil parameter can be used to extract an error
let key = SecKeyCreateWithData(der as CFData, attributes as CFDictionary, nil)!

Now that we have our key, we can use it to encrypt. Be aware however that RSA cannot encrypt huge amounts of data (not technically true, you could create chunks of data and do it that way, but RSA is not intended for this. If you want to do such thing, read up on key exchange and symmetric encryption. RSA is not intended for that behaviour). Also note that OAEP (as used in the example) has a random factor to it. Meaning the cipher text output will differ each time you run this code. This doesn't mean it's not working, it's simply a property OAEP has. I'd also like to point out that PKCS1 padding should be avoided when able in favour of OAEP.

// An example message to encrypt
let plainText = "This is my secret".data(using: .utf8)!

// Perform the actual encryption
// Again force unwrapping for simplicity
let cipherText = SecKeyCreateEncryptedData(key, .rsaEncryptionOAEPSHA256, plainText as CFData, nil)! as Data

In the example above .rsaEncryptionOAEPSHA256 is used. This is one of the available encryption algorithms for RSA:

  • .rsaEncryptionRaw
  • .rsaEncryptionPKCS1
  • .rsaEncryptionOAEPSHA1
  • .rsaEncryptionOAEPSHA224
  • .rsaEncryptionOAEPSHA256
  • .rsaEncryptionOAEPSHA384
  • .rsaEncryptionOAEPSHA512

I highly recommend using one of the OAEP variants, but that is up to you. I hope this helps. The SecKeyCreateEncryptedData is available for macOS since 10.12. You can read more about it here.

RSA encrypt file or text

Update:

  1. Each time the app is launched it generates a new key pair, thus the previously encrypted data can not be decrypted with the new and different private key. The keys (or at least the private key) must be saved for future use.

  2. You are mis-using RSA by encrypting in chunks! When the data size is to large, or in general encrypting data, hybrid encryption is employed. That means a random symmetric key is created, the data is encrypted with symmetric encryption (AES) and the symmetric key is encrypted with asymmetric encryption (RSA). The two encryptions are packaged together.

  3. The data size RSA can encrypt is less than the key size. Even for raw RSA a 1024-bit key is limited to less than 127-bytes.

    In the code the key is 1024 bits ([kSecAttrKeySizeInBits] = 1024), which is 128 bytes. Accounting for 11-bytes of padding the largest data that can be encrypted is 116 bytes.

  4. The real point is why use RSA (asymmetric) vs AES (symmetric) key encryption?

    In general asymmetric encryption such as RSA is not used to encrypt data, data is in general encrypted with symmetric encryption such as AES.The choice usually boils down to the need for separate encryption and decryption keys and/or PKI.

    Both are as secure at comparable key sizes and AES is much faster. Comparable key sizes: AES 128-bits, RSA 3072-bits. See NIST: Recommendation for Key Management Table 2.

send RSA public key to iphone and use it to encrypt

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];
}


Related Topics



Leave a reply



Submit