Why is my CFStream dying with errSSLDecryptionFail?
Why is my CFStream dying with errSSLDecryptionFail?
- Subject: Why is my CFStream dying with errSSLDecryptionFail?
- From: David Riggle <email@hidden>
- Date: Fri, 9 Nov 2007 15:32:08 -0800
I have a peer-to-peer app that uses a self-signed certificate for SSL
encryption purposes only. The code below works on all my Leopard test
machines except one. There doesn't seem to be anything unusual about
the test machine that fails (it is a MacBook Pro). Here's my log
showing the errSSLDecryptionFail error on the server side of the peer-
to-peer connection:
2007-11-09 14:40:19.330 - BonjourPublishSyncTask: connecting with SSL
2007-11-09 14:40:19.342 - SSL common name = riggle
2007-11-09 14:40:19.343 - SSL common name = riggle
2007-11-09 14:40:19.343 - SSL identity not found, creating one
Generating a 1024 bit RSA private key
...................................++++++
......................++++++
writing new private key to '/var/folders/Uh/Uharx-4QEB8ftDbbw6Clsk++
+TI/-Tmp-/tmpCert.pem'
-----
2007-11-09 14:40:19.932 - SSL common name = riggle
2007-11-09 14:40:19.932 - SSL common name = riggle
2007-11-09 14:40:19.933 - SSL common name = com.busymac.busysync
2007-11-09 14:40:19.933 - SSL identity found
2007-11-09 14:40:19.933 - BonjourPublishSyncTask: connection from
192.168.0.3:49387
2007-11-09 14:40:19.949 - BonjourPublishSyncTask: socket disconnected
with error Error Domain=kCFStreamErrorDomainSSL Code=-9845 "Operation
could not be completed. (kCFStreamErrorDomainSSL error -9845.)"
Here's the code that finds/creates my SecIdentityRef
+ (SecIdentityRef)findOrCreateSelfSignedIdentityInKeychain:
(SecKeychainRef)keychain
{
BOOL createdIdentity = NO;
SecIdentitySearchRef searchRef = nil;
OSStatus err;
err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_DECRYPT,
&searchRef);
if (err != noErr) return nil;
for (;;) {
SecIdentityRef mySSLIdentity = nil;
err = SecIdentitySearchCopyNext(searchRef, &mySSLIdentity);
if (err == errSecItemNotFound && !createdIdentity) {
Log(DEBUG, @"SSL identity not found, creating one");
// identity not found; create our own
[SSLUtilities addSelfSignedCertToKeychain:keychain];
// restart search
CFRelease(searchRef);
err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_DECRYPT,
&searchRef);
if (err != noErr) break;
createdIdentity = YES;
continue;
}
if (err != noErr) break;
// an identity found; see if it is ours
SecCertificateRef certificateRef = nil;
err = SecIdentityCopyCertificate(mySSLIdentity, &certificateRef);
if (err == noErr) {
CFStringRef commonName = nil;
err = SecCertificateCopyCommonName(certificateRef, &commonName);
if (err == noErr) {
Log(DEBUG, @"SSL common name = %@", commonName);
if ([(NSString *)commonName isEqual:SSL_COMMON_NAME]) {
Log(DEBUG, @"SSL identity found");
// found it
CFRelease(commonName);
CFRelease(certificateRef);
return mySSLIdentity;
}
}
if (commonName != nil) CFRelease(commonName);
}
// clean up
if (certificateRef != nil) CFRelease(certificateRef);
if (mySSLIdentity != nil) CFRelease(mySSLIdentity);
}
// clean up
if (searchRef != nil) CFRelease(searchRef);
return nil;
}
+ (void)addSelfSignedCertToKeychain:(SecKeychainRef)keychain
{
// create a path to a temp file
NSString *tmpPath = [NSTemporaryDirectory()
stringByAppendingPathComponent:@"tmpCert.pem"];
if (tmpPath == nil) return;
// launch openssl to create the certificate
NSTask *openSSLProcess = [NSTask launchedTaskWithLaunchPath:@"/usr/
bin/openssl" arguments:[NSArray arrayWithObjects:
@"req", @"-x509", @"-nodes", @"-days", @"3650",
@"-subj", [@"/C=US/ST=California/L=Los Altos/CN="
stringByAppendingString:SSL_COMMON_NAME],
@"-newkey", @"rsa:1024", @"-keyout", tmpPath, @"-out", tmpPath,
// @"-passin", @"pass:passphrase", nil]];
nil]];
// need to busy wait for subtask to finish, because we don't want to
go into another run loop & get more AsyncSocket call-backs
do {
[NSThread sleepForTimeInterval:0.1];
} while ([openSSLProcess isRunning]);
if ([openSSLProcess terminationStatus] != 0) {
Log(ERROR, @"openSSL task failed.");
return;
}
// add certificate to keychain
SecExternalFormat format = kSecFormatPEMSequence;
SecExternalItemType type = kSecItemTypeAggregate;
SecKeyImportExportParameters params;
memset(¶ms, 0, sizeof(params));
params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
// params.passphrase = CFSTR("passphrase");
// params.flags = kSecKeySecurePassphrase;
params.keyUsage = CSSM_KEYUSE_DECRYPT;
params.keyAttributes = CSSM_KEYATTR_EXTRACTABLE |
CSSM_KEYATTR_PERMANENT;
OSStatus err = SecKeychainItemImport(
(CFDataRef) [NSData dataWithContentsOfFile:tmpPath],
(CFStringRef) tmpPath,
&format,
&type,
0,
¶ms,
keychain,
NULL);
if (err != noErr) {
Log(ERROR, @"SecKeychainItemImport returned %d.", err);
}
[[NSFileManager defaultManager] removeFileAtPath:tmpPath handler:nil];
}
Here's my AsyncSocket/CFStream server-side code:
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError
*)err
{
if (err != nil)
Log(WARNING, @"BonjourPublishSyncTask: socket disconnected with
error %@", err);
}
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock
{
Log(DEBUG, @"BonjourPublishSyncTask: connecting with SSL");
// set up server side SSL
SecKeychainRef keychainRef = nil;
SecIdentityRef mySSLIdentity = nil;
OSStatus err;
// get SSL identity
err = SecKeychainCopyDefault(&keychainRef);
if (err != noErr) goto error;
mySSLIdentity = [SSLUtilities
findOrCreateSelfSignedIdentityInKeychain:keychainRef];
if (mySSLIdentity == nil) goto error;
// set stream properties
CFArrayRef ca = CFArrayCreate(NULL, (const void **)&mySSLIdentity, 1,
NULL);
NSDictionary *sslProperties = [NSDictionary
dictionaryWithObjectsAndKeys:
(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL,
kCFStreamSSLLevel,
kCFBooleanTrue, kCFStreamSSLAllowsExpiredCertificates,
kCFBooleanTrue, kCFStreamSSLAllowsExpiredRoots,
kCFBooleanTrue, kCFStreamSSLAllowsAnyRoot,
kCFBooleanFalse, kCFStreamSSLValidatesCertificateChain,
kCFNull, kCFStreamSSLPeerName,
ca, kCFStreamSSLCertificates,
kCFBooleanTrue, kCFStreamSSLIsServer,
nil];
CFReadStreamSetProperty([sock getCFReadStream],
kCFStreamPropertySSLSettings, sslProperties);
CFWriteStreamSetProperty([sock getCFWriteStream],
kCFStreamPropertySSLSettings, sslProperties);
CFRelease(ca);
// fall through
error:
// clean up
if (mySSLIdentity != nil) CFRelease(mySSLIdentity);
return YES;
}
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host
port:(UInt16)port
{
Log(INFO, @"BonjourPublishSyncTask: connection from %@:%d", host,
port);
}
Here's my client side code:
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock
{
NSDictionary *sslProperties = [NSDictionary
dictionaryWithObjectsAndKeys:
(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL,
kCFStreamSSLLevel,
kCFBooleanTrue, kCFStreamSSLAllowsExpiredCertificates,
kCFBooleanTrue, kCFStreamSSLAllowsExpiredRoots,
kCFBooleanTrue, kCFStreamSSLAllowsAnyRoot,
kCFBooleanFalse, kCFStreamSSLValidatesCertificateChain,
kCFNull, kCFStreamSSLPeerName,
kCFBooleanFalse, kCFStreamSSLIsServer,
nil];
CFReadStreamSetProperty([sock getCFReadStream],
kCFStreamPropertySSLSettings, sslProperties);
CFWriteStreamSetProperty([sock getCFWriteStream],
kCFStreamPropertySSLSettings, sslProperties);
return YES;
}
Any ideas why my CFStream would disconnect with errSSLDecryptionFail?
Why only on this one machine? If I bought a real certificate instead
of using a self-signed one, would that make a difference? Thanks for
any help!
Dave
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Macnetworkprog mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden