added support for using custom pkcs#12 file with password

This commit is contained in:
Remco
2014-12-22 15:37:25 +01:00
parent 2d927175e0
commit dbbdfb1965
5 changed files with 83 additions and 10 deletions

View File

@@ -46,6 +46,13 @@
# pragma mark Client Auth Settings
/// If set, this contains the location of a pkcs#12 certificate to be used for
/// sync authentication.
@property(readonly) NSString *syncClientAuthCertificateFile;
/// Contains the password for the pkcs#12 certificate.
@property(readonly) NSString *syncClientAuthCertificatePassword;
/// If set, this is the Common Name of a certificate in the System keychain to be used for
/// sync authentication. The corresponding private key must also be in the keychain.
@property(readonly) NSString *syncClientAuthCertificateCn;

View File

@@ -27,6 +27,8 @@ static NSString * const kConfigFilePath = @"/var/db/santa/config.plist";
/// The keys in the config file
static NSString * const kSyncBaseURLKey = @"SyncBaseURL";
static NSString * const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString * const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString * const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
static NSString * const kClientAuthCertificateIssuerKey = @"ClientAuthCertificateIssuerCN";
static NSString * const kServerAuthRootsDataKey = @"ServerAuthRootsData";
@@ -68,6 +70,14 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return [NSURL URLWithString:self.configData[kSyncBaseURLKey]];
}
- (NSString *)syncClientAuthCertificateFile {
return self.configData[kClientAuthCertificateFileKey];
}
- (NSString *)syncClientAuthCertificatePassword {
return self.configData[kClientAuthCertificatePasswordKey];
}
- (NSString *)syncClientAuthCertificateCn {
return self.configData[kClientAuthCertificateCNKey];
}

View File

@@ -31,6 +31,14 @@
/// certificate chain. This will override the trusted roots in the System Roots.
@property(nonatomic) NSData *serverRootsPemData;
/// If set and client certificate authentication is needed, the pkcs#12 file will be
/// loaded
@property(nonatomic) NSString *clientCertFile;
/// If set and client certificate authentication is needed, the password being used for
/// loading the clientCertFile
@property(nonatomic) NSString *clientCertPassword;
/// If set and client certificate authentication is needed, will search the keychain for a
/// certificate matching this common name and use that for authentication
/// @note: Not case sensitive

View File

@@ -112,31 +112,58 @@
/// Handles the process of locating a valid client certificate for authentication.
/// Operates in one of three modes, depending on the configuration in config.plist
///
/// Mode 1: if syncClientAuthCertificateCn is set, look for an identity in the keychain with a
/// Mode 1: if syncClientAuthCertificate is set, use the identity in the pkcs file
/// Mode 2: if syncClientAuthCertificateCn is set, look for an identity in the keychain with a
/// matching common name and return it.
/// Mode 2: if syncClientAuthCertificateIssuer is set, look for an identity in the keychain with a
/// Mode 3: if syncClientAuthCertificateIssuer is set, look for an identity in the keychain with a
/// matching issuer common name and return it.
/// Mode 3: use the list of issuer details sent down by the server to find an identity in the
/// Mode 4: use the list of issuer details sent down by the server to find an identity in the
/// keychain.
///
/// If a valid identity cannot be found, returns nil.
- (NSURLCredential *)clientCredentialForProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
__block OSStatus err = errSecSuccess;
__block SecIdentityRef _foundIdentity;
if (self.clientCertFile) {
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfFile:self.clientCertFile options:0 error:&error];
if (error) {
LOGE(@"Couldn't open client certificate %@ : %@", self.clientCertFile, [error localizedDescription]);
return (nil);
}
CFDataRef inPKCS12Data = (__bridge CFDataRef)data;
SecTrustRef trust = nil;
err = extractIdentityAndTrust(inPKCS12Data, self.clientCertPassword, &_foundIdentity, &trust);
if (err != errSecSuccess) {
LOGE(@"Couldn't load client certificate %@ : %d", self.clientCertFile, err);
return (nil);
}
CFRetain(_foundIdentity);
return [NSURLCredential credentialWithIdentity:_foundIdentity
certificates:nil
persistence:NSURLCredentialPersistenceForSession];
}
CFArrayRef cfIdentities = NULL;
err = SecItemCopyMatching((__bridge CFDictionaryRef)@{
(id)kSecClass : (id)kSecClassIdentity,
(id)kSecReturnRef : @YES,
(id)kSecMatchLimit : (id)kSecMatchLimitAll }, (CFTypeRef *)&cfIdentities);
(id)kSecClass : (id)kSecClassIdentity,
(id)kSecReturnRef : @YES,
(id)kSecMatchLimit : (id)kSecMatchLimitAll }, (CFTypeRef *)&cfIdentities);
if (err != noErr) {
LOGD(@"Client Trust: Failed to load client identities, SecItemCopyMatching returned: %d",
(int)err);
return nil;
}
NSArray *identities = CFBridgingRelease(cfIdentities);
__block SecIdentityRef _foundIdentity;
// Manually iterate through available identities to find one with an allowed issuer.
[identities enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
SecIdentityRef identityRef = (__bridge SecIdentityRef)obj;
@@ -197,6 +224,24 @@
persistence:NSURLCredentialPersistenceForSession];
}
OSStatus extractIdentityAndTrust(CFDataRef inP12data, NSString* password, SecIdentityRef *identity, SecTrustRef *trust)
{
NSDictionary *options = (password ? @{(__bridge id)kSecImportExportPassphrase: password} : @{});
CFArrayRef items = nil;
OSStatus err = SecPKCS12Import(inP12data, (__bridge CFDictionaryRef) options, &items);
if (err == errSecSuccess) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
*identity = (SecIdentityRef) CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*trust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
}
return err;
}
/// Handles the process of evaluating the server's certificate chain.
/// Operates in one of three modes, depending on the configuration in config.plist
///

View File

@@ -91,7 +91,10 @@ REGISTER_COMMAND_NAME(@"sync");
}
// Configure client auth
if ([config syncClientAuthCertificateCn]) {
if ([config syncClientAuthCertificateFile]) {
authURLSession.clientCertFile = [config syncClientAuthCertificateFile];
authURLSession.clientCertPassword = [config syncClientAuthCertificatePassword];
} else if ([config syncClientAuthCertificateCn]) {
authURLSession.clientCertCommonName = [config syncClientAuthCertificateCn];
} else if ([config syncClientAuthCertificateIssuer]) {
authURLSession.clientCertIssuerCn = [config syncClientAuthCertificateIssuer];