How to perform Client Side Encryption in iOS AWS SDK? - ios

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"

Related

How to get compatible encryption version in flutter as in 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!!

How to Decoding AES-128 Data in iOS

I am exchanging data with a device that requires encryption. According to the instructions, my iOS app generates an RSA 2048-bit public-private key pair and I send the device the public key.
I created the public-private key pair using a macOS command line with openssl shown here (as I cannot figure out how to make iOS do it - but that's a future question):
openssl req -x509 -newkey rsa:2048 -out cert.pem
openssl x509 -in cert.pem -noout -pubkey
I copied the values from the cert.pem and the private and public keys into my app:
let certificate = "MIIDej....GWB"
let privateKey = "MIIFhz...Q=="
let publicKey = "MIIBI...AQAB"
When I send the device the public key, the device is happy and sends back data in AES-128 encrypted using the public key I sent.
I am in over my head here, but I'm the one trying to get this device to work.
Is there a way to take the private key or cert that I have set as Strings in my app to decode the data coming from the device?
I have been looking at the Security and CryptoKit docs and it makes my head swim.
FWIW, I was able to use SecCertificateCreateWithData on the String set from the cert.pem file and read some of its info like the summary. But I have no idea how to apply the private key or use the cert to decode the data. I need like a Dummies cliff notes or something.
Thank you.
Apple documentation describe how to generate key pair on the iOS side.
Example:
let tag = "com.example.keys.mykey".data(using: .utf8)!
let attributes: [String: Any] =
[kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag]
]
var publicKey: SecKey?
var privateKey: SecKey?
SecKeyGeneratePair(attributes, &publicKey, &privateKey)
More attributes are available here.
And then you can get External representations of private or public key using method:
SecKeyCopyExternalRepresentation.
Example:
var error: Unmanaged<CFError>?
SecKeyCopyExternalRepresentation(secKey, &error) as Data?
To decrypt AES-128 data you need to use CCCrypt. Here you have example from Apple support how the data is encrypted, but to decrypt data you need to change first argument to kCCDecrypt.
Example:
extern NSData * SecDecryptAES128CBCPad(NSData * data, NSData * key, NSData * iv) {
CCCryptorStatus err;
NSMutableData * result;
size_t resultLength;
NSCParameterAssert(key.length == kCCKeySizeAES128);
NSCParameterAssert(iv.length == kCCBlockSizeAES128);
// Padding can expand the data, so we have to allocate space for that. The rule for block
// cyphers, like AES, is that the padding only adds space on encryption (on decryption it
// can reduce space, obviously, but we don't need to account for that) and it will only add
// at most one block size worth of space.
result = [[NSMutableData alloc] initWithLength:[data length] + kCCBlockSizeAES128];
err = CCCrypt(
kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key.bytes, key.length,
iv.bytes,
data.bytes, data.length,
result.mutableBytes, result.length,
&resultLength
);
assert(err == kCCSuccess);
// Set the output length to the value returned by CCCrypt. This is necessary because
// we have padding enabled, meaning that we might have allocated more space than we needed.
[result setLength:resultLength];
return result;
}
Arguments in CCCrypt method (like kCCOptionPKCS7Padding, iv) you need to adjust to your example.

How to use SecPKCS12Import with private key encrypted with AES_256

I am generating a PKCS12 file using OpenSSL library in Swift. Following is the code that I use to generate the file:
guard let p12 = PKCS12_create(passPhrase, name, privateKey, certificate, nil, NID_aes_256_cbc, 0, 0, 0, 0) else {
ERR_print_errors_fp(stderr)
return
}
Please note that I am using NID_aes_256_cbc to encrypt the private key.
Then when I use the following code to import the p12 file in swift, I had the error code of -26275.
var items: CFArray?
let certOptions: NSDictionary = [kSecImportExportPassphrase as NSString: passwordStr as NSString]
self.securityError = SecPKCS12Import(p12Data as NSData, certOptions, &items)
print(securityError!.description)
After consulting OSStatus for error code explanation, I found that it's a errSecInvalidKey.
If I change the parameter from NID_aes_256_cbc back to 0, which uses the default 3DES algorithm, everything works fine.
So I was wondering if SecPKCS12Import decrypting the private key with 3DES algorithm by default. If yes, how should I make SecPKCS12Import to decrypt the AES encrypted private key?

Generate a P12 file Xcode?

I know there is a function called SecPKCS12Import that allows you to import data from a p12 file. However, I want to go the reverse route. I have a SecCertificateRef and a public/private SecKeyRef, which I want to use to create a P12 file. Does anyone know how to do this on iPhone?
Thanks
Unfortunately, there CommonCrypto does not provide any means to export PKCS12 containers let alone any other export functionality (even though its OSX counterpart can do that). There are ways to extract the SecKeyRef raw data from the key chain but then you still need to write all the PKCS12 wrapping yourself.
We were facing a similar issue and went with OpenSSL.
Compiling OpenSSL for iOS
Integrating OpenSSL requires a bit of work as you need to compile and link the OpenSSL sources yourself. Fortunately, there are some build scripts available so you do not have to do that yourself, e.g, https://github.com/x2on/OpenSSL-for-iPhone . I suggest you use them as you need to patch some of the Makefiles which is a bit of a hazel. Those build scripts generate static linked libraries for both iOS and tvOS. You just need to link them against your project and set the Header and Library Search Path accordingly.
CocoaPods
You can also use the official OpenSSL CocoaPod . That saves you the trouble of configuring your project.
Exporting PKCS12
As you might know, OpenSSL is a C library. That means you might want to encapsulate all the C functions into a Objective-C or Swift wrapper. There are some open source wrappers that support im- and exporting PKCS12 containers but I have not found a single one with good documentation. You should be able to derive the relevant snippets from some of the sources though.
https://github.com/microsec/MscX509Common/blob/master/src/MscPKCS12.m
You can have a look at this example as well http://fm4dd.com/openssl/pkcs12test.htm .
Hope that helps!
I agree that this task can only be performed using OpenSSL. It is a bit tricky to compile it for iOS but with OpenSSL-for-iPhone it is quite possible.
To solve the given task of creating a PKCS12 keystore from a SecCertificate and a SecKey with Swift 3 just add the static libraries libssl.aand libcrypto.a to your project and create the following bridging header:
#import <openssl/err.h>
#import <openssl/pem.h>
#import <openssl/pkcs12.h>
#import <openssl/x509.h>
To create the keystore the input data have to be converted to OpenSSL data structures, which requires some creativity. The SecCertificate can be converted directly to DER format and then read into a X509 structure. The SecKey is even worse to handle. The only possible solution to get the data of the key is to write it to the keychain and get the reference. From the reference we can get the base 64 encoded string, which then can be read into a EVP_PKEY structure. Now, we can create the keystore and save it to a file. To access the data in the keystore via iOS functions we must read the file via let data = FileManager.default.contents(atPath: path)! as NSData
The full solution is shown in the following:
func createP12(secCertificate: SecCertificate, secPrivateKey: SecKey) {
// Read certificate
// Convert sec certificate to DER certificate
let derCertificate = SecCertificateCopyData(secCertificate)
// Create strange pointer to read DER certificate with OpenSSL
// data must be a two-dimensional array containing the pointer to the DER certificate as single element at position [0][0]
let certificatePointer = CFDataGetBytePtr(derCertificate)
let certificateLength = CFDataGetLength(derCertificate)
let certificateData = UnsafeMutablePointer<UnsafePointer<UInt8>?>.allocate(capacity: 1)
certificateData.pointee = certificatePointer
// Read DER certificate
let certificate = d2i_X509(nil, certificateData, certificateLength)
// Print certificate
X509_print_fp(stdout, certificate)
// Read private key
// Convert sec key to PEM key
let tempTag = "bundle.temp"
let tempAttributes = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: tempTag,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecValueRef: secPrivateKey,
kSecReturnData: kCFBooleanTrue
] as NSDictionary
var privateKeyRef: AnyObject?
// Store private key in keychain
SecItemDelete(tempAttributes)
guard SecItemAdd(tempAttributes, &privateKeyRef) == noErr else {
NSLog("Cannot store private key")
return
}
// Get private key data
guard let privateKeyData = privateKeyRef as? Data else {
NSLog("Cannot get private key data")
return
}
let pemPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n\(privateKeyData.base64EncodedString())\n-----END RSA PRIVATE KEY-----\n"
// Delete private key in keychain
SecItemDelete(tempAttributes)
let privateKeyBuffer = BIO_new(BIO_s_mem())
pemPrivateKey.data(using: .utf8)!.withUnsafeBytes({ (bytes: UnsafePointer<Int8>) -> Void in
BIO_puts(privateKeyBuffer, bytes)
})
let privateKey = PEM_read_bio_PrivateKey(privateKeyBuffer, nil, nil, nil)
// !!! Remove in production: Print private key
PEM_write_PrivateKey(stdout, privateKey, nil, nil, 0, nil, nil)
// Check if private key matches certificate
guard X509_check_private_key(certificate, privateKey) == 1 else {
NSLog("Private key does not match certificate")
return
}
// Set OpenSSL parameters
OPENSSL_add_all_algorithms_noconf()
ERR_load_crypto_strings()
// Create P12 keystore
let passPhrase = UnsafeMutablePointer(mutating: ("" as NSString).utf8String)
let name = UnsafeMutablePointer(mutating: ("SSL Certificate" as NSString).utf8String)
guard let p12 = PKCS12_create(passPhrase, name, privateKey, certificate, nil, 0, 0, 0, 0, 0) else {
NSLog("Cannot create P12 keystore:")
ERR_print_errors_fp(stderr)
return
}
// Save P12 keystore
let fileManager = FileManager.default
let tempDirectory = NSTemporaryDirectory() as NSString
let path = tempDirectory.appendingPathComponent("ssl.p12")
fileManager.createFile(atPath: path, contents: nil, attributes: nil)
guard let fileHandle = FileHandle(forWritingAtPath: path) else {
NSLog("Cannot open file handle: \(path)")
return
}
let p12File = fdopen(fileHandle.fileDescriptor, "w")
i2d_PKCS12_fp(p12File, p12)
fclose(p12File)
fileHandle.closeFile()
}

How to create signed url to fetch private content from cloud front in iOS

I am developing iOS app in swift. We need to fetch images from amazon cloud front . I am able to fetch private content from cloud front in Objective C using openssl library in Xcode 6.4 using this reference link.
But I am using same library in swift , so getting importing error.
Please suggest it , How to create signed url for amazon cloud front to access private content in iOS.
If any other library to create signed url for amazon cloud front, please suggest it.
There is a great lib for that :) And this is a full code in Swift 2:
lazy var signedURL: String = {
let epochTime = NSDate().dateByAddingTimeInterval(60*5).timeIntervalSince1970
let resourceURL = "resource url"
let keyPairId = "your pair id"
let keyPairPrivateKeyName = "name of pem file"
let myurl = String(format: "%#?Expires=%.0f&Signature=%#&Key-Pair-Id=%#",
resourceURL,
epochTime,self.getSignature(resourceURL, expiresOn: epochTime, privateKey: keyPairPrivateKeyName), keyPairId)
return myurl
}()
func encodeStringForCloudFront(signature aSignature: String) -> String {
var signature = aSignature
signature = signature.stringByReplacingOccurrencesOfString("+", withString: "-")
signature = signature.stringByReplacingOccurrencesOfString("=", withString: "_")
signature = signature.stringByReplacingOccurrencesOfString("/", withString: "~")
return signature;
}
func getSignature(resourceURL: String, expiresOn: NSTimeInterval, privateKey keyPairPrivateKeyName: String) -> String {
let signString = String(format: "{\"Statement\":[{\"Resource\":\"%#\",\"Condition\":{\"DateLessThan\":{\"AWS:EpochTime\":%.0f}}}]}", resourceURL, expiresOn)
guard let filePath = NSBundle.mainBundle().pathForResource(keyPairPrivateKeyName, ofType: "pem"),
let privateKeyData = NSData(contentsOfURL: NSURL(fileURLWithPath: filePath)) else {
return "Error loading pem file."
}
guard let str = String(data: privateKeyData, encoding: NSUTF8StringEncoding) else {
return "Private Key Data is not valid"
}
do {
let signatureString = try SwiftyRSA.signString(signString, privateKeyPEM: str)
return self.encodeStringForCloudFront(signature: signatureString)
} catch {
return "Something goes wrong URL NotValid"
}
}
As the linked thread states, it is extremely insecure to embed the Amazon CloudFront certificates on mobile devices.
Instead of generating the pre-signed URL on iOS, you should have a server and generate the pre-signed URL on your server. See Serving Private Content through CloudFront for more details on how to do this.
How to create signed url for amazon cloud front to access private content in iOS.
Its not clear to me what this has to do with OpenSSL. I also don't know what a signed URL is. Perhaps its a Amazon or Cloud Front marketing term.
With that said, I believe Python uses self authenticating URLs. A user agent can check authenticity of a package by visiting a URL based on a digest of the package. If the server responds with a 200, then its valid; otherwise its invalid. There's no page backing the URL.
Its not clear to me what you are trying to do, so I should probably stop there. For more reading, see:
Peter Gutmann's Engineering Security, around page 384
Python self authenticating URLs
You can use OpenSSL to create a self authenticating URL. You will have to modify the Base64 encoder, however. The default Base64 encoder uses the the standard Base64 alphabet from the old days (dating back to the 1980s and email). For web gear, you usually need to use the Base64 with the web safe alphabet.

Resources