How to get compatible encryption version in flutter as in iOS - ios

I am working on iOS and flutter application together. There is encryption of data happening on both the sides. Below is the iOS encryption code which is already live,
func encryption(encryptionKey: String) -> String{
if(self.isEmpty){
return ""
}else{
let key = encryptionKey
let dataBytes : [UInt8] = Array(self.utf8)
let keyBytes : [UInt8] = Array(key.utf8)
do {
let encryptedData = try AES(key: keyBytes, blockMode: ECB(), padding: .pkcs7).encrypt(dataBytes)
let encodedString = Data(encryptedData).base64EncodedString()
return encodedString
} catch let error {
print(error)
return ""
}
}
Below is the flutter encryption code that I am working on now (Using encrypt.dart package),
final key = Key.fromBase64("Some_Key");
final iv = IV.fromLength(16));
final encrypter = Encrypter(AES(key, mode: AESMode.ecb, padding: 'PKCS7'));
final encrypted = encrypter.encrypt(someString, iv: iv); //IV is ignored in ECB mode
The issue here is the encrypted string that I am getting in flutter must be the same as iOS which is not the case. Could someone help me out in getting a compatible encryption version in flutter? Kindly help...

I finally resolved it myself. Posting the answer over here hoping it helps someone.
I had to change the below line,
from
final key = Key.fromBase64("Some_Key");
to
final key = Key.fromUtf8("Some_Key");
That't it. It works!!

Related

How can I create a Public Key from a PEM File (String) in Swift?

I'm pretty new to RSA Keys and Certificates and all of that. So I'm getting a PEM File from a network request, I understand the PEM file is basically the certificate without header and footer, so essentially I have a string. I need to create a PublicKey from that in order to Encrypt some text, but I think I'm doing something wrong. This is my code so far, I have talked to other teams (Android) and when they print out the Encrypted Data, they are getting a regular String and I'm getting lots of weird characters. Thanks in advance guys! (:
func encryptBase64(text: String, certificateStr: String) throws -> Data {
let encryptionError = EncrpytionError(message: "There has been an issue with encryption")
guard
let certData = Data(base64Encoded: certificateStr),
let certificate = SecCertificateCreateWithData(nil, certData as CFData),
let publicKey = SecCertificateCopyKey(certificate)
else {
throw encryptionError
}
let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256AESGCM
guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else {
throw encryptionError
}
guard let cipherText = SecKeyCreateEncryptedData(
publicKey,
algorithm,
text.data(using: .utf8)! as CFData, nil)
else {
throw encryptionError
}
return cipherText as Data
}
And when I try to print cipherText as a String, I get this weird thing:
"D�aj�\\m꒤h,�A�{��8�~�\nY\u0003F�Cˤ�#��\"�\u0018�\u0007\u001fX#VC�U_��E\u0005dž1���X��\/4Px��P\u0016�8}% ��<��#�I_�K\u000e�\bR*�� ���斋�7,�%���F^q�\u0000\\�'�ZTD\u0013Q�_\u0010\u001f>i]&��B���#1\u0006\b��E\u0004�F���yS\u0013�3����SB)��m\u0017%��5ʲ����s\u0003��r�&�?�8b��W#\u001e��؞ۡ��8�s~��ӹ�u\"�2��U�&\b�3XV���˔Y��xt[\fm&P:\\�\f� y��6jy"
Android team is doing something like this on Kotlin:
private fun extractPublicKey(certificateString: String): PublicKey {
val encodedCertificateBytes = certificateString.toByteArray()
val decodedCertificateBytes = Base64.decode(encodedCertificateBytes, Base64.DEFAULT)
val inStream = decodedCertificateBytes.inputStream()
val certificateFactory = CertificateFactory.getInstance("X.509")
val certificate = certificateFactory.generateCertificate(inStream)
inStream.close()
return certificate.publicKey
}

`SecKey` object creation for RSA failing, Error Domain Code=-50 "RSA private key creation from data failed swift-iOS

I'm not able to create SecKey object from below private key, I did try many answers available here but nothing is helping.
My swift piece of code is as below:
var error: Unmanaged<CFError>?
guard let keyData = Data(base64Encoded: key) else {
return nil
}
var keyAttributes: CFDictionary {
return [kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrKeySizeInBits: keySize] as CFDictionary
}
guard let secKey = SecKeyCreateWithData(keyData as CFData, keyAttributes, &error) else {
print(error.debugDescription) //Error Domain Code=-50 "RSA private key creation from data failed swift-iOS
return nil
}
The expected result is secKey should have valid value and above guard should not fail.
Note: Public key conversion to the respective secKey is working perfectly fine (the problem is with the only private key while decryption). I have tried removing \r\n from the above key.
After searching a lot, found this Apple thread helpful.
I'm able to manage this ASN.1 parsing using SwiftyRSA library.
let privateKeySwifty = try PrivateKey(pemEncoded: privateKey)
let secPrivateKey = try PrivateKey(reference: privateKeySwifty.reference)
After digging deeper, I can see, there was a need to stripe the header of keyData (ASN.1 Parsing).

JOSESwift jwe encryption failed to decode in the nimbus server

Had anybody used JOSESwift successfully? In my case, decryption in the server failing, probably cannot find the matching private key or wrong with encryption. Getting error 500.
My code is, getting the public keys from a server.
keys?.keys?.forEach({ (key) in
BPLogger.debug("\(key)")
do {
let jwkData = key.toJSONString()?.data(using: .utf8)
let rsaKey = try RSAPublicKey(data: jwkData!)
BPLogger.log("key components: \(rsaKey.parameters)")
BpidCache.shared.joseRsaKey = rsaKey
self?.generateParametersJose()
completion()
return
} catch {
BPLogger.debug("Error: \(error)")
}
})
The server expected a 'kid' field in the jose header, which was missing in the framework. So I have added it... The backend Java server uses nimbus library.
func generateParametersJose() {
let rsa = BpidCache.shared.joseRsaKey
var publicKey: SecKey? = nil
do {
publicKey = try rsa?.converted(to: SecKey.self)
} catch {
BPLogger.log("\(error)")
}
var header = JWEHeader(algorithm: .RSA1_5, encryptionAlgorithm: .A256CBCHS512)
// header.parameters["kid"] = "1"
let jwk = MidApi.Model.JWTKey(key: cek);
let jwkData = try! JSONEncoder().encode(jwk)
BPLogger.debug("jwkData = \(String(data: jwkData, encoding: .utf8)!)")
let payload = Payload(jwkData)
// Encrypter algorithms must match header algorithms.
guard let encrypter = Encrypter<SecKey>(keyEncryptionAlgorithm: .RSA1_5, encryptionKey: publicKey!, contentEncyptionAlgorithm: .A256CBCHS512) else {
return
}
guard let jwe = try? JWE(header: header, payload: payload, encrypter: encrypter) else {
BPLogger.error("Falied jwe creation.")
return
}
var comps = jwe.compactSerializedString.components(separatedBy: ".")
var jweHeader = comps.first
let data = jweHeader?.base64URLDecode()
var orgH = try! JSONDecoder().decode(BPJweHeader.self, from: data!)
orgH.kid = "1"
let newJson = try! JSONEncoder().encode(orgH).base64URLEncodedString()
comps[0] = newJson
let newHeader = comps.joined(separator: ".")
BPLogger.log("jwe.compactSerializedString = \(newHeader)")
headers = ["X-Encrypted-Key": newHeader]
// headers = ["X-Encrypted-Key": jwe.compactSerializedString] // this also fails
}
What am I doing wrong?
The latest version of JOSESwift (1.3.0) contains a fix for the problem that prevented setting additional header parameters.
You can now set the additional header parameters listed in RFC-7516. Setting the "kid" parameter like you tried to do in your question works like this:
var header = JWEHeader(algorithm: .RSA1_5, encryptionAlgorithm: .A256CBCHS512)
header.kid = "1"
If you use the framework via CocoaPods, make sure to run pod repo update to make sure you install the latest version which contains the fix.

Encrypt RSA/ECB/OAEPWithSHA-256AndMGF1Padding Swift

I am going to say in advance i don't know too much about cryptography (Basics only). I am trying to Implement a Credential OpenHome Service and I want to encrypt a password to send it to the device.
The device provides a function written in C that returns a public key String that looks like that:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzjFGuEKD0uWxzb47oRbiSP2uDwVJPeWU7m9VXi626V6lameTzdtwj2eYVZTIAsAW7yW4or2skn7oHqFG4GvhMzgMwoQjKFxeCPPFXRSotnt26AN1DhvFJp3V/d+MpmkzI07iWcD5eNe4EVNK9GSE4JOEHhJ/JYBVMiu04XE5aqwIDAQAB
The Android implementation has been already done and the specs given are
RSA/ECB/OAEPWithSHA-256AndMGF1Padding
also there is a web site that gives "instructions" when encrypting
http://wiki.openhome.org/wiki/Av:Developer:CredentialsService
I have tried so far these libraries:
SwiftyRSA, Heimdall, SwCrypt
I really thing that one of my main failures are I don't understand what I have, what do I need and finally how to achieve it using swift.
ideally at the end i will have a functions like
func encryptMessage(message:String, whithPublicKey key:String)->String
thank you very much.
After a long research i have just implemented my own solution rather than using libraries and not understanding what was going on. It is always good to know what happens and it this case it is not rocket science.
On iOS if you want to encrypt/decrypt you need to use a key stored on the keychain. If, in my case, i have been given the public key i can import it and also I can do the same with the private key. Please see my HelperClass Here.
Then, and from only from iOS 10, you can call this 2 methods for encrypting and decrypting
let error:UnsafeMutablePointer<Unmanaged<CFError>?>? = nil
let plainData = "A Plain text...".data(using: .utf8)
if let encryptedMessageData:Data = SecKeyCreateEncryptedData(publicSecKey, .rsaEncryptionOAEPSHA256, plainData! as CFData,error) as Data?{
print("We have an encrypted message")
let encryptedMessageSigned = encryptedMessageData.map { Int8(bitPattern: $0) }
print(encryptedMessageSigned)
if let decryptedMessage:Data = SecKeyCreateDecryptedData(privateSecKey, .rsaEncryptionOAEPSHA256, encryptedMessageData as CFData,error) as Data?{
print("We have an decrypted message \(String.init(data: decryptedMessage, encoding: .utf8)!)")
}
else{
print("Error decrypting")
}
}
else{
print("Error encrypting")
}
Also, if you want before iOS 10 you have the functions:
func SecKeyEncrypt(_ key: SecKey,
_ padding: SecPadding,
_ plainText: UnsafePointer<UInt8>,
_ plainTextLen: Int,
_ cipherText: UnsafeMutablePointer<UInt8>,
_ cipherTextLen: UnsafeMutablePointer<Int>) -> OSStatus
And
func SecKeyDecrypt(_ key: SecKey,
_ padding: SecPadding,
_ cipherText: UnsafePointer<UInt8>,
_ cipherTextLen: Int,
_ plainText: UnsafeMutablePointer<UInt8>,
_ plainTextLen: UnsafeMutablePointer<Int>) -> OSStatus
But these give less options and They are quite resticted.
Worth mentioning that my public and private key where generate on android using
public static String createStringFromPublicKey(Key key) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key.getEncoded());
return new String(Base64.encode(x509EncodedKeySpec.getEncoded(), Base64.NO_WRAP), "UTF-8");
}
and
public static String createStringFromPrivateKey(Key key) throws Exception {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key.getEncoded());
return new String(Base64.encode(pkcs8EncodedKeySpec.getEncoded(), Base64.NO_WRAP), "UTF-8");
}

How to perform Client Side Encryption in iOS AWS SDK?

is it Available ? or should I choose my own algorithm to encrypt data and upload it to the S3-bucket? I'm having a requirement to create an application in multiplatform(android/C#/ios) in which we have to encrypt data and Store it to the server side . . .
I've tried this library to encrypt data, but in iOS side, I'm having different results than others . . .
I uploaded a video on aws s3 bucket with client side encryption using below code. We need the AES256 key and md5 key when going to upload content on aws.
First, add pod CryptoSwift .
Now generate the AES256 & md5 key from below code.
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9]
let key: Array<UInt8> = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
let iv: Array<UInt8> = AES.randomIV(AES.blockSize)
do {
let encrypted = try AES(key: key, iv: iv, blockMode: .CBC, padding: PKCS7()).encrypt(input)
let base64String: String = encrypted.toBase64()!
let md5Data = encrypted.md5()
let md5DataBase64 = md5Data.toBase64()
print("Encrypted:\(encrypted),\n Base64String:\(base64String)")
print("md5:\(md5Data),\n md5String:\(md5DataBase64)")
} catch {
print(error)
}
Now add below two lines in your upload request of aws.
uploadRequest?.sseCustomerKey = "Your base64 string of AES 256 key"
uploadRequest?.sseCustomerKeyMD5 = "Your base64 string of md5"

Resources