Swift Load RSA Public Key from String (MacOS) - swift

i was wondering how could i load a RSA Public Key from a String and encrypt another string with it.
The public key is something like:
let key = """ -----BEGIN PUBLIC KEY----- blah blah blah -----END PUBLIC KEY-----"""
I would prefer to do this without an outside library but if it makes things easier im willing to use them.
Thanks in advance.

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.

Related

How to encrypt a string using AES-256 in flutter so that it decrypts on the web using openSSL

I tried encrypting a string in dart using encrypt library and It works pretty well other than the fact that when I try the generated encrypted string and the key and iv in an online AES decryptor, It never decrypts successfully.
I want to send encrypted data to a server and then that data needs to be decrypted on the server as well as the mobile device and I couldn't find any solution for this
My server is using PHP with OpenSSL, and I couldn't find any library for openSSl in flutter except this one but it has 0 documentation.
This is the sample code I used
Attempt 1:
final plainText = 'My Phone number is: 1234567890';
final key = encrypt.Key.fromLength(32);
final iv = encrypt.IV.fromLength(16);
final encrypter = encrypt.Encrypter(encrypt.AES(key));
final encrypted = encrypter.encrypt(plainText, iv: iv);
final decrypted = encrypter.decrypt(encrypted, iv: iv);
print(key.base64); // prints AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
print(iv.base64); // prints AAAAAAAAAAAAAAAAAAAAAA==
print(encrypted.base64); // prints kezgKMov5+yNJtd58OFSpzp8sNv2dwWNnFWDyf37cYk=
Attempt 2:
This time I used this gist that works using pointy castle to create the same data, but this time the IV was generated in an Array, and my server is expecting it as an int or a string.
Attempt 3:
this time I tried again using encrypt and found a medium link that encrypts data for decryption in the web using cryptoJS. this made me think, are all AES encryption libraries not compatible with each other?
final plainText = 'My Phone number is: 1234567890';
final newKey = Utils.CreateCryptoRandomString(32); // value is lh1uCZN4c8AFL2P4HudHV8B7dEBLzjxarZ09IrCf9cQ=
final encryptedAES = encryptAESCryptoJS(plainText, newKey);
Inside the encryptAESCryptoJS function, I added print statements to print the generated Salt, IV and key, Here are those:
SALT = [112, 161, 85, 133, 146, 178, 232, 83]
KEY = 0IfSLn8F33SIiWlYTyT4j7n6jnNP74xNaKTivqNeksE=
IV = QCl8fNQtg+QQYTQCINV6IA==
I can encrypt and decrypt locally easily using all the methods, but how can I add support so that the encrypted data can be decrypted on the server as well.
some of the websites I tried using to decrypt the data were
https://string-o-matic.com/aes-decrypt
and
https://www.devglan.com/online-tools/aes-encryption-decryption
both threw errors on adding the key, and iv on the specified fields
Any help would be much appreciated.
couldn't find any library for openSSl in flutter except this one but it has 0 documentation.
Yes, seems this is a problem. As well I consider important that someone understands basics regardless of the language implementation
I want to send encrypted data to a server and then that data needs to be decrypted on the server as well as the mobile device and I couldn't find any solution for this
That is a task of the TLS
The data needs to be stored encrypted as well so that no one working in the backend can look at the data
Just use the same encryption and decryption parameters. The problem with your code I see is it's missing some of the parameters and using defaults (defaults can differ in different libraries) or assuming you are using different parameters.
Symmetric encryption (AES specifically) needs to define:
key - for AES it's always 128, 192 or 256 bit (depending on the strength). Some libraries zero-pad or trim the input to match the required key length what I consider a terrible practice. Simply - a key needs to be a byte array of the specific length.
When encrypting multiple blocks of data:
padding - how input is padded to match the encryption block size (usually pkcs#7 padding)
mode of operation
IV - see the documentation about the mode of operation, IV must be unique and for some modes IV needs to be unpredictable (random).
SALT is used to create an encryption key from a password. So where you see any salt in use, check if you are providing a key or a password. Password can have any length and is usually user-handled (having lower entropy) and there are multiple ways how to derive a key from the password and salt.
var encrypted = encryptAESCryptoJS(plainText, "password");
See the source code, the encryptAESCryptoJS expects a password as input and then generates a salt and derives a key and IV (this is a practice from OpenSSL, but may not be compatible with other libraries).
This is a problem with some libraries, mainly when missing documentation.
Are all AES encryption libraries not compatible with each other?
AS cipher is AES cipher. You need to get the Cipher, Key, Padding, IV and the mode of operation the same for encryption and decryption regardless the programming language or platform. There are some most common defaults (AES-128, CBC mode, PKCS#7 padding, ..) but it's better to properly specify the parameters to be sure.
but this time the IV was generated in an Array, and my server is expecting it as an int or a string.
Encryption always works on top of byte arrays. You may encode a byte array as base64 or hex encoded string.
Edit: extra security measure
What I miss in this solution (in many other solutions in fact) is an authentication tag. Most of the encryption modes are malleable, the ciphertext can be changed and then the decryption would successfully decrypt to a different plaintext without detecting any problem with integrity. I consider using any HMAC necessary, but missing in many implementations.
I had the same problem, since in php the openssl_decrypt with aes-256-cbc is used to decrypt but in dart it didn't work for me, until I found a code snippet on github solutions, which served as the basis for proposing a solution to make it decode a text encrypted with php Lumen and AES openssl, I hope it will help you.
// code decrypt in PHP
$key = '**********key secred';
$encrypted = $request->get('encrypted');
$payload = json_decode(base64_decode($encrypted), true);
$iv = base64_decode($payload['iv']);
$decrypted = openssl_decrypt($payload['value'], 'aes-256-cbc',
base64_decode($key), 0, $iv, '');
$response['decrypted'] = unserialize($decrypted);
return $this->successResponse($response);
/// code decrypt in dart
import 'dart:convert';
import 'package:encrypt/encrypt.dart' as enc;
import 'dart:async';
import 'package:php_serializer/php_serializer.dart';
Future<String> decryptInfo(String data) async {
var encodedKey = 'FCAcEA0HBAoRGyALBQIeCAcaDxYWEQQPBxcXH****** example';
var decoded = base64.decode(data);
var payload = json.decode(String.fromCharCodes(decoded));
String encodedIv = payload["iv"]?? "";
String value = payload["value"] ?? "";
print(decoded);
print(payload);
print (encodedIv);
final key1 = enc.Key.fromBase64(encodedKey);
final iv = enc.IV.fromBase64(encodedIv);
final encrypter = enc.Encrypter(enc.AES(key1, mode: enc.AESMode.cbc));
final decrypted = encrypter.decrypt(enc.Encrypted.fromBase64(value), iv: iv);
print(phpDeserialize(decrypted));
return decrypted;
}

Convert between X.509 and PKCS#1 RSA Public keys

I am using CryptoPP to generate RSA keys, and run encryption / decryption of large amounts of data. Because of this, I am deciding to input data through a web socket from a phone app (currently using flutter), along with already in place desktop clients (the desktop clients work as they support the format the server uses).
My issue is that the keys are in different formats, everything I try (specifically simple_rsa) fails to be compatible with the server. As flutter only supports PKCS#1 as far as I am aware.
I understand now that the public key is formatted with X.509 from crypto++ wiki, and through use of an online tool this I have found that the cipher type (I'm guessing padding?) is OAEP with SHA-1.
These show that both key types are encoded with ASN.1
For reference, the server code is very similar to this, but uses a key size of 4096
////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize( rng, 1536 );
RSA::PrivateKey privateKey( parameters );
RSA::PublicKey publicKey( parameters );
And the key is written to a .pem file by encoding the data in base64.
std::string base64 = macaron::Base64::Encode(pubKeyString);
std::string base64LineLength;
int i = 0;
while (i < base64.size()) {
if (i % 64 == 0 && i)
base64LineLength.push_back('\n');
base64LineLength.push_back(base64[i]);
i++;
}
base64LineLength = "-----BEGIN PUBLIC KEY-----\n" + base64LineLength + "\n-----END PUBLIC KEY-----\n";
std::ofstream pubkeyOut("secure_chat_rsa.pub");
pubkeyOut << base64LineLength;
pubkeyOut.close();
Also, this is an example of a public key with the formatting (X.509)
-----BEGIN PUBLIC KEY-----
MIICIDANBgkqhkiG9w0BAQEFAAOCAg0AMIICCAKCAgEAmQlXbYS1I+B4AUXwU/ua
KgwdrUzYRwXPaR6435DAguDGW+zTeekDgP55lg9Lqn32UW+T/5PIgYQ2HpA/gfYU
IMvrLAhSSaXSEFzIzdwgFgo3IMZHdhcx/xP/+pmDTrflhmba/7QEOE/bC2hFzQCh
2Zd1DJbItIR64uyyi1Z0B6bFlLyDA2E+lctBLhuHSyYSqPrVUqYTRdptLNk8/vsN
HdWKrYq7H8n4QKQefspD5zU3SJdUumOIusqzXeMe70mPds+Qe9u4Ti5Ca7guZCN4
kNXUF/kJH7Y3dBh7409r2v/bjGEznFpY1cmP1f0EFYTQU1BirbNiwqnAjhy9fT2M
quSlpmwo7V0YbZDI/KBcLDxTY64oO6XMz6DHkdmpOluALWQAJZFJ7iHntsLp1GRB
DtLaidCr9EI+pN7cfwsSYLRHtEmUZoiz30RZra5c5+aE4sg24c/PJ5nVe9GDOaQs
dHL3+sc2r9LTEK9pCSO5cWdbxSRvKNrevElr2+8ORUQL1cRsCmL8ri4eYwrwukBn
HFJc1pZFD8i5sFjnJxEIKzoIa+eGVgqEwkwxKvNuUUyH3tJD9YJVlhs1G68VlxOf
RSk+LZGhwSMZs+PbfuxmPQZyapT5TqRJ2JJ7f9HErIq5f8WjBrzcqy63rygy33Hw
M94iQLwuTM3X99Z4FnyTsTECARE=
-----END PUBLIC KEY-----
Edit:
I have tried putting the key through an ANS.1 decoder, and extracted the bit string from it, then put that through a base64 encoder to give:
MIICCAKCAgEAmQlXbYS1I+B4AUXwU/uaKgwdrUzYRwXPaR6435DAguDGW+zTeekDgP55lg9Lqn32UW+T/5PIgYQ2HpA/gfYUIMvrLAhSSaXSEFzIzdwgFgo3IMZHdhcx/xP/+pmDTrflhmba/7QEOE/bC2hFzQCh2Zd1DJbItIR64uyyi1Z0B6bFlLyDA2E+lctBLhuHSyYSqPrVUqYTRdptLNk8/vsNHdWKrYq7H8n4QKQefspD5zU3SJdUumOIusqzXeMe70mPds+Qe9u4Ti5Ca7guZCN4kNXUF/kJH7Y3dBh7409r2v/bjGEznFpY1cmP1f0EFYTQU1BirbNiwqnAjhy9fT2MquSlpmwo7V0YbZDI/KBcLDxTY64oO6XMz6DHkdmpOluALWQAJZFJ7iHntsLp1GRBDtLaidCr9EI+pN7cfwsSYLRHtEmUZoiz30RZra5c5+aE4sg24c/PJ5nVe9GDOaQsdHL3+sc2r9LTEK9pCSO5cWdbxSRvKNrevElr2+8ORUQL1cRsCmL8ri4eYwrwukBnHFJc1pZFD8i5sFjnJxEIKzoIa+eGVgqEwkwxKvNuUUyH3tJD9YJVlhs1G68VlxOfRSk+LZGhwSMZs+PbfuxmPQZyapT5TqRJ2JJ7f9HErIq5f8WjBrzcqy63rygy33HwM94iQLwuTM3X99Z4FnyTsTECARE=
However, this is not a valid RSA key. (from trying to encrypt with it)
I have 4 main questions:
Is there a way for the X.509 format to be converted to PKCS#1 and vice versa
If converted, will the output be compatible. When the server encodes data, will the client be able to decrypt it if the keys are converted?
Is there a way for flutter to work with X.509 formatted keys?
If this is only due to ANS.1, how exactly would you go about encoding ciphers or keys?

PEM encoded certificate conversion in iOS

In my app I get a PEM encoded certificate and need to convert it into a different form to later use it for JWT verification purposes. The result I'm looking for is either a SecKey representation of the public key contained in the certificate, PEM Public Key string or conversion to a DER certificate.
I am very VERY new to this, so I have no idea how to tackle the problem. I've Googled around and found no clear solution, even Apple documentation only mentions DER certificates. If I understand it correctly, one solution would be to use OpenSSL inside my app (is this even possible?) for conversions, but I couldn't find any useful resource on how to implement this so I would appreciate more insight on the right practice.
Making conversions outside the application is not an option for my case.
If you have a certificate in PEM format, you can obtain it's DER encoded data by stripping away the header and footer lines and base64-decoding the text in between them (don't forget to discard line breaks as well). [1, 2]
You can then use SecCertificateCreateWithData from the iOS Security API to create a SecCertificate to use in your app like so:
// example data from http://fm4dd.com/openssl/certexamples.htm
let pem = """
-----BEGIN CERTIFICATE-----
MIIC2jCCAkMCAg38MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG
(...)
+tZ9KynmrbJpTSi0+BM=
-----END CERTIFICATE-----
"""
// remove header, footer and newlines from pem string
let certData = Data(base64Encoded: pemWithoutHeaderFooterNewlines)!
guard let certificate = SecCertificateCreateWithData(nil, data as CFData) else {
// handle error
}
// use certificate e.g. copy the public key
let publicKey = SecCertificateCopyKey(certificate)!
[1]
PEM format is simply base64 encoded data surrounded by header lines.
[2]
.pem – Base64 encoded DER certificate, enclosed between "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----"

Swift: RSA Encrypt a string with a specific private key

I need to write a method in Swift which uses a particular PRIVATE KEY to encrypt a timestamp using RSA. This is NOT used for authentication (rather it validates the client app to the server), I know that you would normally encrypt with a public key to ensure security.
I have a key:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDIg+wteSjhalc1hSHEiUnz9X1pkrObCjaXMHqeSdfFQ/h5Q1Uh
...
o7wjoqFNxFnQMAYvkLzQZ7Y2jjfSJkaTVnhzJIZOfQ0=
-----END RSA PRIVATE KEY-----
And I need to encrypt a string using this particular key. I have written the Android application version of this app, but if I need to re-generate the keys specifically for iOS it's not a problem, but I would need this file for the Android version as well.
I have looked at the following web sites:
http://jslim.net/blog/2013/01/05/rsa-encryption-in-ios-and-decrypt-it-using-php/ Seems useful but it insists that the key be in a .der format - plus it's in Objective-C. Is it possible to generate a Key pair in both DER and PEM format, or convert between the two (using something like https://www.sslshopper.com/ssl-converter.html)?
https://github.com/henrinormak/Heimdall - but you cannot import a custom private key
https://github.com/ideawu/Objective-C-RSA/blob/master/RSA.m - seems long winded and is also in objective-c
One requirement is that all apps use the SAME key - it cannot be generated by each installation of the app. Also my Android app must be able to use the key as well (I am open to rethinking the Android version as leaving the key as a RAW file is not preferred for me).
Any help will be appreciated.
Other references checked:
How to encrypt a string with private key and decrypt with public key?
Using RSA public key to encrypt a string

Cracking RSA without private key

I was wondering whether RSA can be cracked given:
The public key
A plaintext of a known cipher text.
In other words the decrypted message from an encrypted one (but without knowing the private key)
I.E.: you have managed to find the plaintext of an already encrypted message using the public key, however, you DO NOT know the private key.
No. That's called a known-plaintext attack, and there is no such attack known against RSA.