I'm converting some of my older Objective-C code to Swift so I can move away from some deprecated methods but I keep getting a crash and so far I can't seem to figure out what's causing it. I'm getting a private key from a P12 Certificate and this method seems to work fine up until I get to the part where I actually need to get the dictionary from the CFArray, and even though the array has values in it the app keeps crashing. Here's the code that I have :
func privateKeyFromCertificate(p12Name: String, withPassword password: String) -> SecKeyRef {
let resourcePath: String = NSBundle.mainBundle().pathForResource(p12Name, ofType: "p12")!
let p12Data: NSData = NSData(contentsOfFile: resourcePath)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : password]
var privateKeyRef: SecKeyRef? = nil
var items : CFArray?
let securityError: OSStatus = SecPKCS12Import(p12Data, options, &items)
let description : CFString = CFCopyDescription(items)
print(description)
if securityError == noErr && CFArrayGetCount(items) > 0 {
let objects : CFDictionaryRef = CFArrayGetValueAtIndex(items, 0) as! CFDictionaryRef
let kString : NSString = kSecImportItemIdentity as NSString
let identity : SecIdentityRef = CFDictionaryGetValue(objects, unsafeAddressOf(kString)) as! SecIdentityRef
let securityError = SecIdentityCopyPrivateKey(identity, &privateKeyRef)
if securityError != noErr {
privateKeyRef = nil
}
}
return privateKeyRef!
}
The app keeps crashing right in the if statement on the first line where I try to get the CFDictiionaryRef from the CFArray. I added the line to print the description of the CFArray as a test and it does have values :
<CFArray 0x7fd2d2e8c2f0 [0x10b61f7b0]>{type = mutable-small, count = 1, values = (
0 : <CFBasicHash 0x7fd2d2e8c190 [0x10b61f7b0]>{type = mutable dict, count = 3,entries =>
0 : <CFString 0x10bfdd2c0 [0x10b61f7b0]>{contents = "trust"} = <SecTrustRef: 0x7fd2d2e8ad30>
1 : <CFString 0x10bfdd300 [0x10b61f7b0]>{contents = "identity"} = <SecIdentityRef: 0x7fd2d2e80390>
2 : <CFString 0x10bfdd2e0 [0x10b61f7b0]>{contents = "chain"} = <CFArray 0x7fd2d2d016e0 [0x10b61f7b0]>{type = mutable-small, count = 1, values = (
0 : <cert(0x7fd2d2e8c610) s: Client (IPHONE-WebService) i:Client (IPHONE-WebService)>)}}
I ended up changing some things around to get to the data, it's not the prettiest approach but it worked for me.
func privateKeyFromCertificate() -> SecKeyRef {
let certName : String = //name of the certificate//
//get p12 file path
let resourcePath: String = NSBundle.mainBundle().pathForResource(certName, ofType: "p12")!
let p12Data: NSData = NSData(contentsOfFile: resourcePath)!
//create key dictionary for reading p12 file
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "password_for_certificate"]
//create variable for holding security information
var privateKeyRef: SecKeyRef? = nil
var items : CFArray?
let securityError: OSStatus = SecPKCS12Import(p12Data, options, &items)
//let description : CFString = CFCopyDescription(items)
//print(description)
let theArray : CFArray = items!
if securityError == noErr && CFArrayGetCount(theArray) > 0 {
let newArray = theArray as [AnyObject] as NSArray
let dictionary = newArray.objectAtIndex(0)
let secIdentity = dictionary.valueForKey(kSecImportItemIdentity as String) as! SecIdentityRef
let securityError = SecIdentityCopyPrivateKey(secIdentity , &privateKeyRef)
if securityError != noErr {
privateKeyRef = nil
}
}
return privateKeyRef!
}
Related
I have a certificate "cert.p12" that I use to sign some data to send it to s SAS server
I read the certificate this way
let data = try Data.init(contentsOf: _certURL)
let password = "somepassword"
let options = [ kSecImportExportPassphrase as String: password ]
var rawItems: CFArray?
let status = SecPKCS12Import(data as CFData,
options as CFDictionary,
&rawItems)
guard status == errSecSuccess else {
print("Error[36]")
return
}
let items = rawItems! as! Array<Dictionary<String, Any>>
let firstItem = items[0]
let identity = firstItem[kSecImportItemIdentity as String] as! SecIdentity?
let trust = firstItem[kSecImportItemTrust as String] as! SecTrust
let publicKey = SecTrustCopyKey(trust)
let publicKeyData = SecKeyCopyExternalRepresentation(publicKey!, nil)
let publicKeyString = (publicKeyData! as Data).base64EncodedString()
I get the public key like this
"MInsdfknsiufiwefwef ...... etc"
While in android I get
"[Ghhsdfwe89fwenfnwekfjwef]MInsdfknsiufiwefwef ......etc"
So the key is same except for the first like 40 bytes or so
See the image below where I use text comparison tool
The key at the bottom is the correct one accepted by the SAS server
What could be going wrong or missing in iOS
I have a use case where the user gives a PKCS#12 file and its password as input, and I need both the certificate's and the private key's PEM strings.
I have managed to create a SecIdentity using the p12, and its documentation says:
A SecIdentity object contains a SecKey object and an associated SecCertificate object.
So, I believe I am in the right path, but I couldn't find a way of extracting the SecKey and the SecCertificate from this SecIdentity.
I have also not found a way to get a PEM string from a SecKey or SecCertificate, but that would only be the last step.
This is the code I've used to create the identity:
let key: NSString = kSecImportExportPassphrase as NSString
let options: NSDictionary = [key : p12Password]
var rawItems: CFArray?
let p12Data = try! Data(contentsOf: p12FileUrl)
let data = p12Data! as NSData
let cfdata = CFDataCreate(kCFAllocatorDefault, data.bytes.assumingMemoryBound(to: UInt8.self), data.length)!
let result: OSStatus = SecPKCS12Import(cfdata, options, &rawItems)
if result == errSecSuccess {
let items = rawItems! as! [[String: Any]]
let firstItem = items[0]
let identity = firstItem[kSecImportItemIdentity as String] as! SecIdentity
}
-- UPDATE --
I made some more progress, and managed to extract the certificate in a DER format (which I haven't tried to convert to PEM so far - I believe it should be easy), but I still have no idea how to get the private key.
if result == errSecSuccess {
let items = rawItems! as! [[String: Any]]
let firstItem = items[0]
let identity = firstItem[kSecImportItemIdentity as String] as! SecIdentity
var cert: SecCertificate?
SecIdentityCopyCertificate(identity, &cert)
var certDer = SecCertificateCopyData(cert!) //DER format
var key: SecKey?
SecIdentityCopyPrivateKey(identity, &key)
let keyDict = SecKeyCopyAttributes(key!) //Not sure what we can find here
}
I am successfully RSA-Encrypting a string but when i do it on iPhone 4s (iOS 9.3.2) it fails and returns 'nil' as a result. However it is successfully working on all the other iphones(5,6,7,8,X.)
I am using this RSA Public-Key:
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClTlHEResIvOPHR0+o4exJVEI5RQ4NnBBXV9tdoCbqavSgsiuFtZWn5RUVTLb0h7ULpOh8GDcu0yI4lnpMVDZ5U2w0ra2/BNl6XDt9bwwoOh5w2lsdVmdP94t/qVBX4C0OcXw+RdSD1pshucTO7m2YLxtzLuc4ChUwjWZXVEoHdQIDAQAB"
It is actually on this line of code where i am getting &keyRef 'nil'
err = SecItemCopyMatching(dictionary as CFDictionary, &keyRef);
Here is my code;
func encryptString(stringToEncrypt:String) -> String {
print("stringToEncrypt64 = " + stringToEncrypt)
let keyData = NSData(base64Encoded: Constants.RSA_Public_Key, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)
let dictionary: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrApplicationTag: "HBLMobilePublicKeyTag" as AnyObject,
kSecValueData: keyData!,
kSecAttrKeySizeInBits: NSNumber(value: 1024),
kSecReturnRef: true as AnyObject
];
var err = SecItemAdd(dictionary as CFDictionary, nil);
if ((err != noErr) && (err != errSecDuplicateItem)) {
print("error loading public key");
}
var keyRef: AnyObject?;
var base64String: String?
err = SecItemCopyMatching(dictionary as CFDictionary, &keyRef);
if (err == noErr) {
if let keyRef = keyRef as! SecKey? {
let plaintextLen = stringToEncrypt.lengthOfBytes(using: String.Encoding.utf8);
let plaintextBytes = [UInt8](stringToEncrypt.utf8);
var encryptedLen: Int = SecKeyGetBlockSize(keyRef);
var encryptedBytes = [UInt8](repeating: 0, count: encryptedLen);
err = SecKeyEncrypt(keyRef, SecPadding.PKCS1, plaintextBytes, plaintextLen, &encryptedBytes, &encryptedLen);
let data = NSData(bytes: encryptedBytes, length: encryptedBytes.count)
base64String = data.base64EncodedString(options: [])
}
}
SecItemDelete(dictionary as CFDictionary);
return base64String!
}
maybe it's that the iPhone 4s is out of date. If the versions are the same like all phones on ios-9 then it could be the hardware on the phone. Or it could be that you don't have enough space to store the application on the phone for it to work. It could be that half the files are on the phone and that it says Completed Transfer When it didn't complete it.
Sorry, I'm just saying what I know (Which I know nothing about IPhones) about software.
I am working on an iOS app on XCode 7.1 with Swift 2.1 and I am trying to do simple encryption with AES 128 bit and PKCS7 padding using CommonCrypto library.
The code works but every time I try to cast the NSData object to NSString then to String I get a nil and the app crashes.
I debugged the app and the NSData object is not nil.
The error occurs when I try to unwrap the String optional.
How to resolve this issue? and convert the NSData object to a String correctly?
Here is my code
static func AESEncryption(phrase: String,key: String,ivKey: String,encryptOrDecrypt: Bool) -> String {
let phraseData = phrase.dataUsingEncoding(NSUTF8StringEncoding)
let ivData = ivKey.dataUsingEncoding(NSUTF8StringEncoding)
let keyData: NSData! = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let keyBytes = UnsafePointer<Void>(keyData.bytes)
let keyLength = size_t(kCCKeySizeAES128)
let dataLength = Int(phraseData!.length)
let dataBytes = UnsafePointer<Void>(phraseData!.bytes)
let bufferData = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)!
let bufferPointer = UnsafeMutablePointer<Void>(bufferData.mutableBytes)
let bufferLength = size_t(bufferData.length)
let ivBuffer = UnsafePointer<Void>(ivData!.bytes)
var bytesDecrypted = Int(0)
let operation = encryptOrDecrypt ? UInt32(kCCEncrypt) : UInt32(kCCDecrypt)
let algorithm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
let cryptStatus = CCCrypt(
operation,
algorithm,
options,
keyBytes,
keyLength,
ivBuffer,
dataBytes,
dataLength,
bufferPointer,
bufferLength,
&bytesDecrypted)
if Int32(cryptStatus) == Int32(kCCSuccess) {
bufferData.length = bytesDecrypted
let data = bufferData as NSData
let stringData = String(data: data,encoding: NSUTF8StringEncoding)
print("After Operation: \(stringData)")
return stringData!
} else {
print("Encryption Error: \(cryptStatus)")
}
return "";
}
The encrypted data will not be a valid UTF-8 string as it should be indistinguishable from random bits. If you need it in a string form you need to do something like base64 encode it or write out the hex values of the bytes.
NSData has a base64EncodedDataWithOptions method which should produce a String.
I have a code to save a picture in Swift (1.1) and since IOS 9 i try to change it for swift 2.0 but after 3 days i can not ...
So it's a juste a simple function get metadata and function get this, update this and create new image with this. Works fine with xCode 6.2 and Swift (1.1)
So if you can help me please :s
import ImageIO
func savePicture(data: NSData) {
var img: CGImageRef
if let source = CGImageSourceCreateWithData(data, nil) {
var metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)! as Dictionary
let width = CGFloat(metadata[kCGImagePropertyPixelWidth] as NSNumber)
let height = CGFloat(metadata[kCGImagePropertyPixelHeight] as NSNumber)
let reso = width * height
metadata[kCGImageDestinationLossyCompressionQuality as String] = self.jpegQuality
var tiff:NSMutableDictionary = metadata[kCGImagePropertyTIFFDictionary] as NSMutableDictionary
tiff.setValue("Pictures", forKey: kCGImagePropertyTIFFModel)
var exif:NSMutableDictionary = metadata[kCGImagePropertyExifDictionary] as NSMutableDictionary
exif.setValue("Pictures", forKey: kCGImagePropertyExifLensModel)
let gps:NSMutableDictionary = NSMutableDictionary()
metadata[kCGImagePropertyGPSDictionary] = gps
gps.setValue(self.loc["lat"], forKey: kCGImagePropertyGPSLatitude)
gps.setValue(self.loc["lng"], forKey: kCGImagePropertyGPSLongitude)
self.dateFormatter.dateFormat = "HH:mm:ss"
gps.setValue(self.dateFormatter.stringFromDate(NSDate()), forKey: kCGImagePropertyGPSTimeStamp)
self.dateFormatter.dateFormat = "yyyy:MM:dd"
gps.setValue(self.dateFormatter.stringFromDate(NSDate()), forKey: kCGImagePropertyGPSDateStamp)
if (reso > self.desiredRes) {
let ratio = reso / self.desiredRes
metadata[kCGImageSourceThumbnailMaxPixelSize as String] = max(width, height) / sqrt(ratio)
metadata[kCGImageSourceCreateThumbnailFromImageIfAbsent as String] = true
img = CGImageSourceCreateThumbnailAtIndex(source, 0, metadata)
} else {
img = CGImageSourceCreateImageAtIndex(source, 0, metadata)
}
let uti = "public.jpeg"
var d = NSMutableData()
var dest: CGImageDestinationRef = CGImageDestinationCreateWithData(d, uti, 1, nil)
CGImageDestinationAddImage(dest, img, metadata)
CGImageDestinationFinalize(dest)
d.writeToFile(self._makePicName(), atomically: false)
}
// debug purpose: show that metadata have been saved
// let d = NSData(contentsOfFile: self.paths.last!)
// if let t = CGImageSourceCreateWithData(d, nil) {
// let again = CGImageSourceCopyPropertiesAtIndex(t, 0, nil) as Dictionary
// println(again)
// }
dispatch_async(dispatch_get_main_queue(), {
for dlg in self.delegate {
dlg.didFinishSavingPhoto?()
}
})
}