I want to ssl public key pinning in swift, I read lot of examples how to do that, last think who I can't find is How to compare two public keys in SecKey object format.
Example:
let serverPublicKey = SecTrustCopyPublicKey(secTrust) /*return SecKey object from actual SecTrust*/
let clientPublicKey = getLocalPublicKeyFromDer() /*return SecKey from .der local*/
how to compare them? At now I do that and it works:
if(serverPublicKey! as AnyObject).isEqual(clientPublicKey){
/*Key is the same, pinning OK!*/
}
find it way on gitHub: https://github.com/teamcarma/IOS-AlamofireDomain/blob/master/Source/ServerTrustPolicy.swift
but is cast to AnyObject a good idea? How to work isEqual on casted SecKey? Can any explain me?
ps.
Another idea is getting base64 from SecKey - I try and it also works, but it require a KeyChain temp operations and look no profesional.
Cited from the headers:
"Most SecKeychainItem functions will work on an SecKeyRef."*
You may cast SecKeyRef to a SecKeychainItem. If this is a valid operation (that is, the key is a keychain item), you may apply function
SecKeychainItemCreatePersistentReference
and get a CFData object, filled with attributes and data. Then check with memcpyon the bytes or cast it to a NSData object and check with isEqualToData. Don't forget to release the CFData object.
Edit
On iOS, as far as I known, the only reliable approach is to copy the data (or secret) into the keychain, using a temporary key, so that you can find it again, and then extract the data. It's cumbersome, but if you just implement it in a minimalistic way, it should not take more than 30 lines of code. I have a working example.
I The usual disclaimer: Use this at your own risk, and always be careful with security stuff.
iOS10 added:
CFDataRef _Nullable SecKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error)
so you can now create two Data (NSData) objects, then compare those.
Have a look at this answer for just getting the NSData: Can I get the modulus or exponent from a SecKeyRef object in Swift?
You can then compare the two NSData instances using isEqualToData:
I don't have expereince in the domain, but if they are two strings (irrespectiveof their content), you would basically do a simple check:
if(string1 == string2)
{
//condition logic
}
Related
I am in the beginning stages of developing an open-source utility for storing state in the Bundle UserDefaults.
I'm encountering an issue when I add non-Codable data types to my Dictionary of [String: Any].
I need to be able to vet the data before trying to submit it, because the UserDefaults.set(_:) method won't throw any errors. It just crashes.
So I want to make sure that the Dictionary that I'm submitting is kosher.
I can't just check if it's Codable, because it can sometimes say that it isn't, when the struct is actually good. (It's a Dictionary<String, Any>, and I can cram all kinds of things in there).
I need to validate that the Dictionary can produce a plist. If this were ObjC, I might use one of the NSPropertyListSerialization methods to test the Dictionary, but it appears as if this set of methods is not available to Swift.
According to the UserDefaults docs, there are a specific set of types and classes that are "plist-studly."
I think testing each type in the list is unacceptable. I need to see if I can find a way to test that won't be screwed the first time Apple updates an OS.
Is there a good way to test a Dictionary<String, Any> to see if it will make UserDefaults.set(_:) puke?
The Property List type set of UserDefaults is very limited. The supported types are
NSString → Swift String
NSNumber → Swift Int, Double or Bool
NSDate → Swift Date
NSData → Swift Data
Arrays and dictionaries containing the 4 value types.
Any is not supported unless it represents one of the 4 value or 2 collection types.
Property List compliant collection types can be written to UserDefaults with PropertyListSerialization (even in Swift).
There are two protocols to serialize custom types to Data
Codable can serialize structs and classes.
NSCoding can serialize subclasses of NSObject.
All types in the structs/classes must be encodable and decodable (means conform to the protocol themselves).
The APIs of PropertyListSerialization / PropertyListEncoder/-Decoder and NSKeyed(Un)Archiver provide robust error handling to avoid crashes.
UPDATE[1]: And, just because I like to share, here's the actual completed project (MIT License, as is most of my stuff)
UPDATE: This is the solution I came up with. Even though I greenchecked vadian's excellent answer, I decided to get a bit more picky.
Thanks to matt pointing out that I was looking under the wrong sofa cushions for the keys, I found the Swift variant of NSPropertyListSerialization, and I use that to vet the top level of the tree. I suspect that I'll need to refactor it into a recursive crawler before I'm done, but this works for now.
Here's the code for the _save() method at the time of this writing. It works:
/* ################################################################## */
/**
This is a private method that saves the current contents of the _values Dictionary to persistent storage, keyed by the value of the "key" property.
- throws: An error, if the values are not all codable.
*/
private func _save() throws {
#if DEBUG
print("Saving Prefs: \(String(describing: _values))")
#endif
// What we do here, is "scrub" the values of anything that was added against what is expected.
var temporaryDict: [String: Any] = [:]
keys.forEach {
temporaryDict[$0] = _values[$0]
}
_values = temporaryDict
if PropertyListSerialization.propertyList(_values, isValidFor: .xml) {
UserDefaults.standard.set(_values, forKey: key)
} else {
#if DEBUG
print("Attempt to set non-codable values!")
#endif
// What we do here, is look through our values list, and record the keys of the elements that are not considered Codable. We return those in the error that we throw.
var valueElementList: [String] = []
_values.forEach {
if PropertyListSerialization.propertyList($0.value, isValidFor: .xml) {
#if DEBUG
print("\($0.key) is OK")
#endif
} else {
#if DEBUG
print("\($0.key) is not Codable")
#endif
valueElementList.append($0.key)
}
}
throw PrefsError.valuesNotCodable(invalidElements: valueElementList)
}
}
I'm trying to generate a public (and private) key pair using the SecKey class from Xamarin.iOS.
The KeySize is defined to 1024 bit and this seems to work (if I change this value, the length of the result array is changing too).
I generate the keys with
SecKey.GenerateKeyPair(CreateRsaParams(), out publicKey, out privateKey);
byte[] key = publicKey.GetExternalRepresentation().ToArray()
(CreateRsaParams() is a function giving back a NSDictionary with the required data)
The problem is: I get a byte array (public key) with 140 Bytes - but depended on the key size it should have only 128 Byte - and I need a 128 Byte public key for data exchange with an other system
(by the way - using PCLCrypto is not an option for me since the project is not allowed to use this 3rd party component)
Does anyone know the problem and know a solution?
Okay, problem solved.
If anyone is facing the same problem, you can find the solution at
https://forums.developer.apple.com/thread/111109
The problem was not the key, but the wrongly formulated requirement.
I use gzip library to convert data to gzip data
If I want to get string from data I do String(data: data, encoding: .utf8) . But if I do the same for gzip data I get nil string, because as far as I understand it's impossible to convert gzip data to string with .utf8 encoding.
I need that because I want to compare data with a server developer (because he sometimes he says that my data is incorrect format, but sometimes it's ok, and that's strange because I use the same library, and I have thoughts that may be the problem is on server side).
Is there any way to convert gzip data to any string to compare it then later?
If this is just for debug purpose, then I think the quickest way is:
let myGZipNSData = myGZipData as! NSData //I wrote a "!" just for the sample)
print("myGZipNSData: \(myGZipNSData)")
It should output:
myGZipNSData: <5b226d79 41727261 7956616c 75653022 2c226d79 41727261 7956616c 75653122 5d>
This relies on -description method of NSData which print "<hexValue>".
Do not rely on it on release version, almost never rely on -description (yes, Apple could change the behavior of -description in next release. It happened on a different class).
Other way (safer) to get almost the same result, you can check this question: How to convert Data to hex string in swift which you get you the same result (almost, less space, and no </>) as the previous one.
Other way: Base 64
Use base64EncodedString() method of Data:
let base64Str = myGZipData?.base64EncodedString()
print("base64Str: \(base64Str!)")
Output:
base64Str: WyJteUFycmF5VmFsdWUwIiwibXlBcnJheVZhbHVlMSJd
Now, there should be other way:
Have a array representation with Int values (between 0/255 instead of hex), but it seems to be for debug purpose so check if you can "reproduce the current solutions" (working/fast implementation) I gave you, and check with the server developer what he/she can do on his/her side too to compare.
Note:
For the purpose of this sample, myGZipData is constructed this way:
let array = ["myArrayValue0", "myArrayValue1"]
let myGZipData = try? JSONSerialization.data(withJSONObject: array, options:[])
It's not really a GZipData, but the goal was to quickly have a Data object "big enough".
I want to implement a web service to recover an array of my entity PictureCaptures :
PictureCaptures
---------------
- description : string
- captureDate : DateTime
- photoBinary : byte[]
The web service will be mainly called by an iOS application.
What's the best way to implement it, because of the byte array attribute?
Am I suppose to return the byte array without any transformation, as a simple JSON attribute? If yes, how to interpet the JSON response ? -In this case JSONObjectWithData:options:error: doesn't work, too much data and memory issue)-
Thank you for your help.
I would suggest you add two resources: one for the meta data (description, captureDate and so on) and one for the binary data. Let the meta data resource contain a link to the binary photo data.
Like this:
GET /images/1234
Response:
{
description: "Nice photo",
captureDate: "2012-04-23T18:25:43.511Z",
photoData: "http://example.org/images/1234/photo"
}
and http://example.org/images/1234/photo returns the raw photo data
(see also See also The "right" JSON date format for a discussion on date formats).
when you get JSON responce you shoud convert the btye array to NSData.
first add Base64.h and m file to the project ( you can find it easily on internet)
then import Base64.h
from your JSON data
NSString *data= [yourJSONDict objectForKey:#"photoBinary"];
NSData* imageData = [data base64DecodedData];
UIImage *imag=[UIImage imageWithData:imageData];
[yourImageView setImage:imag];
this might help you.
When debugging in XCode, the debugger is telling me that the NSDictionary object contains 1 key/value pair. When the debug console prints the description of the key/value pair is shows:
Printing description of testdictionary:
{
"Unknown (<1809>)" = <000000ff>;
}
I want to extract both the <1809> and the <000000ff>. I have tried both the valueForKey and objectforKey methods as described elsewhere on this site. But I think I am having difficulty understanding what is the key and what is the value here.
For example, is "Unknown (<1809>)" the key? Or is "<1809>" the key? Or is 1809 the key?
Thanks Tim for the reply.
The NSDictionary comes from the CoreBluetoothFramework the didDiscoverPeripheral: method is called and passes advertising data into an NSDictionary called "advertisementData".
This dictionary contains all sorts of stuff like the advertising channel and device name. However, I am trying to extract just the advertising data from "advertisementData". I used the key provided by corebluetooth "CBAdvertisementDataServiceDataKey" like this:
NSData* information;
information = [advertisementData objectForKey:CBAdvertisementDataServiceDataKey];
I was declaring "information" as an NSDictionary* object before. But changed it to NSData* after some more reading on Apples documentation. The result is the same. The debugger says that it contains a key/value pair as follows:
"Unknown (<1809>)" = <000000ff>;
Thanks again.
Nik
When you do not know the keys that are present in the dictionary, for example, because the key-value pairs come from an external source, you can use enumerateKeysAndObjectsUsingBlock: method to go through all key-value pairs present in the dictionary:
[testdictionary enumerateKeysAndObjectsUsingBlock::^(id key, id object, BOOL *stop) {
NSLog(#"The key is %#", key);
NSLog(#"The value is %#", object);
}];
I've never seen this before so this is nothing more than an educated guess:
The dictionary may have been casted from CFDictionaryRef, in which case both the key and value are const void * (instead of NSObject). The key might have been some Core Foundation type holding a file descriptor (hence 1809). The value could be a pointer (or an integer casted to a "pointer": (void *)32).
You should try and find out where the dictionary originates from, because it's the only thing that's going to give you any valuable information.
Update: the docs state that the value of CBAdvertisementDataServiceDataKey is a dictionary. The keys are CBUUID objects, representing CBService UUIDs and the values are NSData objects. (1)