1.1 --- a/MYCertGen.m Thu Apr 09 22:47:11 2009 -0700
1.2 +++ b/MYCertGen.m Sun Apr 12 22:02:20 2009 -0700
1.3 @@ -177,7 +177,7 @@
1.4 // that's binary 111111000; see http://tools.ietf.org/html/rfc3280#section-4.2.1.3
1.5 CSSM_X509_EXTENSION keyUsage = {
1.6 CSSMOID_KeyUsage,
1.7 - true,
1.8 + false, // non-critical
1.9 CSSM_X509_DATAFORMAT_PARSED,
1.10 {.parsedValue = &keyUsageBits}
1.11 };
1.12 @@ -187,11 +187,11 @@
1.13 UInt32 count;
1.14 const CSSM_OID *oids;
1.15 };
1.16 - CSSM_OID usageOids[2] = {CSSMOID_ServerAuth, CSSMOID_ClientAuth};
1.17 - struct ExtendedUsageList extUsageBits = {2, usageOids};
1.18 + CSSM_OID usageOids[3] = {CSSMOID_ServerAuth, CSSMOID_ClientAuth, CSSMOID_ExtendedKeyUsageAny};
1.19 + struct ExtendedUsageList extUsageBits = {3, usageOids};
1.20 CSSM_X509_EXTENSION extendedKeyUsage = {
1.21 CSSMOID_ExtendedKeyUsage,
1.22 - true,
1.23 + false, // non-critical
1.24 CSSM_X509_DATAFORMAT_PARSED,
1.25 {.parsedValue = &extUsageBits}
1.26 };
1.27 @@ -466,10 +466,9 @@
1.28 Log(@"CSSM_CL_HANDLE = %p", cl);
1.29 CAssert(cl);
1.30
1.31 - MYKeychain *keychain = [MYKeychain allKeychains];
1.32 - Log(@"Looking for a key pair...");
1.33 - MYPrivateKey *privateKey = [[keychain enumeratePrivateKeys] nextObject];
1.34 - Log(@"Using key pair { %@, %@ }", privateKey, privateKey.publicKey);
1.35 + Log(@"Generating a key pair...");
1.36 + MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 2048];
1.37 + Log(@"Key-pair = { %@, %@ }", privateKey, privateKey.publicKey);
1.38
1.39 Log(@"...creating cert...");
1.40
1.41 @@ -483,6 +482,7 @@
1.42 ));
1.43 Log(@"Cert = %@", cert);
1.44 CAssert(cert);
1.45 + [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
1.46
1.47 Log(@"Cert name = %@", cert.commonName);
1.48 Log(@"Cert email = %@", cert.emailAddresses);
1.49 @@ -491,5 +491,7 @@
1.50 CAssertEqual(cert.emailAddresses, $array(@"waldo@example.com"));
1.51 CAssertEqual(cert.publicKey.publicKeyDigest, privateKey.publicKeyDigest);
1.52
1.53 - [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
1.54 + CAssert([[MYKeychain defaultKeychain] addCertificate: cert]);
1.55 +
1.56 + CAssert([cert setUserTrust: kSecTrustResultProceed]);
1.57 }
2.1 --- a/MYCertificate-iPhone.m Thu Apr 09 22:47:11 2009 -0700
2.2 +++ b/MYCertificate-iPhone.m Sun Apr 12 22:02:20 2009 -0700
2.3 @@ -15,6 +15,10 @@
2.4 @implementation MYCertificate
2.5
2.6
2.7 ++ (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
2.8 + return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
2.9 +}
2.10 +
2.11 /** Creates a MYCertificate object for an existing Keychain certificate reference. */
2.12 - (id) initWithCertificateRef: (SecCertificateRef)certificateRef {
2.13 self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef];
2.14 @@ -34,6 +38,10 @@
2.15 }
2.16
2.17
2.18 +- (BOOL)isEqualToCertificate:(MYCertificate*)cert {
2.19 + return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
2.20 +}
2.21 +
2.22 @synthesize certificateRef=_certificateRef;
2.23
2.24 - (NSData*) certificateData {
3.1 --- a/MYCertificate.h Thu Apr 09 22:47:11 2009 -0700
3.2 +++ b/MYCertificate.h Sun Apr 12 22:02:20 2009 -0700
3.3 @@ -22,11 +22,17 @@
3.4 }
3.5
3.6 /** Creates a MYCertificate object for an existing Keychain certificate reference. */
3.7 ++ (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef;
3.8 +
3.9 +/** Initializes a MYCertificate object for an existing Keychain certificate reference. */
3.10 - (id) initWithCertificateRef: (SecCertificateRef)certificateRef;
3.11
3.12 /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
3.13 - (id) initWithCertificateData: (NSData*)data;
3.14
3.15 +/** Checks whether two MYCertificate objects have bit-for-bit identical certificate data. */
3.16 +- (BOOL)isEqualToCertificate:(MYCertificate*)cert;
3.17 +
3.18 /** The Keychain object reference for this certificate. */
3.19 @property (readonly) SecCertificateRef certificateRef;
3.20
3.21 @@ -63,4 +69,25 @@
3.22 #endif
3.23 //@}
3.24
3.25 +
3.26 +/** @name Expert
3.27 + */
3.28 +//@{
3.29 +#if !TARGET_OS_IPHONE
3.30 +
3.31 ++ (SecPolicyRef) X509Policy;
3.32 ++ (SecPolicyRef) SSLPolicy;
3.33 ++ (SecPolicyRef) SMIMEPolicy;
3.34 +- (CSSM_CERT_TYPE) certificateType;
3.35 +- (NSArray*) trustSettings;
3.36 +- (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting;
3.37 +
3.38 +#endif
3.39 +//@}
3.40 +
3.41 @end
3.42 +
3.43 +
3.44 +NSString* MYPolicyGetName( SecPolicyRef policy );
3.45 +NSString* MYTrustDescribe( SecTrustRef trust );
3.46 +NSString* MYTrustResultDescribe( SecTrustResultType result );
4.1 --- a/MYCertificate.m Thu Apr 09 22:47:11 2009 -0700
4.2 +++ b/MYCertificate.m Sun Apr 12 22:02:20 2009 -0700
4.3 @@ -8,6 +8,8 @@
4.4
4.5 #import "MYCertificate.h"
4.6 #import "MYCrypto_Private.h"
4.7 +#import "MYDigest.h"
4.8 +#import "MYErrorUtils.h"
4.9
4.10 #if !MYCRYPTO_USE_IPHONE_API
4.11
4.12 @@ -24,6 +26,10 @@
4.13 return self;
4.14 }
4.15
4.16 ++ (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
4.17 + return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
4.18 +}
4.19 +
4.20 /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
4.21 - (id) initWithCertificateData: (NSData*)data
4.22 type: (CSSM_CERT_TYPE) type
4.23 @@ -48,6 +54,21 @@
4.24 encoding: CSSM_CERT_ENCODING_BER];
4.25 }
4.26
4.27 +
4.28 +- (NSString*) description {
4.29 + return $sprintf(@"%@[%@ %@/%p]",
4.30 + [self class],
4.31 + self.commonName,
4.32 + self.certificateData.my_SHA1Digest.abbreviatedHexString,
4.33 + _certificateRef);
4.34 +}
4.35 +
4.36 +
4.37 +- (BOOL)isEqualToCertificate:(MYCertificate*)cert {
4.38 + return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
4.39 +}
4.40 +
4.41 +
4.42 + (MYCertificate*) preferredCertificateForName: (NSString*)name {
4.43 SecCertificateRef certRef = NULL;
4.44 if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef),
4.45 @@ -61,6 +82,7 @@
4.46 @"SecCertificateSetPreference");
4.47 }
4.48
4.49 +
4.50 @synthesize certificateRef=_certificateRef;
4.51
4.52 - (NSData*) certificateData {
4.53 @@ -98,7 +120,133 @@
4.54 }
4.55
4.56
4.57 +#pragma mark -
4.58 +#pragma mark TRUST/POLICY STUFF:
4.59 +
4.60 +
4.61 ++ (SecPolicyRef) policyForOID: (CSSM_OID) policyOID {
4.62 + SecPolicySearchRef search;
4.63 + if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search),
4.64 + @"SecPolicySearchCreate"))
4.65 + return nil;
4.66 + SecPolicyRef policy = NULL;
4.67 + if (!check(SecPolicySearchCopyNext(search, &policy), @"SecPolicySearchCopyNext"))
4.68 + policy = NULL;
4.69 + CFRelease(search);
4.70 + return policy;
4.71 +}
4.72 +
4.73 ++ (SecPolicyRef) X509Policy {
4.74 + static SecPolicyRef sX509Policy = NULL;
4.75 + if (!sX509Policy)
4.76 + sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC];
4.77 + return sX509Policy;
4.78 +}
4.79 +
4.80 ++ (SecPolicyRef) SSLPolicy {
4.81 + static SecPolicyRef sSSLPolicy = NULL;
4.82 + if (!sSSLPolicy)
4.83 + sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL];
4.84 + return sSSLPolicy;
4.85 +}
4.86 +
4.87 ++ (SecPolicyRef) SMIMEPolicy {
4.88 + static SecPolicyRef sSMIMEPolicy = NULL;
4.89 + if (!sSMIMEPolicy)
4.90 + sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME];
4.91 + return sSMIMEPolicy;
4.92 +}
4.93 +
4.94 +
4.95 +- (CSSM_CERT_TYPE) certificateType {
4.96 + CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN;
4.97 + if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType"))
4.98 + type = CSSM_CERT_UNKNOWN;
4.99 + return type;
4.100 +}
4.101 +
4.102 +- (NSArray*) trustSettings {
4.103 + CFArrayRef settings = NULL;
4.104 + OSStatus err = SecTrustSettingsCopyTrustSettings(_certificateRef, kSecTrustSettingsDomainUser,
4.105 + &settings);
4.106 + if (err == errSecItemNotFound || !check(err,@"SecTrustSettingsCopyTrustSettings") || !settings)
4.107 + return nil;
4.108 + return [(id)CFMakeCollectable(settings) autorelease];
4.109 +}
4.110 +
4.111 +
4.112 +- (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting
4.113 +{
4.114 + if (trustSetting == kSecTrustResultProceed) {
4.115 + return check(SecTrustSettingsSetTrustSettings(_certificateRef,
4.116 + kSecTrustSettingsDomainUser, nil),
4.117 + @"SecTrustSettingsSetTrustSettings");
4.118 + } else if (trustSetting == kSecTrustResultDeny) {
4.119 + OSStatus err = SecTrustSettingsRemoveTrustSettings(_certificateRef,
4.120 + kSecTrustSettingsDomainUser);
4.121 + return err == errSecItemNotFound || check(err, @"SecTrustSettingsRemoveTrustSettings");
4.122 + } else
4.123 + return paramErr;
4.124 +}
4.125 +
4.126 +
4.127 @end
4.128
4.129
4.130 +NSString* MYPolicyGetName( SecPolicyRef policy ) {
4.131 + if (!policy)
4.132 + return @"(null)";
4.133 + CSSM_OID oid = {};
4.134 + SecPolicyGetOID(policy, &oid);
4.135 + return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
4.136 +}
4.137 +
4.138 +NSString* MYTrustResultDescribe( SecTrustResultType result ) {
4.139 + static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = {
4.140 + @"Invalid",
4.141 + @"Proceed",
4.142 + @"Confirm",
4.143 + @"Deny",
4.144 + @"Unspecified",
4.145 + @"RecoverableTrustFailure",
4.146 + @"FatalTrustFailure",
4.147 + @"OtherError"
4.148 + };
4.149 + if (result>=0 && result <=kSecTrustResultOtherError)
4.150 + return kTrustResultNames[result];
4.151 + else
4.152 + return $sprintf(@"(Unknown trust result %i)", result);
4.153 +}
4.154 +
4.155 +
4.156 +NSString* MYTrustDescribe( SecTrustRef trust ) {
4.157 + SecTrustResultType result;
4.158 + CFArrayRef certChain=NULL;
4.159 + CSSM_TP_APPLE_EVIDENCE_INFO* statusChain;
4.160 + OSStatus err = SecTrustGetResult(trust, &result, &certChain, &statusChain);
4.161 + NSString *desc;
4.162 + if (err)
4.163 + desc = $sprintf(@"SecTrust[%p, err=%@]", trust, MYErrorName(NSOSStatusErrorDomain, err));
4.164 + else
4.165 + desc = $sprintf(@"SecTrust[%@, %u in chain]",
4.166 + MYTrustResultDescribe(result),
4.167 + CFArrayGetCount(certChain));
4.168 + if (certChain) CFRelease(certChain);
4.169 + return desc;
4.170 +}
4.171 +
4.172 +
4.173 +
4.174 +TestCase(Trust) {
4.175 + Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
4.176 + Log(@" SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
4.177 + Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy]));
4.178 + for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) {
4.179 + NSArray *settings = cert.trustSettings;
4.180 + if (settings)
4.181 + Log(@"---- %@ = %@", cert, settings);
4.182 + }
4.183 +}
4.184 +
4.185 +
4.186 #endif !MYCRYPTO_USE_IPHONE_API
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/MYCrypto+Cocoa.h Sun Apr 12 22:02:20 2009 -0700
5.3 @@ -0,0 +1,27 @@
5.4 +//
5.5 +// MYCrypto+Cocoa.h
5.6 +// MYCrypto
5.7 +//
5.8 +// Created by Jens Alfke on 4/10/09.
5.9 +// Copyright 2009 Jens Alfke. All rights reserved.
5.10 +//
5.11 +
5.12 +#import <SecurityInterface/SFChooseIdentityPanel.h>
5.13 +@class MYIdentity;
5.14 +
5.15 +
5.16 +@interface SFChooseIdentityPanel (MYCrypto)
5.17 +
5.18 +- (NSInteger)my_runModalForIdentities:(NSArray *)myIdentitObjects
5.19 + message:(NSString *)message;
5.20 +
5.21 +- (void)my_beginSheetForWindow:(NSWindow *)docWindow
5.22 + modalDelegate:(id)delegate
5.23 + didEndSelector:(SEL)didEndSelector
5.24 + contextInfo:(void *)contextInfo
5.25 + identities:(NSArray *)myIdentitObjects
5.26 + message:(NSString *)message;
5.27 +
5.28 +- (MYIdentity*) my_identity;
5.29 +
5.30 +@end
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/MYCrypto+Cocoa.m Sun Apr 12 22:02:20 2009 -0700
6.3 @@ -0,0 +1,48 @@
6.4 +//
6.5 +// MYCrypto+Cocoa.m
6.6 +// MYCrypto
6.7 +//
6.8 +// Created by Jens Alfke on 4/10/09.
6.9 +// Copyright 2009 Jens Alfke. All rights reserved.
6.10 +//
6.11 +
6.12 +#import "MYCrypto+Cocoa.h"
6.13 +#import "MYCrypto_Private.h"
6.14 +#import "MYIdentity.h"
6.15 +
6.16 +
6.17 +@implementation SFChooseIdentityPanel (MYCrypto)
6.18 +
6.19 +
6.20 +- (NSInteger)my_runModalForIdentities:(NSArray *)identities
6.21 + message:(NSString *)message
6.22 +{
6.23 + NSMutableArray *identityRefs = $marray();
6.24 + for (MYIdentity *ident in identities)
6.25 + [identityRefs addObject: (id)ident.identityRef];
6.26 + return [self runModalForIdentities: identityRefs message: message];
6.27 +}
6.28 +
6.29 +- (void)my_beginSheetForWindow:(NSWindow *)docWindow
6.30 + modalDelegate:(id)delegate
6.31 + didEndSelector:(SEL)didEndSelector
6.32 + contextInfo:(void *)contextInfo
6.33 + identities:(NSArray *)identities
6.34 + message:(NSString *)message
6.35 +{
6.36 + NSMutableArray *identityRefs = $marray();
6.37 + for (MYIdentity *ident in identities)
6.38 + [identityRefs addObject: (id)ident.identityRef];
6.39 + [self beginSheetForWindow:docWindow
6.40 + modalDelegate:delegate
6.41 + didEndSelector:didEndSelector
6.42 + contextInfo:contextInfo
6.43 + identities:identityRefs
6.44 + message:message];
6.45 +}
6.46 +
6.47 +- (MYIdentity*) my_identity {
6.48 + return [MYIdentity identityWithIdentityRef: [self identity]];
6.49 +}
6.50 +
6.51 +@end
7.1 --- a/MYCrypto-iPhone.xcodeproj/project.pbxproj Thu Apr 09 22:47:11 2009 -0700
7.2 +++ b/MYCrypto-iPhone.xcodeproj/project.pbxproj Sun Apr 12 22:02:20 2009 -0700
7.3 @@ -22,7 +22,7 @@
7.4 276FB34B0F856CA400CB326E /* MYCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB34A0F856CA400CB326E /* MYCertificate.m */; };
7.5 27A430120F87C6C10063D362 /* MYKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E823110F81D56E0019BE60 /* MYKey.m */; };
7.6 27A430140F87C6D50063D362 /* MYSymmetricKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A430130F87C6D50063D362 /* MYSymmetricKey.m */; };
7.7 - 27E823210F81D56E0019BE60 /* MYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E8230F0F81D56E0019BE60 /* MYCryptor.m */; };
7.8 + 27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */; };
7.9 27E823240F81D56E0019BE60 /* MYKeychainItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E823150F81D56E0019BE60 /* MYKeychainItem.m */; };
7.10 27E823270F81D56E0019BE60 /* MYDigest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E8231C0F81D56E0019BE60 /* MYDigest.m */; };
7.11 27E823290F81D56E0019BE60 /* MYCrypto.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 27E8231E0F81D56E0019BE60 /* MYCrypto.xcconfig */; };
7.12 @@ -58,9 +58,9 @@
7.13 27A430130F87C6D50063D362 /* MYSymmetricKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYSymmetricKey.m; sourceTree = "<group>"; };
7.14 27A430150F87C6DB0063D362 /* MYSymmetricKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYSymmetricKey.h; sourceTree = "<group>"; };
7.15 27AAD9710F8927DB0064DD7C /* MYCryptoConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptoConfig.h; sourceTree = "<group>"; };
7.16 + 27E3A6A60F91B8B30079D4D9 /* MYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptor.h; sourceTree = "<group>"; };
7.17 + 27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptor.m; sourceTree = "<group>"; };
7.18 27E8230C0F81D56E0019BE60 /* MYCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCertificate.h; sourceTree = "<group>"; };
7.19 - 27E8230E0F81D56E0019BE60 /* MYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptor.h; sourceTree = "<group>"; };
7.20 - 27E8230F0F81D56E0019BE60 /* MYCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptor.m; sourceTree = "<group>"; };
7.21 27E823100F81D56E0019BE60 /* MYKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYKey.h; sourceTree = "<group>"; };
7.22 27E823110F81D56E0019BE60 /* MYKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYKey.m; sourceTree = "<group>"; };
7.23 27E823120F81D56E0019BE60 /* MYKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYKeychain.h; sourceTree = "<group>"; };
7.24 @@ -125,6 +125,15 @@
7.25 name = Products;
7.26 sourceTree = "<group>";
7.27 };
7.28 + 27E3A6A10F91B8B30079D4D9 /* Encryption */ = {
7.29 + isa = PBXGroup;
7.30 + children = (
7.31 + 27E3A6A60F91B8B30079D4D9 /* MYCryptor.h */,
7.32 + 27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */,
7.33 + );
7.34 + name = Encryption;
7.35 + sourceTree = "<group>";
7.36 + };
7.37 27E8230B0F81D56E0019BE60 /* Source */ = {
7.38 isa = PBXGroup;
7.39 children = (
7.40 @@ -132,8 +141,6 @@
7.41 27E8230C0F81D56E0019BE60 /* MYCertificate.h */,
7.42 276FB34A0F856CA400CB326E /* MYCertificate.m */,
7.43 273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */,
7.44 - 27E8230E0F81D56E0019BE60 /* MYCryptor.h */,
7.45 - 27E8230F0F81D56E0019BE60 /* MYCryptor.m */,
7.46 27059CF10F8F0F8E00A8422F /* MYIdentity.h */,
7.47 27059CF20F8F0F9200A8422F /* MYIdentity.m */,
7.48 27E823100F81D56E0019BE60 /* MYKey.h */,
7.49 @@ -182,6 +189,7 @@
7.50 isa = PBXGroup;
7.51 children = (
7.52 27E8230B0F81D56E0019BE60 /* Source */,
7.53 + 27E3A6A10F91B8B30079D4D9 /* Encryption */,
7.54 27E8232B0F81D5760019BE60 /* MYUtilities */,
7.55 080E96DDFE201D6D7F000001 /* iPhone */,
7.56 29B97323FDCFA39411CA2CEA /* Frameworks */,
7.57 @@ -259,7 +267,6 @@
7.58 files = (
7.59 1D60589B0D05DD56006BFB54 /* MYCrypto_iPhone_main.m in Sources */,
7.60 1D3623260D0F684500981E51 /* MYCrypto_iPhoneAppDelegate.m in Sources */,
7.61 - 27E823210F81D56E0019BE60 /* MYCryptor.m in Sources */,
7.62 27E823240F81D56E0019BE60 /* MYKeychainItem.m in Sources */,
7.63 27E823270F81D56E0019BE60 /* MYDigest.m in Sources */,
7.64 27E823370F81D5760019BE60 /* CollectionUtils.m in Sources */,
7.65 @@ -278,6 +285,7 @@
7.66 27FE453F0F87CC8500A86D63 /* MYKey-iPhone.m in Sources */,
7.67 2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */,
7.68 27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */,
7.69 + 27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */,
7.70 );
7.71 runOnlyForDeploymentPostprocessing = 0;
7.72 };
8.1 --- a/MYCrypto.xcodeproj/project.pbxproj Thu Apr 09 22:47:11 2009 -0700
8.2 +++ b/MYCrypto.xcodeproj/project.pbxproj Sun Apr 12 22:02:20 2009 -0700
8.3 @@ -7,6 +7,10 @@
8.4 objects = {
8.5
8.6 /* Begin PBXBuildFile section */
8.7 + 27059D530F8F9BB500A8422F /* MYEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D500F8F9BB500A8422F /* MYEncoder.m */; };
8.8 + 27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */; };
8.9 + 27059D840F8FA82500A8422F /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27059D830F8FA82500A8422F /* SecurityInterface.framework */; };
8.10 + 27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D520F8F9BB500A8422F /* MYDecoder.m */; };
8.11 270B879F0F8C565000C56781 /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 270B879E0F8C565000C56781 /* MYPrivateKey.m */; };
8.12 274861D50F8E4B5200FE617B /* MYCertGen.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42ECD0F8689D30063D362 /* MYCertGen.m */; };
8.13 274863A20F8EF39400FE617B /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 274863A10F8EF39400FE617B /* MYIdentity.m */; };
8.14 @@ -44,6 +48,13 @@
8.15
8.16 /* Begin PBXFileReference section */
8.17 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
8.18 + 27059D4F0F8F9BB500A8422F /* MYEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYEncoder.h; sourceTree = "<group>"; };
8.19 + 27059D500F8F9BB500A8422F /* MYEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYEncoder.m; sourceTree = "<group>"; };
8.20 + 27059D510F8F9BB500A8422F /* MYDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYDecoder.h; sourceTree = "<group>"; };
8.21 + 27059D520F8F9BB500A8422F /* MYDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYDecoder.m; sourceTree = "<group>"; };
8.22 + 27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MYCrypto+Cocoa.h"; sourceTree = "<group>"; };
8.23 + 27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYCrypto+Cocoa.m"; sourceTree = "<group>"; };
8.24 + 27059D830F8FA82500A8422F /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = /System/Library/Frameworks/SecurityInterface.framework; sourceTree = "<absolute>"; };
8.25 270B879D0F8C565000C56781 /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = "<group>"; };
8.26 270B879E0F8C565000C56781 /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = "<group>"; };
8.27 2748604D0F8D5C4C00FE617B /* MYCrypto_Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYCrypto_Release.xcconfig; sourceTree = "<group>"; };
8.28 @@ -98,6 +109,7 @@
8.29 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
8.30 27CFF51F0F7E94AE000B418E /* Security.framework in Frameworks */,
8.31 27E820720F7EA6260019BE60 /* CoreServices.framework in Frameworks */,
8.32 + 27059D840F8FA82500A8422F /* SecurityInterface.framework in Frameworks */,
8.33 );
8.34 runOnlyForDeploymentPostprocessing = 0;
8.35 };
8.36 @@ -108,6 +120,7 @@
8.37 isa = PBXGroup;
8.38 children = (
8.39 08FB7795FE84155DC02AAC07 /* Source */,
8.40 + 27059D4E0F8F9B9E00A8422F /* Encryption */,
8.41 274861440F8D757600FE617B /* Certificate Generation */,
8.42 270B881C0F8D055A00C56781 /* Internal */,
8.43 27CFF4CC0F7E86E8000B418E /* MYUtilities */,
8.44 @@ -116,6 +129,7 @@
8.45 1AB674ADFE9D54B511CA2CBB /* Products */,
8.46 27CFF51E0F7E94AE000B418E /* Security.framework */,
8.47 27E820710F7EA6260019BE60 /* CoreServices.framework */,
8.48 + 27059D830F8FA82500A8422F /* SecurityInterface.framework */,
8.49 );
8.50 name = MYCrypto;
8.51 sourceTree = "<group>";
8.52 @@ -125,8 +139,6 @@
8.53 children = (
8.54 27CFF4B10F7E8535000B418E /* MYCertificate.h */,
8.55 27CFF4B20F7E8535000B418E /* MYCertificate.m */,
8.56 - 27CFF4B30F7E8535000B418E /* MYCryptor.h */,
8.57 - 27CFF4B40F7E8535000B418E /* MYCryptor.m */,
8.58 27CFF4BF0F7E8535000B418E /* MYDigest.h */,
8.59 27CFF4C00F7E8535000B418E /* MYDigest.m */,
8.60 274863A00F8EF39400FE617B /* MYIdentity.h */,
8.61 @@ -145,6 +157,8 @@
8.62 27A42D410F858ED80063D362 /* MYSymmetricKey.m */,
8.63 27AAD97B0F892A0D0064DD7C /* MYCryptoConfig.h */,
8.64 27EAF0390F8B2D700091AF95 /* README.textile */,
8.65 + 27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */,
8.66 + 27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */,
8.67 );
8.68 name = Source;
8.69 sourceTree = "<group>";
8.70 @@ -165,6 +179,19 @@
8.71 name = Products;
8.72 sourceTree = "<group>";
8.73 };
8.74 + 27059D4E0F8F9B9E00A8422F /* Encryption */ = {
8.75 + isa = PBXGroup;
8.76 + children = (
8.77 + 27059D4F0F8F9BB500A8422F /* MYEncoder.h */,
8.78 + 27059D500F8F9BB500A8422F /* MYEncoder.m */,
8.79 + 27059D510F8F9BB500A8422F /* MYDecoder.h */,
8.80 + 27059D520F8F9BB500A8422F /* MYDecoder.m */,
8.81 + 27CFF4B30F7E8535000B418E /* MYCryptor.h */,
8.82 + 27CFF4B40F7E8535000B418E /* MYCryptor.m */,
8.83 + );
8.84 + name = Encryption;
8.85 + sourceTree = "<group>";
8.86 + };
8.87 270B881C0F8D055A00C56781 /* Internal */ = {
8.88 isa = PBXGroup;
8.89 children = (
8.90 @@ -263,7 +290,7 @@
8.91 );
8.92 runOnlyForDeploymentPostprocessing = 0;
8.93 shellPath = /bin/csh;
8.94 - shellScript = "doxygen\n";
8.95 + shellScript = "doxygen |& sed s/Warning/warning/\n";
8.96 showEnvVarsInLog = 0;
8.97 };
8.98 /* End PBXShellScriptBuildPhase section */
8.99 @@ -291,6 +318,9 @@
8.100 270B879F0F8C565000C56781 /* MYPrivateKey.m in Sources */,
8.101 274861D50F8E4B5200FE617B /* MYCertGen.m in Sources */,
8.102 274863A20F8EF39400FE617B /* MYIdentity.m in Sources */,
8.103 + 27059D530F8F9BB500A8422F /* MYEncoder.m in Sources */,
8.104 + 27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */,
8.105 + 27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */,
8.106 );
8.107 runOnlyForDeploymentPostprocessing = 0;
8.108 };
9.1 --- a/MYCryptoTest.m Thu Apr 09 22:47:11 2009 -0700
9.2 +++ b/MYCryptoTest.m Sun Apr 12 22:02:20 2009 -0700
9.3 @@ -11,6 +11,7 @@
9.4 #import "MYKeychain.h"
9.5 #import "MYDigest.h"
9.6 #import "MYIdentity.h"
9.7 +#import "MYCrypto+Cocoa.h"
9.8 #import "MYCrypto_Private.h"
9.9
9.10
9.11 @@ -78,7 +79,7 @@
9.12 Log(@"Enumerator = %@", e);
9.13 CAssert(e);
9.14 for (MYIdentity *ident in e) {
9.15 - Log(@"Found %@ -- name=%@, emails=(%@), key=%@",
9.16 + Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@",
9.17 ident, ident.commonName,
9.18 #if TARGET_OS_IPHONE
9.19 nil,
9.20 @@ -172,33 +173,63 @@
9.21 #pragma mark KEY-PAIRS:
9.22
9.23
9.24 -TestCase(MYPrivateKey) {
9.25 - RequireTestCase(MYKeychain);
9.26 -
9.27 - Log(@"Generating key pair...");
9.28 - MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
9.29 - Log(@"...created { %@ , %@ }.", pair, pair.publicKey);
9.30 +static void TestUseKeyPair(MYPrivateKey *pair) {
9.31 + Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey);
9.32 CAssert(pair);
9.33 CAssert(pair.keyRef);
9.34 MYPublicKey *publicKey = pair.publicKey;
9.35 CAssert(publicKey.keyRef);
9.36
9.37 + NSData *pubKeyData = publicKey.keyData;
9.38 + Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
9.39 + CAssert(pubKeyData);
9.40 +
9.41 + MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
9.42 + Log(@"Public key digest = %@",pubKeyDigest);
9.43 + CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
9.44 +
9.45 + Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
9.46 +
9.47 + // Let's sign data:
9.48 + NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding];
9.49 + NSData *sig = [pair signData: data];
9.50 + Log(@"Signature = %@ (%u bytes)",sig,sig.length);
9.51 + CAssert(sig);
9.52 + CAssert( [publicKey verifySignature: sig ofData: data] );
9.53 +
9.54 + // Now let's encrypt...
9.55 + NSData *crypted = [publicKey encryptData: data];
9.56 + Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
9.57 + CAssert(crypted);
9.58 + CAssertEqual([pair decryptData: crypted], data);
9.59 + Log(@"Verified decryption.");
9.60 +
9.61 + // Test creating a standalone public key:
9.62 + MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef];
9.63 + CAssert( [pub verifySignature: sig ofData: data] );
9.64 + Log(@"Verified signature.");
9.65 +
9.66 + // Test creating a public key from data:
9.67 + Log(@"Reconstituting public key from data...");
9.68 + pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
9.69 + CAssert(pub);
9.70 + CAssertEqual(pub.keyData, pubKeyData);
9.71 + CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
9.72 + CAssert( [pub verifySignature: sig ofData: data] );
9.73 + Log(@"Verified signature from reconstituted key.");
9.74 +}
9.75 +
9.76 +
9.77 +TestCase(MYGenerateKeyPair) {
9.78 + RequireTestCase(MYKeychain);
9.79 +
9.80 + Log(@"Generating key pair...");
9.81 + MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
9.82 + MYPublicKey *publicKey = pair.publicKey;
9.83 + Log(@"...created { %@ , %@ }.", pair, publicKey);
9.84 +
9.85 @try{
9.86 - NSData *pubKeyData = publicKey.keyData;
9.87 - Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
9.88 - CAssert(pubKeyData);
9.89 -
9.90 - MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
9.91 - Log(@"Public key digest = %@",pubKeyDigest);
9.92 - CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
9.93 -
9.94 - Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
9.95 -
9.96 - NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding];
9.97 - NSData *sig = [pair signData: data];
9.98 - Log(@"Signature = %@ (%u bytes)",sig,sig.length);
9.99 - CAssert(sig);
9.100 - CAssert( [publicKey verifySignature: sig ofData: data] );
9.101 + TestUseKeyPair(pair);
9.102
9.103 [pair setName: @"Test KeyPair Label"];
9.104 CAssertEqual(pair.name, @"Test KeyPair Label");
9.105 @@ -212,28 +243,6 @@
9.106 CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
9.107 CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com");
9.108
9.109 - // Test creating a standalone public key:
9.110 - MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef];
9.111 - CAssert( [pub verifySignature: sig ofData: data] );
9.112 - Log(@"Verified signature.");
9.113 -
9.114 - // Test creating a public key from data:
9.115 - Log(@"Reconstituting public key from data...");
9.116 - pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
9.117 - CAssert(pub);
9.118 - CAssertEqual(pub.keyData, pubKeyData);
9.119 - CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
9.120 - CAssert( [pub verifySignature: sig ofData: data] );
9.121 - Log(@"Verified signature from reconstituted key.");
9.122 -
9.123 - // Now let's encrypt...
9.124 - NSData *crypted = [pub encryptData: data];
9.125 - Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
9.126 - CAssert(crypted);
9.127 -
9.128 - CAssertEqual([pair decryptData: crypted], data);
9.129 - Log(@"Verified decryption.");
9.130 -
9.131 CAssert([pair removeFromKeychain]);
9.132 Log(@"Removed key-pair.");
9.133 pair = nil;
9.134 @@ -249,6 +258,24 @@
9.135 }
9.136
9.137
9.138 +TestCase(MYUseIdentity) {
9.139 + MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
9.140 + if (!me) {
9.141 + NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
9.142 + SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
9.143 + [panel setAlternateButtonTitle: @"Cancel"];
9.144 + if ([panel my_runModalForIdentities: idents
9.145 + message: @"Choose an identity for the MYEncoder test case:"]
9.146 + != NSOKButton) {
9.147 + [NSException raise: NSGenericException format: @"User canceled"];
9.148 + }
9.149 + me = [panel my_identity];
9.150 + [me makePreferredIdentityForName: @"MYCryptoTest"];
9.151 + }
9.152 + CAssert(me,@"No default identity has been set up in the Keychain");
9.153 + TestUseKeyPair(me.privateKey);
9.154 +}
9.155 +
9.156
9.157 #pragma mark -
9.158 #pragma mark KEYPAIR EXPORT:
10.1 --- a/MYCrypto_Private.h Thu Apr 09 22:47:11 2009 -0700
10.2 +++ b/MYCrypto_Private.h Sun Apr 12 22:02:20 2009 -0700
10.3 @@ -114,6 +114,8 @@
10.4 #undef check
10.5 BOOL check(OSStatus err, NSString *what);
10.6
10.7 +#define checksave(CALL) ({OSStatus err=(CALL); check(err,@""#CALL) || (_error=err, NO);})
10.8 +
10.9 #if !MYCRYPTO_USE_IPHONE_API
10.10 BOOL checkcssm(CSSM_RETURN err, NSString *what);
10.11
10.12 @@ -121,4 +123,7 @@
10.13 SecExternalItemType type,
10.14 SecKeychainRef keychain,
10.15 SecKeyImportExportParameters *params /*non-null*/);
10.16 +
10.17 +NSString* OIDAsString(CSSM_OID OID);
10.18 +
10.19 #endif
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/MYDecoder.h Sun Apr 12 22:02:20 2009 -0700
11.3 @@ -0,0 +1,170 @@
11.4 +//
11.5 +// CryptoDecoder.h
11.6 +// Cloudy
11.7 +//
11.8 +// Created by Jens Alfke on 1/16/08.
11.9 +// Copyright 2008 Jens Alfke. All rights reserved.
11.10 +//
11.11 +
11.12 +#import <Cocoa/Cocoa.h>
11.13 +#import <Security/CMSDecoder.h>
11.14 +
11.15 +@class MYKeychain, MYCertificate;
11.16 +
11.17 +
11.18 +/** Decodes a CMS-formatted message into the original data, and identifies & verifies signatures. */
11.19 +@interface MYDecoder : NSObject
11.20 +{
11.21 + @private
11.22 + CMSDecoderRef _decoder;
11.23 + OSStatus _error;
11.24 + SecPolicyRef _policy;
11.25 +}
11.26 +
11.27 +/** Initializes a new decoder. */
11.28 +- (id) init;
11.29 +
11.30 +/** Initializes a new decoder and reads the entire message data. */
11.31 +- (id) initWithData: (NSData*)data error: (NSError**)outError;
11.32 +
11.33 +/** Specifies a keychain to use to look up certificates and keys, instead of the default
11.34 + keychain search path. */
11.35 +- (BOOL) useKeychain: (MYKeychain*)keychain;
11.36 +
11.37 +/** Adds data to the decoder. You can add the entire data at once, or in bits and pieces
11.38 + (if you're reading it from a stream). */
11.39 +- (BOOL) addData: (NSData*)data;
11.40 +
11.41 +/** The error, if any, that occurred while decoding the content.
11.42 + If -addData: returns NO, read this property to find out what went wrong.
11.43 + The most likely error is (NSOSStatusErrorDomain, errSecUnknownFormat). */
11.44 +@property (readonly) NSError *error;
11.45 +
11.46 +/** If the message content is detached (stored separately from the encoded message),
11.47 + you must copy it to this property before calling -finish, so that the decoder can use it
11.48 + to verify signatures. */
11.49 +@property (retain) NSData *detachedContent;
11.50 +
11.51 +/** Tells the decoder that all of the data has been read, after the last call to -addData:.
11.52 + You must call this before accessing the message content or metadata. */
11.53 +- (BOOL) finish;
11.54 +
11.55 +/** The decoded message content. */
11.56 +@property (readonly) NSData* content;
11.57 +
11.58 +/** YES if the message was signed. (Use the signers property to see who signed it.) */
11.59 +@property (readonly) BOOL isSigned;
11.60 +
11.61 +/** YES if the message was encrypted. */
11.62 +@property (readonly) BOOL isEncrypted;
11.63 +
11.64 +/** An array of MYSigner objects representing the identities who signed the message.
11.65 + Nil if the message is unsigned. */
11.66 +@property (readonly) NSArray* signers;
11.67 +
11.68 +/** All of the certificates (as MYCertificate objects) that were attached to the message. */
11.69 +@property (readonly) NSArray* certificates;
11.70 +
11.71 +
11.72 +/** @name Expert
11.73 + * Advanced methods.
11.74 + */
11.75 +//@{
11.76 +
11.77 +/** The X.509 content-type of the message contents.
11.78 + The Data field points to autoreleased memory: do not free it yourself, and do not
11.79 + expect it to remain valid after the calling method returns. */
11.80 +@property (readonly) CSSM_OID contentType;
11.81 +
11.82 +/** The Policy that will be used to evaluate trust when calling MYSigner.copyTrust.
11.83 + NULL by default. */
11.84 +@property (assign) SecPolicyRef policy;
11.85 +
11.86 +/** Returns a string with detailed information about the message metadata.
11.87 + Not user-presentable; used for debugging. */
11.88 +- (NSString*) dump;
11.89 +
11.90 +//@}
11.91 +
11.92 +@end
11.93 +
11.94 +
11.95 +/** Represents a signer of a CMS message, as returned by the MYDecoder.signers property. */
11.96 +@interface MYSigner : NSObject
11.97 +{
11.98 + @private
11.99 + CMSDecoderRef _decoder;
11.100 + size_t _index;
11.101 + CFTypeRef _policy;
11.102 + CMSSignerStatus _status;
11.103 + OSStatus _verifyResult;
11.104 + SecTrustRef _trust;
11.105 +}
11.106 +
11.107 +/** The status of the signature, i.e. whether it's valid or not.
11.108 + * Values include:
11.109 + * kCMSSignerValid :both signature and signer certificate verified OK.
11.110 + * kCMSSignerNeedsDetachedContent:the MYDecoder's detachedContent property must be set,
11.111 + * to ascertain the signature status.
11.112 + * kCMSSignerInvalidSignature :bad signature -- either the content or the signature
11.113 + * data were tampered with after the message was encoded.
11.114 + * kCMSSignerInvalidCert :an error occurred verifying the signer's certificate.
11.115 + * Further information available via the verifyResult
11.116 + * and copyTrust methods.
11.117 + */
11.118 +@property (readonly) CMSSignerStatus status;
11.119 +
11.120 +/** The signer's certificate.
11.121 + You should check the status property first, to see whether the signature and certificate
11.122 + are valid.
11.123 + For safety purposes, if you haven't checked status yet, this method will return nil
11.124 + if the signer status is not kCMSSignerValid. */
11.125 +@property (readonly) MYCertificate *certificate;
11.126 +
11.127 +/** The signer's email address (if any), as stored in the certificate. */
11.128 +@property (readonly) NSString* emailAddress;
11.129 +
11.130 +/** @name Expert
11.131 + * Advanced methods.
11.132 + */
11.133 +//@{
11.134 +
11.135 +/** Returns the SecTrustRef that was used to verify the certificate.
11.136 + You can use this object to get more detailed information about how the verification was done.
11.137 + If you set the parent decoder's policy property, then that SecPolicy will be used to evaluate
11.138 + trust; otherwise you'll need to do it yourself using the SecTrust object.
11.139 + You must CFRelease the result when you're finished with it. */
11.140 +- (SecTrustRef) trust;
11.141 +
11.142 +/** The result of certificate verification, as a CSSM_RESULT code;
11.143 + * a nonzero value indicates an error.
11.144 + *
11.145 + * Some of the most common and interesting errors are:
11.146 + *
11.147 + * CSSMERR_TP_INVALID_ANCHOR_CERT : The cert was verified back to a
11.148 + * self-signed (root) cert which was present in the message, but
11.149 + * that root cert is not a known, trusted root cert.
11.150 + * CSSMERR_TP_NOT_TRUSTED: The cert could not be verified back to
11.151 + * a root cert.
11.152 + * CSSMERR_TP_VERIFICATION_FAILURE: A root cert was found which does
11.153 + * not self-verify.
11.154 + * CSSMERR_TP_VERIFY_ACTION_FAILED: Indicates a failure of the requested
11.155 + * policy action.
11.156 + * CSSMERR_TP_INVALID_CERTIFICATE: Indicates a bad leaf cert.
11.157 + * CSSMERR_TP_CERT_EXPIRED: A cert in the chain was expired at the time of
11.158 + * verification.
11.159 + * CSSMERR_TP_CERT_NOT_VALID_YET: A cert in the chain was not yet valie at
11.160 + * the time of verification.
11.161 + */
11.162 +@property (readonly) OSStatus verifyResult;
11.163 +
11.164 +//@}
11.165 +
11.166 +@end
11.167 +
11.168 +
11.169 +enum {
11.170 + /** Returned from MYSigner.status to indicate a failure (non-noErr return value)
11.171 + of the underlying CMSDecoderCopySignerStatus call. Should never occur. */
11.172 + kMYSignerStatusCheckFailed = 666
11.173 +};
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/MYDecoder.m Sun Apr 12 22:02:20 2009 -0700
12.3 @@ -0,0 +1,389 @@
12.4 +//
12.5 +// MYDecoder.m
12.6 +// Cloudy
12.7 +//
12.8 +// Created by Jens Alfke on 1/16/08.
12.9 +// Copyright 2008 Jens Alfke. All rights reserved.
12.10 +//
12.11 +
12.12 +#import "MYDecoder.h"
12.13 +#import "MYCrypto_Private.h"
12.14 +#import "Test.h"
12.15 +#import "MYErrorUtils.h"
12.16 +
12.17 +
12.18 +@interface MYSigner ()
12.19 +- (id) initWithDecoder: (CMSDecoderRef)decoder index: (size_t)index policy: (CFTypeRef)policy;
12.20 +@end
12.21 +
12.22 +
12.23 +@implementation MYDecoder
12.24 +
12.25 +
12.26 +- (id) initWithData: (NSData*)data error: (NSError**)outError
12.27 +{
12.28 + self = [self init];
12.29 + if( self ) {
12.30 + [self addData: data];
12.31 + [self finish];
12.32 + *outError = self.error;
12.33 + if( *outError ) {
12.34 + [self release];
12.35 + return nil;
12.36 + }
12.37 + }
12.38 + return self;
12.39 +}
12.40 +
12.41 +- (id) init
12.42 +{
12.43 + self = [super init];
12.44 + if (self != nil) {
12.45 + OSStatus err = CMSDecoderCreate(&_decoder);
12.46 + if( err ) {
12.47 + [self release];
12.48 + self = nil;
12.49 + }
12.50 + }
12.51 + return self;
12.52 +}
12.53 +
12.54 +- (void) dealloc
12.55 +{
12.56 + if( _decoder ) CFRelease(_decoder);
12.57 + if (_policy) CFRelease(_policy);
12.58 + [super dealloc];
12.59 +}
12.60 +
12.61 +
12.62 +- (BOOL) addData: (NSData*)data
12.63 +{
12.64 + Assert(data);
12.65 + return !_error && checksave( CMSDecoderUpdateMessage(_decoder, data.bytes, data.length) );
12.66 +}
12.67 +
12.68 +
12.69 +- (BOOL) finish
12.70 +{
12.71 + return !_error && checksave( CMSDecoderFinalizeMessage(_decoder) );
12.72 +}
12.73 +
12.74 +- (NSError*) error
12.75 +{
12.76 + if( _error )
12.77 + return MYError(_error, NSOSStatusErrorDomain,
12.78 + @"%@", MYErrorName(NSOSStatusErrorDomain,_error));
12.79 + else
12.80 + return nil;
12.81 +}
12.82 +
12.83 +- (BOOL) useKeychain: (MYKeychain*)keychain
12.84 +{
12.85 + return !_error && checksave( CMSDecoderSetSearchKeychain(_decoder, keychain.keychainRef) );
12.86 +}
12.87 +
12.88 +
12.89 +@synthesize policy=_policy;
12.90 +
12.91 +
12.92 +- (NSData*) _dataFromFunction: (OSStatus (*)(CMSDecoderRef,CFDataRef*))function
12.93 +{
12.94 + CFDataRef data=NULL;
12.95 + if( checksave( (*function)(_decoder, &data) ) )
12.96 + return [(NSData*)CFMakeCollectable(data) autorelease];
12.97 + else
12.98 + return nil;
12.99 +}
12.100 +
12.101 +
12.102 +- (NSData*) detachedContent
12.103 +{
12.104 + return [self _dataFromFunction: &CMSDecoderCopyDetachedContent];
12.105 +}
12.106 +
12.107 +- (void) setDetachedContent: (NSData*)detachedContent
12.108 +{
12.109 + if( ! _error )
12.110 + checksave( CMSDecoderSetDetachedContent(_decoder, (CFDataRef)detachedContent) );
12.111 +}
12.112 +
12.113 +- (CSSM_OID) contentType
12.114 +{
12.115 + NSData *data = [self _dataFromFunction: &CMSDecoderCopyEncapsulatedContentType];
12.116 + return (CSSM_OID){data.length,(uint8*)data.bytes}; // safe since data is autoreleased
12.117 +}
12.118 +
12.119 +- (NSData*) content
12.120 +{
12.121 + return [self _dataFromFunction: &CMSDecoderCopyContent];
12.122 +}
12.123 +
12.124 +- (BOOL) isSigned
12.125 +{
12.126 + size_t n;
12.127 + return checksave( CMSDecoderGetNumSigners(_decoder, &n) ) && n > 0;
12.128 +}
12.129 +
12.130 +- (BOOL) isEncrypted
12.131 +{
12.132 + Boolean isEncrypted;
12.133 + return check(CMSDecoderIsContentEncrypted(_decoder,&isEncrypted), @"CMSDecoderIsContentEncrypted")
12.134 + && isEncrypted;
12.135 +}
12.136 +
12.137 +- (NSArray*) signers
12.138 +{
12.139 + size_t n;
12.140 + if( ! checksave( CMSDecoderGetNumSigners(_decoder, &n) ) )
12.141 + return nil;
12.142 + NSMutableArray *signers = [NSMutableArray arrayWithCapacity: n];
12.143 + for( size_t i=0; i<n; i++ ) {
12.144 + MYSigner *signer = [[MYSigner alloc] initWithDecoder: _decoder
12.145 + index: i
12.146 + policy: _policy];
12.147 + [signers addObject: signer];
12.148 + [signer release];
12.149 + }
12.150 + return signers;
12.151 +}
12.152 +
12.153 +
12.154 +- (NSArray*) certificates
12.155 +{
12.156 + CFArrayRef certRefs = NULL;
12.157 + if( ! checksave( CMSDecoderCopyAllCerts(_decoder, &certRefs) ) || ! certRefs )
12.158 + return nil;
12.159 + unsigned n = CFArrayGetCount(certRefs);
12.160 + NSMutableArray *certs = [NSMutableArray arrayWithCapacity: n];
12.161 + for( unsigned i=0; i<n; i++ ) {
12.162 + SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(certRefs, i);
12.163 + [certs addObject: [MYCertificate certificateWithCertificateRef: certRef]];
12.164 + }
12.165 + CFRelease(certRefs);
12.166 + return certs;
12.167 +}
12.168 +
12.169 +
12.170 +- (NSString*) dump
12.171 +{
12.172 + static const char * kStatusNames[kCMSSignerInvalidIndex+1] = {
12.173 + "kCMSSignerUnsigned", "kCMSSignerValid", "kCMSSignerNeedsDetachedContent",
12.174 + "kCMSSignerInvalidSignature","kCMSSignerInvalidCert","kCMSSignerInvalidIndex"};
12.175 +
12.176 + CSSM_OID contentType = self.contentType;
12.177 + NSMutableString *s = [NSMutableString stringWithFormat: @"%@<%p>:\n"
12.178 + "\tcontentType = %@ (\"%@\")\n"
12.179 + "\tcontent = %u bytes\n"
12.180 + "\tsigned=%i, encrypted=%i\n"
12.181 + "\tpolicy=%@\n"
12.182 + "\t%u certificates\n"
12.183 + "\tsigners =\n",
12.184 + self.class, self,
12.185 + OIDAsString(contentType), @"??"/*nameOfOID(&contentType)*/,
12.186 + self.content.length,
12.187 + self.isSigned,self.isEncrypted,
12.188 + MYPolicyGetName(_policy),
12.189 + self.certificates.count];
12.190 + for( MYSigner *signer in self.signers ) {
12.191 + CMSSignerStatus status = signer.status;
12.192 + const char *statusName = (status<=kCMSSignerInvalidIndex) ?kStatusNames[status] :"??";
12.193 + [s appendFormat:@"\t\t- status = %s\n"
12.194 + "\t\t verifyResult = %@\n"
12.195 + "\t\t trust = %@\n"
12.196 + "\t\t cert = %@\n",
12.197 + statusName,
12.198 + (signer.verifyResult ?MYErrorName(NSOSStatusErrorDomain,signer.verifyResult)
12.199 + :@"OK"),
12.200 + MYTrustDescribe(signer.trust),
12.201 + signer.certificate];
12.202 + }
12.203 + return s;
12.204 +}
12.205 +
12.206 +
12.207 +@end
12.208 +
12.209 +
12.210 +
12.211 +#pragma mark -
12.212 +@implementation MYSigner : NSObject
12.213 +
12.214 +#define kUncheckedStatus ((CMSSignerStatus)-1)
12.215 +
12.216 +- (id) initWithDecoder: (CMSDecoderRef)decoder index: (size_t)index policy: (CFTypeRef)policy
12.217 +{
12.218 + self = [super init];
12.219 + if( self ) {
12.220 + CFRetain(decoder);
12.221 + _decoder = decoder;
12.222 + _index = index;
12.223 + if(policy) _policy = CFRetain(policy);
12.224 + _status = kUncheckedStatus;
12.225 + }
12.226 + return self;
12.227 +}
12.228 +
12.229 +- (void) dealloc
12.230 +{
12.231 + if(_decoder) CFRelease(_decoder);
12.232 + if(_policy) CFRelease(_policy);
12.233 + if(_trust) CFRelease(_trust);
12.234 + [super dealloc];
12.235 +}
12.236 +
12.237 +- (void) _getInfo {
12.238 + if (_status == kUncheckedStatus) {
12.239 + if( !check(CMSDecoderCopySignerStatus(_decoder, _index, _policy, (_policy!=nil),
12.240 + &_status, &_trust, &_verifyResult),
12.241 + @"CMSDecoderCopySignerStatus"))
12.242 + _status = kMYSignerStatusCheckFailed;
12.243 + }
12.244 +}
12.245 +
12.246 +- (CMSSignerStatus) status
12.247 +{
12.248 + [self _getInfo];
12.249 + return _status;
12.250 +}
12.251 +
12.252 +- (OSStatus) verifyResult
12.253 +{
12.254 + [self _getInfo];
12.255 + return _verifyResult;
12.256 +}
12.257 +
12.258 +- (SecTrustRef) trust
12.259 +{
12.260 + [self _getInfo];
12.261 + return _trust;
12.262 +}
12.263 +
12.264 +
12.265 +- (NSString*) emailAddress
12.266 +{
12.267 + // Don't let caller see the addr if they haven't checked validity & the signature's invalid:
12.268 + if (_status==kUncheckedStatus && self.status != kCMSSignerValid)
12.269 + return nil;
12.270 +
12.271 + CFStringRef email=NULL;
12.272 + if( CMSDecoderCopySignerEmailAddress(_decoder, _index, &email) == noErr )
12.273 + return [(NSString*)CFMakeCollectable(email) autorelease];
12.274 + return nil;
12.275 +}
12.276 +
12.277 +- (MYCertificate *) certificate
12.278 +{
12.279 + // Don't let caller see the cert if they haven't checked validity & the signature's invalid:
12.280 + if (_status==kUncheckedStatus && self.status != kCMSSignerValid)
12.281 + return nil;
12.282 +
12.283 + SecCertificateRef certRef=NULL;
12.284 + OSStatus err = CMSDecoderCopySignerCert(_decoder, _index, &certRef);
12.285 + if( err == noErr )
12.286 + return [MYCertificate certificateWithCertificateRef: certRef];
12.287 + else {
12.288 + Warn(@"CMSDecoderCopySignerCert returned err %i",err);
12.289 + return nil;
12.290 + }
12.291 +}
12.292 +
12.293 +
12.294 +- (NSString*) description
12.295 +{
12.296 + NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[st=%i", self.class,(int)self.status];
12.297 + int verify = self.verifyResult;
12.298 + if( verify )
12.299 + [desc appendFormat: @"; verify error %i",verify];
12.300 + else {
12.301 + MYCertificate *cert = self.certificate;
12.302 + if( cert )
12.303 + [desc appendFormat: @"; %@",cert.commonName];
12.304 + }
12.305 + [desc appendString: @"]"];
12.306 + return desc;
12.307 +}
12.308 +
12.309 +
12.310 +@end
12.311 +
12.312 +
12.313 +
12.314 +// Taken from Keychain.framework
12.315 +NSString* OIDAsString(const CSSM_OID oid) {
12.316 + if ((NULL == oid.Data) || (0 >= oid.Length)) {
12.317 + return nil;
12.318 + } else {
12.319 + NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
12.320 + unsigned int i;
12.321 +
12.322 + for (i = 0; i < oid.Length; ++i) {
12.323 + [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
12.324 + }
12.325 +
12.326 + return result;
12.327 + }
12.328 +}
12.329 +
12.330 +
12.331 +
12.332 +#pragma mark -
12.333 +#pragma mark TEST CASE:
12.334 +
12.335 +
12.336 +#if DEBUG
12.337 +
12.338 +#import "MYEncoder.h"
12.339 +#import "MYIdentity.h"
12.340 +
12.341 +static void TestRoundTrip( NSString *title, NSData *source, MYIdentity *signer, MYCertificate *recipient )
12.342 +{
12.343 + Log(@"Testing MYEncoder/Decoder %@...",title);
12.344 + NSError *error;
12.345 + NSData *encoded = [MYEncoder encodeData: source signer: signer recipient: recipient error: &error];
12.346 + CAssertEq(error,nil);
12.347 + CAssert([encoded length]);
12.348 + Log(@"MYEncoder encoded %u bytes into %u bytes", source.length,encoded.length);
12.349 + Log(@"Decoding...");
12.350 + MYDecoder *d = [[MYDecoder alloc] init];
12.351 + d.policy = [MYCertificate X509Policy];
12.352 + [d addData: encoded];
12.353 + [d finish];
12.354 +
12.355 + CAssertEq(d.error,nil);
12.356 + Log(@"%@", d.dump);
12.357 + CAssert(d.content);
12.358 + CAssert([d.content isEqual: source]);
12.359 + CAssertEq(d.detachedContent,nil);
12.360 + CAssertEq(d.isSigned,(signer!=nil));
12.361 + CAssertEq(d.isEncrypted,(recipient!=nil));
12.362 +
12.363 + if( signer ) {
12.364 + CAssert(d.certificates.count >= 1); // may include extra parent certs
12.365 + CAssertEq(d.signers.count,1U);
12.366 + MYSigner *outSigner = [d.signers objectAtIndex: 0];
12.367 + CAssertEq(outSigner.status,(CMSSignerStatus)kCMSSignerValid);
12.368 + CAssertEq(outSigner.verifyResult,noErr);
12.369 + CAssert([outSigner.certificate isEqualToCertificate: signer]);
12.370 + } else {
12.371 + CAssertEq(d.certificates.count, 0U);
12.372 + CAssertEq(d.signers.count,0U);
12.373 + }
12.374 +}
12.375 +
12.376 +
12.377 +TestCase(MYDecoder) {
12.378 + RequireTestCase(MYEncoder);
12.379 +
12.380 + MYIdentity *me = [MYIdentity preferredIdentityForName: @"MYCryptoTest"];
12.381 + CAssert(me,@"No default identity has been set up in the Keychain");
12.382 + Log(@"Using %@", me);
12.383 +
12.384 + NSData *source = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
12.385 + CAssert(source);
12.386 +
12.387 + TestRoundTrip(@"signing", source, me, nil);
12.388 + TestRoundTrip(@"encryption", source, nil, me);
12.389 + TestRoundTrip(@"signing+encryption", source, me, me);
12.390 +}
12.391 +
12.392 +#endif DEBUG
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/MYEncoder.h Sun Apr 12 22:02:20 2009 -0700
13.3 @@ -0,0 +1,90 @@
13.4 +//
13.5 +// MYEncoder.h
13.6 +// MYCrypto
13.7 +//
13.8 +// Created by Jens Alfke on 1/16/08.
13.9 +// Copyright 2008-2009 Jens Alfke. All rights reserved.
13.10 +//
13.11 +
13.12 +#import <Foundation/Foundation.h>
13.13 +#import <Security/CMSEncoder.h>
13.14 +
13.15 +@class MYIdentity, MYCertificate;
13.16 +
13.17 +
13.18 +/** Creates a CMS-formatted message from a blob of data; it can be signed and/or encrypted. */
13.19 +@interface MYEncoder : NSObject
13.20 +{
13.21 + @private
13.22 + CMSEncoderRef _encoder;
13.23 + OSStatus _error;
13.24 +}
13.25 +
13.26 +/** A convenience method for one-shot encoding of a block of data.
13.27 + @param data The data that will be signed/encrypted.
13.28 + @param signerOrNil If non-nil, an Identity whose private key will sign the data.
13.29 + @param recipientOrNil If non-nil, the data will be encrypted so only the owner of this
13.30 + certificate can read it.
13.31 + @param outError On return, will be set to an NSError if something went wrong.
13.32 + @return The encoded data. */
13.33 ++ (NSData*) encodeData: (NSData*)data
13.34 + signer: (MYIdentity*)signerOrNil
13.35 + recipient: (MYCertificate*)recipientOrNil
13.36 + error: (NSError**)outError;
13.37 +
13.38 +/** Initializes a new encoder.
13.39 + You must add at least one signer or recipient. */
13.40 +- (id) init;
13.41 +
13.42 +/** Tells the encoder to sign the content with this identity's private key.
13.43 + (Multiple signers can be added, but this is rare.) */
13.44 +- (BOOL) addSigner: (MYIdentity*)signer;
13.45 +
13.46 +/** Tells the encoder to encrypt the content with this recipient's public key.
13.47 + Multiple recipients can be added; any one of them will be able to decrypt the message. */
13.48 +- (BOOL) addRecipient: (MYCertificate*)recipient;
13.49 +
13.50 +/** The current error status of the encoder.
13.51 + If something goes wrong with an operation, it will return NO,
13.52 + and this property will contain the error. */
13.53 +@property (readonly) NSError* error;
13.54 +
13.55 +/** Setting this property to YES tells the encoder not to copy the content itself into the
13.56 + encoded message. The encodedData property will then contain only metadata, such as
13.57 + signatures and certificates.
13.58 + This is useful if you're working with a data format that already specifies a content
13.59 + format: it allows you to attach the encoded data elsewhere, e.g. in a header or metadata
13.60 + attribute. */
13.61 +@property BOOL hasDetachedContent;
13.62 +
13.63 +/** Adds data to the encoder. You can add the entire data at once, or in bits and pieces
13.64 + (if you're reading it from a stream). */
13.65 +- (BOOL) addData: (NSData*)data;
13.66 +
13.67 +/** The signed/encoded output data.
13.68 + Don't call this until after the last call to -addData:. */
13.69 +- (NSData*) encodedData;
13.70 +
13.71 +
13.72 +/** @name Expert
13.73 + * Advanced methods.
13.74 + */
13.75 +//@{
13.76 +
13.77 +/** Adds a timestamp showing when the message was encoded.
13.78 + [Unfortunately there is no system API for reading these timestamps in decoded messages...] */
13.79 +- (BOOL) addTimestamp;
13.80 +
13.81 +/** Specifies which certificates to include in the message: none, only the signer certs,
13.82 + or the signer certs' entire chain (the default). */
13.83 +@property CMSCertificateChainMode certificateChainMode;
13.84 +
13.85 +/** Adds an extra certificate to the encoded data, for the recipient's use. Rarely needed. */
13.86 +- (BOOL) addSupportingCert: (MYCertificate*)supportingCert;
13.87 +
13.88 +/** The X.509 content type of the message data. */
13.89 +@property CSSM_OID contentType;
13.90 +
13.91 +//@}
13.92 +
13.93 +@end
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/MYEncoder.m Sun Apr 12 22:02:20 2009 -0700
14.3 @@ -0,0 +1,201 @@
14.4 +//
14.5 +// MYEncoder.m
14.6 +// MYCrypto
14.7 +//
14.8 +// Created by Jens Alfke on 1/16/08.
14.9 +// Copyright 2008-2009 Jens Alfke. All rights reserved.
14.10 +//
14.11 +
14.12 +#import "MYEncoder.h"
14.13 +#import "MYIdentity.h"
14.14 +#import "MYCrypto_Private.h"
14.15 +#import "Test.h"
14.16 +#import "MYErrorUtils.h"
14.17 +
14.18 +
14.19 +@implementation MYEncoder
14.20 +
14.21 +
14.22 +- (id) init
14.23 +{
14.24 + self = [super init];
14.25 + if (self != nil) {
14.26 + if( ! checksave(CMSEncoderCreate(&_encoder)) ) {
14.27 + [self release];
14.28 + return nil;
14.29 + }
14.30 + }
14.31 + return self;
14.32 +}
14.33 +
14.34 +- (void) dealloc
14.35 +{
14.36 + if(_encoder) CFRelease(_encoder);
14.37 + [super dealloc];
14.38 +}
14.39 +
14.40 +
14.41 +
14.42 +- (BOOL) addSigner: (MYIdentity*)signer
14.43 +{
14.44 + Assert(signer);
14.45 + return checksave( CMSEncoderAddSigners(_encoder, signer.identityRef) );
14.46 +}
14.47 +
14.48 +- (BOOL) addRecipient: (MYCertificate*)recipient
14.49 +{
14.50 + Assert(recipient);
14.51 + return checksave( CMSEncoderAddRecipients(_encoder, recipient.certificateRef) );
14.52 +}
14.53 +
14.54 +- (BOOL) addSupportingCert: (MYCertificate*)supportingCert
14.55 +{
14.56 + Assert(supportingCert);
14.57 + return checksave( CMSEncoderAddSupportingCerts(_encoder, supportingCert.certificateRef) );
14.58 +}
14.59 +
14.60 +- (BOOL) addTimestamp
14.61 +{
14.62 + return checksave( CMSEncoderAddSignedAttributes(_encoder, kCMSAttrSigningTime) );
14.63 +}
14.64 +
14.65 +
14.66 +- (NSError*) error
14.67 +{
14.68 + if( _error )
14.69 + return MYError(_error, NSOSStatusErrorDomain,
14.70 + @"%@", MYErrorName(NSOSStatusErrorDomain,_error));
14.71 + else
14.72 + return nil;
14.73 +}
14.74 +
14.75 +
14.76 +- (CMSCertificateChainMode) certificateChainMode
14.77 +{
14.78 + CMSCertificateChainMode mode;
14.79 + if( CMSEncoderGetCertificateChainMode(_encoder, &mode) == noErr )
14.80 + return mode;
14.81 + else
14.82 + return -1;
14.83 +}
14.84 +
14.85 +- (void) setCertificateChainMode: (CMSCertificateChainMode)mode
14.86 +{
14.87 + checksave( CMSEncoderSetCertificateChainMode(_encoder, mode) );
14.88 +}
14.89 +
14.90 +- (BOOL) hasDetachedContent
14.91 +{
14.92 + Boolean detached;
14.93 + return CMSEncoderGetHasDetachedContent(_encoder, &detached)==noErr && detached;
14.94 +}
14.95 +
14.96 +- (void) setHasDetachedContent: (BOOL)detached
14.97 +{
14.98 + checksave( CMSEncoderSetHasDetachedContent(_encoder, detached) );
14.99 +}
14.100 +
14.101 +- (NSData*) _dataFromFunction: (OSStatus (*)(CMSEncoderRef,CFDataRef*))function
14.102 +{
14.103 + CFDataRef data=NULL;
14.104 + if( checksave( (*function)(_encoder, &data) ) )
14.105 + return [(NSData*)CFMakeCollectable(data) autorelease];
14.106 + else
14.107 + return nil;
14.108 +}
14.109 +
14.110 +
14.111 +- (CSSM_OID) contentType
14.112 +{
14.113 + NSData *data = [self _dataFromFunction: &CMSEncoderCopyEncapsulatedContentType];
14.114 + return (CSSM_OID){data.length,(uint8*)data.bytes};
14.115 +}
14.116 +
14.117 +- (void) setContentType: (CSSM_OID)contentType
14.118 +{
14.119 + checksave( CMSEncoderSetEncapsulatedContentType(_encoder, &contentType) );
14.120 +}
14.121 +
14.122 +
14.123 +- (BOOL) addData: (NSData*)data
14.124 +{
14.125 + Assert(data);
14.126 + return ! _error && checksave( CMSEncoderUpdateContent(_encoder, data.bytes, data.length) );
14.127 +}
14.128 +
14.129 +
14.130 +- (NSData*) encodedData
14.131 +{
14.132 + if( ! _error )
14.133 + return [self _dataFromFunction: &CMSEncoderCopyEncodedContent];
14.134 + else
14.135 + return nil;
14.136 +}
14.137 +
14.138 +
14.139 ++ (NSData*) encodeData: (NSData*)data
14.140 + signer: (MYIdentity*)signer
14.141 + recipient: (MYCertificate*)recipient
14.142 + error: (NSError**)outError
14.143 +{
14.144 + MYEncoder *e = [[self alloc] init];
14.145 + if( signer )
14.146 + [e addSigner: signer];
14.147 + if( recipient )
14.148 + [e addRecipient: recipient];
14.149 + [e addData: data];
14.150 + *outError = e.error;
14.151 + NSData *result = e.encodedData;
14.152 + [e release];
14.153 + return result;
14.154 +}
14.155 +
14.156 +
14.157 +@end
14.158 +
14.159 +
14.160 +#if DEBUG
14.161 +
14.162 +#import "MYCrypto+Cocoa.h"
14.163 +
14.164 +TestCase(MYEncoder) {
14.165 + MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
14.166 + if (!me) {
14.167 + NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
14.168 + SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
14.169 + [panel setAlternateButtonTitle: @"Cancel"];
14.170 + if ([panel my_runModalForIdentities: idents
14.171 + message: @"Choose an identity for the MYEncoder test case:"]
14.172 + != NSOKButton) {
14.173 + [NSException raise: NSGenericException format: @"User canceled"];
14.174 + }
14.175 + me = [panel my_identity];
14.176 + [me makePreferredIdentityForName: @"MYCryptoTest"];
14.177 + }
14.178 + CAssert(me,@"No default identity has been set up in the Keychain");
14.179 +
14.180 + NSData *source = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
14.181 + CAssert(source);
14.182 +
14.183 + NSError *error;
14.184 + NSData *encoded;
14.185 +
14.186 + Log(@"Testing signing...");
14.187 + encoded = [MYEncoder encodeData: source signer: me recipient: nil error: &error];
14.188 + CAssertEq(error,nil);
14.189 + CAssert([encoded length]);
14.190 + Log(@"MYEncoder signed %u bytes into %u bytes", source.length,encoded.length);
14.191 +
14.192 + Log(@"Testing encryption...");
14.193 + encoded = [MYEncoder encodeData: source signer: nil recipient: me error: &error];
14.194 + CAssertEq(error,nil);
14.195 + CAssert([encoded length]);
14.196 + Log(@"MYEncoder encrypted %u bytes into %u bytes", source.length,encoded.length);
14.197 +
14.198 + Log(@"Testing signing+encryption...");
14.199 + encoded = [MYEncoder encodeData: source signer: me recipient: me error: &error];
14.200 + CAssertEq(error,nil);
14.201 + CAssert([encoded length]);
14.202 + Log(@"MYEncoder signed/encrypted %u bytes into %u bytes", source.length,encoded.length);
14.203 +}
14.204 +#endif
15.1 --- a/MYIdentity.h Thu Apr 09 22:47:11 2009 -0700
15.2 +++ b/MYIdentity.h Sun Apr 12 22:02:20 2009 -0700
15.3 @@ -17,12 +17,20 @@
15.4 SecIdentityRef _identityRef;
15.5 }
15.6
15.7 -/** Initializes a MYIdentity given an existing SecIdentityRef. */
15.8 -- (id) initWithIdentityRef: (SecIdentityRef)identityRef;
15.9 +/** Creates a MYIdentity object for an existing Keychain identity reference. */
15.10 ++ (MYIdentity*) identityWithIdentityRef: (SecIdentityRef)identityRef;
15.11 +
15.12 +/** The underlying SecIdentityRef. */
15.13 +@property (readonly) SecIdentityRef identityRef;
15.14
15.15 /** The identity's associated private key. */
15.16 @property (readonly) MYPrivateKey *privateKey;
15.17
15.18 +
15.19 +/** @name Mac-Only
15.20 + * Functionality not available on iPhone.
15.21 + */
15.22 +//@{
15.23 #if !TARGET_OS_IPHONE
15.24
15.25 /** Returns the identity that's been set as the preferred one for the given name, or nil. */
15.26 @@ -33,5 +41,17 @@
15.27 - (BOOL) makePreferredIdentityForName: (NSString*)name;
15.28
15.29 #endif
15.30 +//@}
15.31 +
15.32 +
15.33 +/** @name Expert
15.34 + * Advanced methods.
15.35 + */
15.36 +//@{
15.37 +
15.38 +/** Initializes a MYIdentity given an existing SecIdentityRef. */
15.39 +- (id) initWithIdentityRef: (SecIdentityRef)identityRef;
15.40 +
15.41 +//@}
15.42
15.43 @end
16.1 --- a/MYIdentity.m Thu Apr 09 22:47:11 2009 -0700
16.2 +++ b/MYIdentity.m Sun Apr 12 22:02:20 2009 -0700
16.3 @@ -13,6 +13,11 @@
16.4 @implementation MYIdentity
16.5
16.6
16.7 +/** Creates a MYIdentity object for an existing Keychain identity reference. */
16.8 ++ (MYIdentity*) identityWithIdentityRef: (SecIdentityRef)identityRef {
16.9 + return [[[self alloc] initWithIdentityRef: identityRef] autorelease];
16.10 +}
16.11 +
16.12 - (id) initWithIdentityRef: (SecIdentityRef)identityRef {
16.13 Assert(identityRef);
16.14 SecCertificateRef certificateRef;
16.15 @@ -57,6 +62,8 @@
16.16 }
16.17
16.18
16.19 +@synthesize identityRef=_identityRef;
16.20 +
16.21 - (MYPrivateKey*) privateKey {
16.22 SecKeyRef keyRef = NULL;
16.23 if (!check(SecIdentityCopyPrivateKey(_identityRef, &keyRef), @"SecIdentityCopyPrivateKey"))
16.24 @@ -74,10 +81,10 @@
16.25 {
16.26 Assert(name);
16.27 SecIdentityRef identityRef;
16.28 - if (!check(SecIdentityCopyPreference((CFStringRef)name, 0, NULL, &identityRef),
16.29 - @"SecIdentityCopyPreference"))
16.30 + OSStatus err = SecIdentityCopyPreference((CFStringRef)name, 0, NULL, &identityRef);
16.31 + if (err==errKCItemNotFound || !check(err,@"SecIdentityCopyPreference") || !identityRef)
16.32 return nil;
16.33 - return identityRef ?[[[self alloc] initWithIdentityRef: identityRef] autorelease] :nil;
16.34 + return [self identityWithIdentityRef: identityRef];
16.35 }
16.36
16.37 - (BOOL) makePreferredIdentityForName: (NSString*)name {
17.1 --- a/MYKey.m Thu Apr 09 22:47:11 2009 -0700
17.2 +++ b/MYKey.m Sun Apr 12 22:02:20 2009 -0700
17.3 @@ -40,6 +40,10 @@
17.4 }
17.5
17.6
17.7 +- (NSString*) description {
17.8 + return $sprintf(@"%@[%@ /%p]", [self class], (self.name ?:@""), self.keychainItemRef);
17.9 +}
17.10 +
17.11 - (SecExternalItemType) keyType {
17.12 AssertAbstractMethod();
17.13 }
18.1 --- a/MYPrivateKey.h Thu Apr 09 22:47:11 2009 -0700
18.2 +++ b/MYPrivateKey.h Sun Apr 12 22:02:20 2009 -0700
18.3 @@ -51,7 +51,10 @@
18.4 /** Creates a self-signed identity certificate using this key-pair.
18.5 The attributes describe the certificate's metadata, including its expiration date and the
18.6 subject's name. Keys for the dictionary are given below; the only mandatory one is
18.7 - kMYIdentityCommonNameKey. */
18.8 + kMYIdentityCommonNameKey.
18.9 + The resulting identity certificate includes X.509 extended attributes allowing it to be
18.10 + used for SSL connections. (Plug: See my MYNetwork library for an easy way to run SSL
18.11 + servers and clients.) */
18.12 - (MYIdentity*) createSelfSignedIdentityWithAttributes: (NSDictionary*)attributes;
18.13
18.14 /** Exports the private key as a data blob, so that it can be stored as a backup, or transferred
18.15 @@ -84,6 +87,7 @@
18.16
18.17
18.18 /* Attribute keys for creating identities: */
18.19 +
18.20 #define kMYIdentityCommonNameKey @"Common Name" // NSString. Required!
18.21 #define kMYIdentityGivenNameKey @"Given Name"
18.22 #define kMYIdentitySurnameKey @"Surname"
19.1 --- a/MYPrivateKey.m Thu Apr 09 22:47:11 2009 -0700
19.2 +++ b/MYPrivateKey.m Sun Apr 12 22:02:20 2009 -0700
19.3 @@ -155,9 +155,9 @@
19.4 0LL,
19.5 CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY, // public key
19.6 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
19.7 - CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN, // private key
19.8 - CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_PERMANENT,
19.9 - NULL, // SecAccessRef
19.10 + CSSM_KEYUSE_ANY, // private key
19.11 + CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
19.12 + NULL, // SecAccessRef
19.13 &pubKey, &privKey);
19.14 #endif
19.15 if (!check(err, @"SecKeyCreatePair")) {
19.16 @@ -172,7 +172,10 @@
19.17
19.18
19.19 - (NSString*) description {
19.20 - return $sprintf(@"%@[%@]", [self class], self.publicKeyDigest.abbreviatedHexString);
19.21 + return $sprintf(@"%@[%@ %@ /%p]", [self class],
19.22 + self.publicKeyDigest.abbreviatedHexString,
19.23 + (self.name ?:@""),
19.24 + self.keychainItemRef);
19.25 }
19.26
19.27 @synthesize publicKey=_publicKey;
20.1 --- a/MYSymmetricKey.m Thu Apr 09 22:47:11 2009 -0700
20.2 +++ b/MYSymmetricKey.m Sun Apr 12 22:02:20 2009 -0700
20.3 @@ -62,7 +62,6 @@
20.4 {(id)kSecAttrCanSign, $false},
20.5 {(id)kSecAttrCanVerify, $false},
20.6 {(id)kSecValueData, keyData},
20.7 - //{(id)kSecAttrApplicationTag, [@"foo" dataUsingEncoding: NSUTF8StringEncoding]}, //TEMP
20.8 {(id)kSecReturnPersistentRef, $true});
20.9 if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
20.10 [self release];
21.1 --- a/README.textile Thu Apr 09 22:47:11 2009 -0700
21.2 +++ b/README.textile Sun Apr 12 22:02:20 2009 -0700
21.3 @@ -20,6 +20,8 @@
21.4 * Cryptographic digests/hashes (effectively-unique IDs for data)
21.5 * The Keychain (a secure, encrypted storage system for keys and passwords)
21.6
21.7 +It's open source, released under a friendly BSD license.
21.8 +
21.9 h3. Requirements
21.10
21.11 * Mac OS X 10.5 or later _[has been tested on 10.5.6]_
21.12 @@ -32,7 +34,7 @@
21.13
21.14 * "Download the current source code":http://mooseyard.com/hg/hgwebdir.cgi/MYCrypto/archive/tip.zip
21.15 * To check out the source code using "Mercurial":http://selenic.com/mercurial/:<br>
21.16 -@hg clone /hg/hgwebdir.cgi/MYCrypto/ MYCrypto@
21.17 +@hg clone http://mooseyard.com/hg/hgwebdir.cgi/MYCrypto/ MYCrypto@
21.18 * As described above, you'll also need to download or check out MYUtilities and put it in a directory next to MYCrypto.
21.19 * Or if you're just looking:
21.20 ** "Browse the source code":http://mooseyard.com/hg/hgwebdir.cgi/MYCrypto/file/tip