We are preparing for a release and during upgrade tests I came around to a blocking problem. We are saving some stuff to the keychain, and after doing the app update, the app keeps throwing errSecItemNotFound after attempting SecItemCopyMatching.
CFTypeRef identity_ref = NULL;
const void *keys[] = { kSecClass, kSecReturnRef, kSecValuePersistentRef };
const void *values[] = { kSecClassIdentity, kCFBooleanTrue, persistent_ref };
CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
3, NULL, NULL);
OSStatus status = SecItemCopyMatching(dict, &identity_ref);
if (DEBUG) NSLog(#"%s --> status: %d", __func__, (int)status);
Now, I've never personally used Keychain. This code is written by someone that isn't working anymore. I will start reading up on keychain to understand how it works, but I decided to post a question here in the meantime in the hope that I will receive an answer before I figure it out by myself (the release is waiting :D).
Ok, so the problem the two versions pointed to two different keychain-sharing-groups. Mofos...
Related
I have a private key in p12 which has empty passphrase set.
Now when I'm trying to import this private key using SecPKCS12Import on OS X or iOS, I've got an error (equivalent on Windows works).
- (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?1:0),
NULL, NULL);
CFArrayRef items = NULL;
securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data,
optionsDictionary,
&items);
I've tried different combinations when password is empty:
optionsDictionary = NULL
optionsDictionary with no values
optionsDictionary with #"" value for key kSecImportExportPassphrase
It always ends with securityError not equal to errSecSuccess. Respectively:
securityError=-25260 "Passphrase is required for import/export."
securityError=-25260 "Passphrase is required for import/export."
securityError=-25264 "MAC verification failed during PKCS12 import (wrong password?)"
Now I'm Ok that it doesn't work. I understudy that p12 without a password is a security threat, but If this is the reason why it doesn't work I need some documentation which states that.
I've tried to Google that without luck so far.
I've also tried to import this file to OS X Keychain application with same result (invalid password), so this must be problem with SecPKCS12Import.
Or maybe there is a way to overcome this problem?
I believe this is a defect with the Cocoa SDK, see rdar://22909471.
Unfortunately, this radar has been closed as a dupe of an existing radar. I would file an additional bug report with Apple to elevate this issue. I can think of two alternatives:
Add a password your p12 blob.
Convert the certificate to DER encoding. This answer may help.
This seems to be fixed by now, I've tested it on iOS 10. I can use an empty string as the password for my p12. However it seems like the kSecImportExportPassphrase is still required.
I'm writing iOS code to authenticate with an HTTPS proxy server, and need to use the Core Foundation APIs in order to construct the "Proxy-Authorization" header. I'm blocked trying to build a valid CFHTTPAuthenticationRef object, though.
Upon receiving the '407 Proxy authentication required' response with a 'Proxy-Authenticate: Basic realm="example.com"' header, I'm calling CFHTTPAuthenticationCreateFromResponse. This returns a CFHTTPAuthenticationRef object that is invalid; the error is -1000, or kCFStreamErrorHTTPAuthenticationTypeUnsupported.
From looking at the very outdated source code available at http://www.opensource.apple.com/source/CFNetwork/CFNetwork-129.20/HTTP/CFHTTPAuthentication.c, this should all be working fine. I've verified in the debugger that some of the internal structures, like _schemes, are in fact being parsed properly, so I know my header value is correct. Clearly the code inside CFHTTPAuthenticationCreateFromResponse has changed, but I'm at a loss as to what I need to do to make this work.
From stepping through the assembly code, it seems like it may be related to a lack of URL. There is no public API to add a URL to a response, and the fact it's not needed for Basic and Digest auth is odd.
I've observed this on OSX Mavericks, iOS 7 simulator, and iOS 8 simulator. I've boiled this down to a simple repro function, pasted below. I've also tried using a test proxy server and CFReadStream, creating the response from CFReadStreamCopyProperty(requestStream, kCFStreamPropertyHTTPResponseHeader), to no avail (which, presumably, would associate a URL with the response object).
void testCFAuthentication()
{
Boolean result;
CFStringRef str;
const char *rawResponse = "HTTP/1.1 407 Proxy authentication required\r\nProxy-Authenticate: Basic realm=\"example.com\"\r\n\r\n";
CFHTTPMessageRef responseMessage = NULL;
responseMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false);
// Optional hack, doesn't help
/*
CFURLRef url = NULL;
url = CFURLCreateWithString(NULL, CFSTR("https://example.com/"), NULL);
_CFHTTPMessageSetResponseURL(responseMessage, url);
NSLog(#"_CFHTTPMessageSetResponseURL called\n");
*/
result = CFHTTPMessageAppendBytes(responseMessage, (uint8_t *)rawResponse, strlen(rawResponse));
NSLog(#"CFHTTPMessageAppendBytes result: %d\n", (int)result);
NSLog(#"CFHTTPMessageIsHeaderComplete: %d\n", (int)CFHTTPMessageIsHeaderComplete(responseMessage));
NSLog(#"CFHTTPMessageGetResponseStatusCode: %d\n", (int)CFHTTPMessageGetResponseStatusCode(responseMessage));
NSLog(#"CFHTTPMessageCopyResponseStatusLine: %s\n", CFStringGetCStringPtr(CFHTTPMessageCopyResponseStatusLine(responseMessage), kCFStringEncodingUTF8));
NSLog(#"Proxy-Authenticate header value: %s\n", CFStringGetCStringPtr(CFHTTPMessageCopyHeaderFieldValue(responseMessage, CFSTR("Proxy-Authenticate")), kCFStringEncodingUTF8));
CFHTTPAuthenticationRef auth = NULL;
auth = CFHTTPAuthenticationCreateFromResponse(NULL, responseMessage);
NSLog(#"CFHTTPAuthenticationCreateFromResponse:\n"); CFShow(auth);
if (auth) {
CFStreamError error = { 0 };
result = CFHTTPAuthenticationIsValid(auth, &error);
if (!result) {
NSLog(#"CFHTTPAuthenticationIsValid: false, error %d (0x%x), domain %ld (0x%lx)\n", error.error, error.error, error.domain, error.domain);
} else {
NSLog(#"CFHTTPAuthenticationCopyMethod=%s\n", CFStringGetCStringPtr(CFHTTPAuthenticationCopyMethod(auth), kCFStringEncodingUTF8));
NSLog(#"authRealm=%s\n", CFStringGetCStringPtr(CFHTTPAuthenticationCopyRealm(auth), kCFStringEncodingUTF8));
}
};
}
Output:
2015-02-13 11:01:05.654 CFHTTPMessageAppendBytes result: 1
2015-02-13 11:01:05.655 CFHTTPMessageIsHeaderComplete: 1
2015-02-13 11:01:05.656 CFHTTPMessageGetResponseStatusCode: 407
2015-02-13 11:01:05.657 CFHTTPMessageCopyResponseStatusLine: HTTP/1.1 407 Proxy authentication required
2015-02-13 11:01:05.657 Proxy-Authenticate header value: Basic realm="example.com"
2015-02-13 11:01:05.658 CFHTTPAuthenticationCreateFromResponse:
<CFHTTPAuthentication 0x7fe0d36017f0>{state = Failed; scheme = <undecided>, forProxy = true}
2015-02-13 11:01:05.658 CFHTTPAuthenticationIsValid: false, error -1000 (0xfffffc18), domain 4 (0x4)
You encounter same problem as I, but in a bit different flavor.
Problem is that CFHTTPAuthenticationCreateFromResponse works only with response fetched from CFHTTPStream.
I found a solution see my answer.
Expose private API (this will cause that you will fail Apple review).
// exposing private API for workaround
extern void _CFHTTPMessageSetResponseURL(CFHTTPMessageRef, CFURLRef);
Add add URL to response using this private API:
_CFHTTPMessageSetResponseURL(responseMessage,
(__bridge CFURLRef)[NSURL URLWithString: "https://example.com/"]);
auth = CFHTTPAuthenticationCreateFromResponse(NULL, responseMessage);
We are using WL.EncryptedCache.open to open the locat storage. This at times takes 4-8 seconds to get the encryption key. Please suggest on how to resolve this performance issue ?
We are using Worklight 6.0
You can generate the secure token locally by overwriting the secure random call: WL.EncryptedCache.secureRandom = function(callback){callback(Math.random()+"")}. That way you don't have to hit the server to get it. It will be significantly less secure and I do not recommend it. Beyond that and running the application on a faster device, there's nothing you can do. Generating a secure key is an expensive operation.
Alternatively, you can replace Math.random above with a cordova plugin exec call that gets a cryptographically secure random string using SecRandomCopyBytes. Some example code:
int bytes = 32;
uint8_t randBytes[bytes];
int rc = SecRandomCopyBytes(kSecRandomDefault, (size_t)bytes, randBytes);
if (rc != 0) {
//handle failure
}
NSMutableString* hexEncoded = [NSMutableString new];
for (int i = 0; i < bytes; i++) {
[hexEncoded appendString:[NSString stringWithFormat:#"%02x", randBytes[i]]];
}
NSString* randomStr = [NSString stringWithFormat:#"%#", hexEncoded];
There are getting started modules that explain how to write cordova plugins for iOS here.
Updates
Edit 2/6/14:
I created a local Apache Tomcat server to test SSL w/ certificate authentication. I was successful! Everything works as expected, using both of my approaches below. (MKNetworkKit, and custom code). While this does tell me my code is working, my original issue is still not solved. I updated the title of the question to more specifically reflect the issue. Does anyone know if SAP Portal needs special settings to accept certificates from an iOS app? Remember, I was able to successfully authenticate using Safari mobile after importing the CA and .p12 into the shared keychain, I was only unsuccessful in code (which I now know the code works, just not with the portal).
I am creating a very simple iOS7 Cordova 3.2 application with a custom plugin to get data from an SSL web service by providing only a .p12 certificate for authentication (no basic auth or other user credentials needed). I am performing all tests on a physical iPad (no simulator). The web service lives on a development SAP NetWeaver portal box using a self signed certificate. For now, I imported the server's CA into the iOS keychain to avoid certificate trust errors. For testing purposes, my .p12 certificate is bundled locally inside the app, in the root of the mainBundle.
When trying to connect to the web service I get the follow error in the console:
CFNetwork SSLHandshake failed (-9825)
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9825)
Error Domain=NSURLErrorDomain Code=-1205 "The server “myhostremoved.com” did not accept the certificate." UserInfo=0x14e99000 {NSErrorFailingURLStringKey=https://myhostremoved.com/sslwebservice/, NSErrorFailingURLKey=https://myhostremoved.com/sslwebservice/, NSLocalizedDescription=The server “myhostremoved.com” did not accept the certificate., NSUnderlyingError=0x14d9b1d0 "The server “myhostremoved.com” did not accept the certificate.", NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x14d94720>}
According to Apple's documentation site, the -9825 error referes to a bad certificate.
There are many questions on SO related to what I am trying to do, but none specifically pertaining to the error I am seeing. I approached the development of the code in two different ways.
First I tried to use code already on SO, adapting it to my use case. See code below:
- (void)startConnection:(CDVInvokedUrlCommand*)command {
NSDictionary *options = [command.arguments objectAtIndex:0];
NSURL *serverURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#", [options objectForKey:#"host"]]];//hostname provided by Cordova plugin, but could just as easily be hardcoded here
NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:serverURL];
NSURLConnection *connection = nil;
connection = [[NSURLConnection alloc] initWithRequest:connectionRequest delegate:self startImmediately:YES];
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
// gets a certificate from local resources
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"mycert" ofType:#"p12"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
SecIdentityRef identity;
// extract the ideneity from the certificate
[self extractIdentity :inPKCS12Data :&identity];
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate (identity, &certificate);
const void *certs[] = {certificate};
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
// create a credential from the certificate and ideneity, then reply to the challenge with the credential
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
- (OSStatus)extractIdentity:(CFDataRef)inP12Data :(SecIdentityRef*)identity {
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("MyCertPassw0rd");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inP12Data, options, &items);
if (securityError == 0) {
CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
}
if (options) {
CFRelease(options);
}
return securityError;
}
On my second approach, I tried to use the MKNetworkKit library, which abstracts away alot of the code needed to interface with the certificate. All you need to do is provide the path to the certificate and the password. Again, I get the same error as above. This code is below.
- (void)startConnection:(CDVInvokedUrlCommand*)command {
NSDictionary *options = [command.arguments objectAtIndex:0];
NSURL *serverURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#", [options objectForKey:#"host"]]];//hostname provided by Cordova plugin, but could just as easily be hardcoded here
MKNetworkEngine *engine = [[MKNetworkEngine alloc] initWithHostName:serverURL customHeaderFields:nil];
MKNetworkOperation *op = [engine operationWithPath:nil params:nil httpMethod:#"GET" ssl:YES];
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"mycert" ofType:#"p12"];
[op setShouldContinueWithInvalidCertificate:YES];
op.clientCertificate = thePath;
op.clientCertificatePassword = #"MyCertPassw0rd";
[op addCompletionHandler:^(MKNetworkOperation *operation) {
NSLog(#"[operation responseData]-->>%#", [operation responseString]);
}errorHandler:^(MKNetworkOperation *errorOp, NSError* err) {
NSLog(#"MKNetwork request error : %#", [err localizedDescription]);
}];
[engine enqueueOperation:op];
}
I get the same error using both approaches. Any ideas?
Known information
I know that the app is finding .p12 certificate because when I fudge the path to the .p12 cert, I receive an error saying it cannot find the certificate which I don't normally see otherwise.
I know the password I am providing for the certificate file is correct because when I fudge the password, I receive an error regarding the password I don't normally see otherwise.
I don't think certificate trust is an issue because if I remove my server's CA from the iOS keychain, I get an error in the console specifically stating the server cannot be trusted. After adding the CA back, this error no longer occurs, but I get same error as above (-9825)
Others tests
When providing basic auth in code (bypassing certificate authentication), everything works as expected and I receive no errors.
I have also tried all the same steps above, but using a .pfx file instead of my .p12 certificate, same errors.
The SSL service works in mobile Safari. I imported the .p12 certificate into my keychain via the iPhone Configuration utility, and tested the web service via mobile safari. Everything works as expected, no errors occur (no trust errors either), and I receive the expected output.
I am also able to successfully test the web service on my desktop using the rest-client utility
TL;DR
I am trying to authenticate to an SSL web service using only a .p12 certificate in objective-c. I know the server and web service both work with the cert for auth, but I keep getting errors when I try to establish the connection in objective-c, so something must be wrong in my code. Please help!
I finally was able to resolve this problem. I found the answer here
http://oso.com.pl/?p=207&lang=en
It looks like the certificate was being sent twice, causing an error on the server.
Incase the above link dies, I found the same solution already on SO (but for a different question and error).
When using Client Certificate Authentication, why do I keep getting NSURLErrorDomain Code=-1206?
Ultimately, changing this:
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:(__bridge NSArray*)certsArray persistence:NSURLCredentialPersistenceNone];
to this:
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:nil persistence:NSURLCredentialPersistenceNone];
solved my issue. It would be nice if anyone could offer more insight into this issue.
In some cases handshake may fail because server also asks intermediate and root certificates.
To fix it do the following:
Authentication challenge part:
OSStatus status = extractIdentityAndTrust(inP12data, &myIdentity, &myTrust, password);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
CFIndex count = SecTrustGetCertificateCount(myTrust);
NSMutableArray* myCertificates = [NSMutableArray arrayWithCapacity:count];
if (count > 1) {
for (int i = 1; i < count; ++i) {
[myCertificates addObject:(__bridge id)SecTrustGetCertificateAtIndex(myTrust, i)];
}
}
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:myCertificates persistence: NSURLCredentialPersistenceForSession];
Extracting method
OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
SecIdentityRef *outIdentity,
SecTrustRef *outTrust,
CFStringRef keyPassword) {
OSStatus securityError = errSecSuccess;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { keyPassword };
CFDictionaryRef optionsDictionary = NULL;
/* Create a dictionary containing the passphrase if one
was specified. Otherwise, create an empty dictionary. */
optionsDictionary = CFDictionaryCreate(
NULL, keys,
values, (keyPassword ? 1 : 0),
NULL, NULL);
CFArrayRef items = NULL;
securityError = SecPKCS12Import(inPKCS12Data,
optionsDictionary,
&items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,
kSecImportItemIdentity);
CFRetain(tempIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
CFRetain(tempTrust);
*outTrust = (SecTrustRef)tempTrust;
}
if (optionsDictionary)
CFRelease(optionsDictionary);
if (items)
CFRelease(items);
return securityError; }
Please note that loop from 1 (not 0) is not a mistake. First cert is already added to "myIdentify", so only other certs should be passed as certificates, otherwise most likely you'll receive an error during handshake because of duplicating.
What is the proper way to check the basic constraints of a certificate ? Below is the code I am currently using to mimic the icons show in the Keychain (the reason for below is that while we have a
SFChooseIdentityPanel * identityPanel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
the equivalent for selecting a CA or a Host/Leaf cert does not exist. And that is useful when setting up/locking down SSL connections.
Unfortunately - I cannot find the OID strings in the header files to cleanly extract the CA:TRUE or false form a cert (or am using the API in the wrong way).
So the questions are
How do I cleanly check for CA:TRUE - while below works - I can imagine that it would get foiled by a malformed cert with text strings in the right places.
Secondly - I am using a heuristic of Issuer==Subject to detect self signed. Is there a cleaner way to do this ?
Finally - from trial and error - below seems to mimic apple her choices in the keychain - but the docs are rather hard to understand. Does kSecTrustResultProceed really mean that the user has set an override and kSecTrustResultUnspecified that in fact the trust is specified by the system basic trust ? While it 'work's - I cannot quite understand the exact interpretation of the docs.
Thanks a lot. Code below.
Dw.
#implementation NSImage (CertificateSelectionPanelExtensions)
+(NSImage *)iconForCertificate:(SecCertificateRef)certificateRef small:(BOOL)isSmall
{
BOOL isCA = FALSE, isInvalid = TRUE, isUserTrust = FALSE;
NSString * issuer = nil, * subject = nil;
const void *keys[] = { kSecOIDX509V1SubjectName, kSecOIDX509V1IssuerName, kSecOIDExtendedKeyUsage, kSecOIDBasicConstraints };
CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks);
CFDictionaryRef vals = SecCertificateCopyValues(certificateRef, keySelection, NULL);
CFArrayRef values;
CFDictionaryRef dict;
dict = CFDictionaryGetValue(vals, kSecOIDBasicConstraints );
values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
if (values) {
for(int i = 0; i < CFArrayGetCount(values); i++) {
CFDictionaryRef subDict = CFArrayGetValueAtIndex(values, i);
// We cannot find OID defines for the CA - so rely on the lower libraries to give us a string
// of sorts. Not a good idea - as now this code can be foiled by a actual string.
//
NSString *k = [NSString stringWithFormat:#"%#", CFDictionaryGetValue(subDict, kSecPropertyKeyLabel)];
NSString *v = [NSString stringWithFormat:#"%#", CFDictionaryGetValue(subDict, kSecPropertyKeyValue)];
if ([#"Certificate Authority" isEqualToString:k] && [#"Yes" isEqualToString:v]) {
isCA = TRUE;
}
}
};
// Fall back on a simple self-sign check if there where no kSecOIDBasicConstraints.
// set on the cert. Note that it is a DN is equal check - in some cases
// doing a 509v3 Subject/Authority Key Identifier may be better ?? XXXX
//
if (!isCA && !values) {
dict = CFDictionaryGetValue(vals, kSecOIDX509V1SubjectName);
values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
subject = [NSString stringWithFormat:#"%#", values];
dict = CFDictionaryGetValue(vals, kSecOIDX509V1IssuerName);
values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
issuer = [NSString stringWithFormat:#"%#", values];
// Crap way of secondgessing CA ness.
if ([issuer isEqualToString:subject])
isCA = TRUE;
};
SecPolicyRef policy = SecPolicyCreateBasicX509(); // SecPolicyCreateSSL(YES,nil);
CFArrayRef chain = CFArrayCreate(NULL, (const void**)(&certificateRef), 1, NULL);
SecTrustRef trustRef;
SecTrustCreateWithCertificates(chain, policy, &trustRef);
SecTrustResultType result;
SecTrustEvaluate (trustRef, &result);
if(result == kSecTrustResultProceed) {
isUserTrust = TRUE;
isInvalid = FALSE;
} else
if (result == kSecTrustResultUnspecified)
isInvalid = FALSE;
CFRelease(trustRef);
CFRelease(chain);
// Images as per /System/Library/Frameworks/SecurityInterface.framework/Versions/A/Resources
// Cert <Small | Large> <Personal | Root> [_Invalid | _UserTrust ]
//
return [NSImage imageNamed:[NSString stringWithFormat:#"Cert%#%#%#",
isSmall ? #"Small" : #"Large",
isCA ? #"Root" : #"Personal",
isInvalid ? #"_Invalid" : (isUserTrust ? #"_UserTrust" : #"")]];
}
#end
The basicConstraints extension is defined in X.509 as follows:
basicConstraints EXTENSION ::= {
SYNTAX BasicConstraintsSyntax
IDENTIFIED BY id-ce-basicConstraints }
BasicConstraintsSyntax ::= SEQUENCE {
cA BOOLEAN DEFAULT FALSE,
pathLenConstraint INTEGER (0..MAX) OPTIONAL }
This, in turn, is encoded according to the Distinguished Encoding Rules (X.690). The individual parts of the BasicConstraintsSyntax sequence do not have their own OIDs.
I wouldn’t claim to be an expert, but I don’t think there is a problem with the test for the cA flag that you are doing. I don’t think the [NSString stringWithFormat:#"%#", ...] part is necessary, mind.
As for checking for self-signed certificates, it seems not unreasonable to test the subject and issuer names; obviously that really tells you whether the certificate claims to be self-signed, and to actually test that it is you’d need to check the signature yourself (whether that’s something you want to do, I don’t know). FYI, on the topic of the key identifiers, according to RFC3280, in the specific case of a self-signed certificate, the authority key identifier can be omitted, so a certificate without an authority key identifier might be an indication that the certificate was self-signed, but it’s entirely possible for someone to deliberately issue a malformed certificate without authority key identifier.
The final question appears to be cleared up by looking at the docs, which indicate that the values mean roughly what you say.
The other thing worth saying is that there is code out there that can help with this kind of thing; for instance, Jens Alfke’s MYCrypto library
Edit (8th Feb 2012)
According to the X.509 Style Guide, it’s possible for the subject name to be empty, in which case you would need to look at the subjectAltName extension instead. It’s very unlikely that this would break a test for self-signed certificates that relied on comparing the subject and issuer names — it seems unreasonable to issue a certificate to yourself providing a DN for the issuer name but then leaving the subject name empty. However, it’s worth bearing in mind.