How to get private key from p12 in X509 certificate - ssl

I need to integrate my iOS app to consume a Web service requires a client certificate. I am using the below code to extract the private key from p12 in X509 certificate.
I found this code but not working, I translated it to swift 3.
func privateKeyFromCertificate() -> SecKeyRef {
let certName : String = //name of the certificate//
let resourcePath: String = NSBundle.mainBundle().pathForResource(certName, ofType: "p12")!
let p12Data: NSData = NSData(contentsOfFile: resourcePath)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "password_for_certificate"]
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!
}
The error encounter at :
let secIdentity = dictionary.valueForKey(kSecImportItemIdentity as String) as! SecIdentityRef
Below Updated version has the same problem at the same line:
func privateKeyFromCertificate() -> SecKey {
let certName : String = "certficateName"
let resourcePath: String = Bundle.main.path(forResource: certName, ofType: "p12")!
let p12Data: NSData = NSData(contentsOfFile: resourcePath)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "password_for_certificate"]
var privateKeyRef: SecKey? = 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.object(at: 0)
//- encounter error here:
let secIdentity = (dictionary as AnyObject).value(kSecImportItemIdentity as String) as! SecIdentity
let securityError = SecIdentityCopyPrivateKey(secIdentity , &privateKeyRef)
if securityError != noErr {
privateKeyRef = nil
}
}
return privateKeyRef!
}
I get the following error:
let secIdentity = (dictionary as AnyObject).value(kSecImportItemIdentity as String) as! SecIdentity
Cannot invoke 'Value' with any argument list of Type '(String)'
Your help is greatly appreciated.
Thanks

Can you try this for the error block - putting them in conditional id let block.
if let secIdentity = (dictionary as AnyObject).value(forKey: kSecImportItemIdentity as String) {
let securityError = SecIdentityCopyPrivateKey(secIdentity as! SecIdentity , &privateKeyRef)
if securityError != noErr {
privateKeyRef = nil
}
}

Related

Convert subscript function from Objective C to Swift

I am trying to convert some Objective C code to Swift and can't manage to do it right with subscripting.
This is the method I am trying to migrate to Swift:
- (NSArray *)rangesOfSubstringAlphaNumeric:(NSString *)substring rangesLimit:(NSUInteger)rangesLimit {
NSAssert(rangesLimit, #"A range limit grather than 0 must be specified");
if (!substring.length) {
return nil;
}
static NSCharacterSet * restrictedCharacters = nil;
if (!restrictedCharacters) {
restrictedCharacters = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
}
NSArray * substrings = [substring componentsSeparatedByCharactersInSet:restrictedCharacters];
NSMutableArray * allRanges = [NSMutableArray array];
NSString *searchedString = self;
for (NSString *stringToMatch in substrings) {
if (![stringToMatch isEqualToString:#""]) {
NSRange aRange;
NSUInteger lastLocation = 0;
NSUInteger foundRanges = 0;
while (foundRanges++ < rangesLimit &&
(aRange = [searchedString localizedStandardRangeOfString:stringToMatch]).location != NSNotFound) {
searchedString = [searchedString substringFromIndex:aRange.location + aRange.length];
aRange.location = aRange.location + lastLocation;
lastLocation = aRange.location + aRange.length;
[allRanges addObject:[NSValue valueWithRange:aRange]];
}
}
}
return allRanges.count ? [allRanges copy] : nil;
}
I got stuck on the subscripting part since it seems I cannot assign integer values to Indexes and conversion from Index to Int is out of hand for me I'm kind of stuck, this is what I managed to do:
func rangesOfAlphanumeric(substring: String, limit: UInt) -> [Range<String.Index>] {
guard limit > 0, !substring.isEmpty else {
if limit == 0 {
assert(false, "limit must be greather than 0")
}
return []
}
var searchedString = self
let substrings = substring.components(separatedBy: NSCharacterSet.restricted)
for stringToMatch in substrings {
if !stringToMatch.isEmpty {
// var aRange: Range<String.Index>?
// var lastLocation: UInt = 0
// var foundRanges: UInt = 0
// while foundRanges < limit,
// let tempRange = searchedString.localizedStandardRange(of: stringToMatch),
// !tempRange.isEmpty {
//
// searchedString = String(searchedString[tempRange.upperBound...])
// if let lastLocation = lastLocation {
// aRange = temp
// }
// }
}
}
}
UPDATE: Solution below.
Managed to resolve the issue using the ranges function posted here:
func rangesOfAlphanumeric(substring: String) -> [Range<String.Index>] {
var searchedString = self
let substrings = substring.components(separatedBy: NSCharacterSet.restricted)
return substrings.compactMap { (stringToMatch) -> [Range<String.Index>]? in
guard !stringToMatch.isEmpty else {
return nil
}
let ranges = searchedString.ranges(of: stringToMatch, options: [
.diacriticInsensitive,
.caseInsensitive
])
if let lastRange = ranges.last {
searchedString = String(searchedString[index(after: lastRange.upperBound)])
}
return ranges
}.flatMap{$0}
}
I created this repo with swift 5 is very easy to use
all is already set up. you have just to change the IAP ids
The Github repo

How to extract performance metrics measured by measureBlock in XCTest

I have a simple test function which will tap a button an measure the performance. I'm using XCTest. After measureBlock returns I can see a bunch of perf-metrics on the console. I would like to get this within the test-program such that I can populate the data somewhere else programmatically. Watching the test data on test-console is proving to be slow because I have a lot of test-cases.
- (void)testUseMeasureBlock {
XCUIElement *launchTest1Button = [[XCUIApplication alloc] init].buttons[#"Launch Test 1"];
void (^blockToMeasure)(void) = ^void(void) {
[launchTest1Button tap];
};
// Run once to warm up any potential caching properties
#autoreleasepool {
blockToMeasure();
}
// Now measure the block
[self measureBlock:blockToMeasure];
/// Collect the measured metrics and send somewhere.
When we run a test it prints:
measured [Time, seconds] average: 0.594, relative standard deviation: 0.517%, values: [0.602709, 0.593631, 0.593004, 0.592350, 0.596199, 0.593807, 0.591444, 0.593460, 0.592648, 0.592769],
If I could get the average time, that'd be sufficient for now.
Since there is no API to get this data you can pipe stderr stream and parse tests logs to get needed info e.g. average time. For instance you can use next approach:
#interface MeasureParser : NSObject
#property (nonatomic) NSPipe* pipe;
#property (nonatomic) NSRegularExpression* regex;
#property (nonatomic) NSMutableDictionary* results;
#end
#implementation MeasureParser
- (instancetype)init {
self = [super self];
if (self) {
self.pipe = NSPipe.pipe;
self.results = [NSMutableDictionary new];
let pattern = [NSString stringWithFormat:#"[^']+'\\S+\\s([^\\]]+)\\]'\\smeasured\\s\\[Time,\\sseconds\\]\\saverage:\\s([^,]+)"];
NSError* error = nil;
self.regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
if (error) {
return nil;
}
}
return self;
}
- (void)capture:(void (^)(void))block {
// Save original output
int original = dup(STDERR_FILENO);
setvbuf(stderr, nil, _IONBF, 0);
dup2(self.pipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO);
__weak let wself = self;
self.pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *handle) {
var *str = [[NSString alloc] initWithData:handle.availableData encoding:NSUTF8StringEncoding];
let firstMatch = [wself.regex firstMatchInString:str options:NSMatchingReportCompletion range:NSMakeRange(0, str.length)];
if (firstMatch) {
let name = [str substringWithRange:[firstMatch rangeAtIndex:1]];
let average = [str substringWithRange:[firstMatch rangeAtIndex:2]];
wself.results[name] = average;
}
// Print to stdout because stderr is piped
printf("%s", [str cStringUsingEncoding:NSUTF8StringEncoding]);
};
block();
// Revert
fflush(stderr);
dup2(original, STDERR_FILENO);
close(original);
}
#end
How to use:
- (void)testPerformanceExample {
let measureParser = [MeasureParser new];
[measureParser capture:^{
[self measureBlock:^{
// Put the code you want to measure the time of here.
sleep(1);
}];
}];
NSLog(#"%#", measureParser.results);
}
// Outputs
{
testPerformanceExample = "1.001";
}
Swift 5 version
final class MeasureParser {
let pipe: Pipe = Pipe()
let regex: NSRegularExpression?
let results: NSMutableDictionary = NSMutableDictionary()
init() {
self.regex = try? NSRegularExpression(
pattern: "\\[(Clock Monotonic Time|CPU Time|Memory Peak Physical|Memory Physical|CPU Instructions Retired|Disk Logical Writes|CPU Cycles), (s|kB|kI|kC)\\] average: ([0-9\\.]*),",
options: .caseInsensitive)
}
func capture(completion: #escaping () -> Void) {
let original = dup(STDERR_FILENO)
setvbuf(stderr, nil, _IONBF, 0)
dup2(self.pipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO)
self.pipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
guard self != nil else { return }
let data = handle.availableData
let str = String(data: data, encoding: .utf8) ?? "<Non-ascii data of size\(data.count)>\n"
self!.fetchAndSaveMetrics(str)
// Print to stdout because stderr is piped
if let copy = (str as NSString?)?.cString(using: String.Encoding.utf8.rawValue) {
print("\(copy)")
}
}
completion()
fflush(stderr)
dup2(original, STDERR_FILENO)
close(original)
}
private func fetchAndSaveMetrics(_ str: String) {
guard let mRegex = self.regex else { return }
let matches = mRegex.matches(in: str, options: .reportCompletion, range: NSRange(location: 0, length: str.count))
matches.forEach {
let nameIndex = Range($0.range(at: 1), in: str)
let averageIndex = Range($0.range(at: 3), in: str)
if nameIndex != nil && averageIndex != nil {
let name = str[nameIndex!]
let average = str[averageIndex!]
self.results[name] = average
}
}
}
}
How to use it:
import XCTest
final class MyUiTests: XCTestCase {
var app: XCUIApplication!
let measureParser = MeasureParser()
// MARK: - XCTestCase
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
override func tearDown() {
//FIXME: Just for debugging
print(self.measureParser.results)
print(self.measureParser.results["CPU Cycles"])
print(self.measureParser.results["CPU Instructions Retired"])
print(self.measureParser.results["CPU Time"])
print(self.measureParser.results["Clock Monotonic Time"])
print(self.measureParser.results["Disk Logical Writes"])
print(self.measureParser.results["Memory Peak Physical"])
print(self.measureParser.results["Memory Physical"])
}
// MARK: - Tests
func testListing() {
self.measureParser.capture { [weak self] in
guard let self = self else { return }
self.measureListingScroll()
}
}
// MARK: XCTest measures
private func measureListingScroll() {
measure(metrics: [XCTCPUMetric(), XCTClockMetric(), XCTMemoryMetric(), XCTStorageMetric()]) {
self.app.swipeUp()
self.app.swipeUp()
self.app.swipeUp()
}
}
}
There's a private instance variable __perfMetricsForID of XCTestCase store the result.
And you can access it by call
NSDictionary* perfMetrics = [testCase valueForKey:#"__perfMetricsForID"];
the result is just like this:

How can I use NSArray and NSDictionary with this SQL data?

Couldn't parse JSON data.
Could not cast value of type '__NSArrayI' (0x10f02fc08) to 'NSMutableArray' (0x10f02fcd0).
JSON data here and code here;
echo json_encode($resultArray);
And here is my trial code;
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let locations: NSMutableArray = NSMutableArray()
for i in 1...jsonResult.count
{
jsonElement = jsonResult[i-1] as! NSDictionary
let location = LocationModel()
if let name = jsonElement["Name"] as? String,
let address = jsonElement["Address"] as? String,
let latitude = jsonElement["Latitude"] as? String,
let longitude = jsonElement["Longitude"] as? String
{
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
NOTE: IT WILL FIX THIS
instead of:
var jsonResult: NSMutableArray = NSMutableArray()
do this:
var jsonResult: NSArray = NSArray()
and instead of:
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSMutableArray
do this:
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
full code:
func parseJSON() {
var jsonResult: NSArray = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: self.data as Data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let locations: NSMutableArray = NSMutableArray()
for i in 1...jsonResult.count
{
jsonElement = jsonResult[i-1] as! NSDictionary
let location = LocationModel()
if let name = jsonElement["Name"] as? String,
let address = jsonElement["Address"] as? String,
let latitude = jsonElement["Latitude"] as? String,
let longitude = jsonElement["Longitude"] as? String
{
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
The issue is that JSONSerialization is returning a non-mutable array, and you can't just cast a NSArray to a NSMutableArray. Instead, just declare it to be NSArray:
func parseJSON() {
var jsonResult: NSArray
do {
jsonResult = try JSONSerialization.jsonObject(with: data as Data) as! NSArray
} catch let error as NSError {
print(error)
return
}
...
}
Or, alternatively, use Swift's native collection types rather than falling back on old NSArray and NSDictionary types:
func parseJSON() {
var jsonResult: [[String: Any]] // this is an array of dictionaries
do {
jsonResult = try JSONSerialization.jsonObject(with: data as Data) as! [[String: Any]]
} catch let error as NSError {
print(error)
return
}
for jsonElement in jsonResult {
if let name = jsonElement["Name"] as? String,
let address = jsonElement["Address"] as? String,
let latitude = jsonElement["Latitude"] as? String,
let longitude = jsonElement["Longitude"] as? String
{
let location = LocationModel()
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
locations.add(location) // or, if you defined `locations` to be a Swift array, `locations.append(location)`
}
}
...
}
(Personally, I'd probably also define the data to be a Data rather than a NSData and define locations to be [LocationModel] rather than NSMutableArray.)

Sample function to store and retrieve a p12 certificate in keychain - Objective C

I need two functions one which stores the p12 certificate in iOS Keychain and the other function to retrieve the stored p12 certificate.
-(BOOL)addItem:(NSData*)item forKey:(NSString*)key {
NSMutableDictionary *dict = [self newItemDictionaryForKey:key];
[dict setObject:item forKey:(id)kSecValueData];
OSStatus ossstatus = SecItemAdd((CFDictionaryRef)dict, NULL);
if(errSecSuccess != ossstatus) {
NSLog(#"Unable to add item for key %# %ld request dict:%#",key,ossstatus,dict);
}
return (errSecSuccess == ossstatus);
}
To the above function i am sending the NSData that i get from p12 file.
You have to split this into two tasks:
Converting certificate from p12 data to SecIdentityRef. With this I can help
Store private key and certificate chain in keychain - I'm struggling with this too, but I'm couple steps ahead comparing with you.
To perform a conversion, here is a code I'm using:
- (NSError *)setClientIdentityCertificateFromPKCS12Data: (NSData *)PKCS12Data withPassword: (NSString *)password
{
OSStatus securityError = errSecSuccess;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { (__bridge CFStringRef)password };
CFDictionaryRef optionsDictionary = NULL;
optionsDictionary = CFDictionaryCreate(
NULL, keys,
values, (password.length!=0 ? 1 : 0),
NULL, NULL);
CFArrayRef items = NULL;
securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data,
optionsDictionary,
&items);
if (securityError == 0) {
CFDictionaryRef identityDic = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0);
SecIdentityRef secIdentity = (SecIdentityRef)CFDictionaryGetValue(identityDic, kSecImportItemIdentity);
CFArrayRef identityCertChain = (CFArrayRef)CFDictionaryGetValue(identityDic, kSecImportItemCertChain);
securityError = [self setClientIdentity: secIdentity withCertificateChain: identityCertChain];
}
if (optionsDictionary) {
CFRelease(optionsDictionary);
}
if (items) {
CFRelease(items);
}
NSError *error = nil;
if (securityError != errSecSuccess)
{
NSDictionary *info = nil;
#if !TARGET_OS_IPHONE
NSString *errorDescription = nil;
errorDescription = (__bridge_transfer NSString *)SecCopyErrorMessageString(securityError, NULL);
if (errorDescription)
{
info = #{ NSLocalizedDescriptionKey:errorDescription };
}
#endif
error = [NSError errorWithDomain: NSOSStatusErrorDomain
code: securityError
userInfo: info];
}
return error;
}

IOS::How to get the MPMediaquery Songsquery Artwork

I'm using this code for getting the Artwork, but it's not workout for me. What's the wrong in this code.Suggest me.
Thanks.
MPMediaQuery *mySongsQuery = [MPMediaQuery songsQuery];
NSArray *SongsList = [mySongsQuery collections];
for (MPMediaItemCollection *SongsArt in SongsList) {
NSArray *songs = [SongsArt items];
for (MPMediaItem *song in songs) {
if ([(MPMediaItem*)item valueForProperty:MPMediaItemPropertyAssetURL] != nil) {
CGSize artworkImageViewSize = CGSizeMake(40, 40);
MPMediaItemArtwork *artwork = [song valueForProperty:MPMediaItemPropertyArtwork];
UIImage * image = [artwork imageWithSize:artworkImageViewSize];
if (image!= nil)
{
imgv_songImageView.image = image;
}
else
{
imgv_songImageView.image = [UIImage imageNamed:#"musicD-jpeg.png"];
}
}
}
I assume you just want to loop through all songs in the music library so I don't see a need for collections:
MPMediaQuery *mySongsQuery = [MPMediaQuery songsQuery];
for (MPMediaItem *item in mySongsQuery.items) {
if (![[item valueForProperty:MPMediaItemPropertyIsCloudItem]boolValue]) {
CGSize artworkImageViewSize = CGSizeMake(40, 40);
MPMediaItemArtwork *artwork = [song valueForProperty:MPMediaItemPropertyArtwork];
UIImage *image = [artwork imageWithSize:artworkImageViewSize];
if (image) {
imgv_songImageView.image = image;
} else {
imgv_songImageView.image = [UIImage imageNamed:#"musicD-jpeg.png"];
}
}
}
I'm not sure why you want to check for the Asset URL but I've left it in.
Here i am posting the code to get the tracks and sorting them alphabetically. Its written in swift3.
/// Get all the songs in the device and display in the tableview
///
func getAllSongs() {
let query: MPMediaQuery = MPMediaQuery.songs()
let allSongs = query.items
allSongItems?.removeAll()
guard allSongs != nil else {
return
}
var index = 0
for item in allSongs! {
let pathURL: URL? = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
if pathURL == nil {
print("#Warning!!! Track : \(item) is not playable.")
} else {
let trackInfo = SongItem()
trackInfo.index = index
trackInfo.mediaItem = item
let title = item.value(forProperty: MPMediaItemPropertyTitle) as? String ?? "<Unknown>"
let artistName = item.value(forProperty: MPMediaItemPropertyArtist) as? String ?? "<Unknown>"
trackInfo.songName = title
trackInfo.artistName = artistName
trackInfo.isSelected = false
trackInfo.songURL = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
allSongItems?.append(trackInfo)
index += 1
}
}
// Sort the songs alphabetically
let sortedArray: [SongItem]? = allSongItems?.sorted {
$0.songName!.localizedCompare($1.songName!) == .orderedAscending
}
allSongItems?.removeAll()
if let arr = sortedArray {
allSongItems?.append(contentsOf: arr)
}
}