1.1 --- a/MYParsedCertificate.m Wed Jun 03 17:20:53 2009 -0700
1.2 +++ b/MYParsedCertificate.m Thu Jun 04 18:36:30 2009 -0700
1.3 @@ -7,6 +7,7 @@
1.4 //
1.5
1.6 // References:
1.7 +// <http://www.columbia.edu/~ariel/ssleay/layman.html>
1.8 // <http://en.wikipedia.org/wiki/X.509>
1.9 // <http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt>
1.10
1.11 @@ -17,10 +18,14 @@
1.12 #import "MYBERParser.h"
1.13 #import "MYDEREncoder.h"
1.14 #import "MYPublicKey.h"
1.15 +#import "MYPrivateKey.h"
1.16 #import "MYCertificate.h"
1.17 #import "MYErrorUtils.h"
1.18
1.19
1.20 +#define kDefaultExpirationTime (60.0 * 60.0 * 24.0 * 365.0)
1.21 +
1.22 +
1.23 static id $atIf(NSArray *array, NSUInteger index) {
1.24 return index < array.count ?[array objectAtIndex: index] :nil;
1.25 }
1.26 @@ -29,24 +34,30 @@
1.27 @implementation MYParsedCertificate
1.28
1.29
1.30 -static MYOID *kRSAAlgorithmID, *kRSAWithSHA1AlgorithmID;
1.31 +static MYOID *kRSAAlgorithmID, *kRSAWithSHA1AlgorithmID, *kCommonNameOID,
1.32 + *kGivenNameOID, *kSurnameOID, *kDescriptionOID, *kEmailOID;
1.33
1.34
1.35 + (void) initialize {
1.36 - if (!kRSAAlgorithmID) {
1.37 - UInt32 components[7] = {1, 2, 840, 113549, 1, 1, 1,};
1.38 - kRSAAlgorithmID = [[MYOID alloc] initWithComponents: components count: 7];
1.39 + if (!kEmailOID) {
1.40 + kRSAAlgorithmID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 1, 1,}
1.41 + count: 7];
1.42 + kRSAWithSHA1AlgorithmID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 1, 5}
1.43 + count: 7];
1.44 + kCommonNameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 3}
1.45 + count: 4];
1.46 + kGivenNameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 42}
1.47 + count: 4];
1.48 + kSurnameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 4}
1.49 + count: 4];
1.50 + kDescriptionOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 13}
1.51 + count: 7];
1.52 + kEmailOID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 9, 1}
1.53 + count: 7];
1.54 }
1.55 - if (!kRSAWithSHA1AlgorithmID) {
1.56 - UInt32 components[7] = {1, 2, 840, 113549, 1, 1, 5};
1.57 - kRSAWithSHA1AlgorithmID = [[MYOID alloc] initWithComponents: components count: 7];
1.58 - }
1.59 +
1.60 }
1.61
1.62 -+ (MYOID*) RSAAlgorithmID {return kRSAAlgorithmID;}
1.63 -+ (MYOID*) RSAWithSHA1AlgorithmID {return kRSAWithSHA1AlgorithmID;}
1.64 -
1.65 -
1.66 + (NSString*) validate: (id)root {
1.67 NSArray *top = $castIf(NSArray,root);
1.68 if (top.count < 3)
1.69 @@ -91,48 +102,71 @@
1.70
1.71 [_root release];
1.72 [_issuer release];
1.73 + [_data release];
1.74 [super dealloc];
1.75 }
1.76
1.77
1.78 -@synthesize issuer=_issuer;
1.79 +- (NSArray*) _info {return $castIf(NSArray,$atIf(_root,0));}
1.80
1.81 +- (NSArray*) _validDates {return $castIf(NSArray, [self._info objectAtIndex: 4]);}
1.82
1.83 -- (NSArray*) info {return $castIf(NSArray,$atIf(_root,0));}
1.84 -
1.85 -- (BOOL) isSelfSigned {
1.86 - id issuer = $atIf(self.info,3);
1.87 - id subject = $atIf(self.info,5);
1.88 - return $equal(issuer,subject);
1.89 +- (NSArray*) _pairForOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
1.90 + NSArray *names = $castIf(NSArray, $atIf(self._info, infoIndex));
1.91 + for (id nameEntry in names) {
1.92 + for (id pair in $castIf(NSSet,nameEntry)) {
1.93 + if ([pair isKindOfClass: [NSArray class]] && [pair count] == 2) {
1.94 + if ($equal(oid, [pair objectAtIndex: 0]))
1.95 + return pair;
1.96 + }
1.97 + }
1.98 + }
1.99 + return nil;
1.100 }
1.101
1.102 +- (NSString*) _stringForOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
1.103 + return [[self _pairForOID: oid atInfoIndex: infoIndex] objectAtIndex: 1];
1.104 +}
1.105 +
1.106 +
1.107 +@synthesize issuer=_issuer, certificateData=_data;
1.108 +
1.109 +
1.110 +- (NSDate*) validFrom {return $castIf(NSDate, $atIf(self._validDates, 0));}
1.111 +- (NSDate*) validTo {return $castIf(NSDate, $atIf(self._validDates, 1));}
1.112 +- (NSString*) commonName {return [self _stringForOID: kCommonNameOID atInfoIndex: 5];}
1.113 +- (NSString*) givenName {return [self _stringForOID: kGivenNameOID atInfoIndex: 5];}
1.114 +- (NSString*) surname {return [self _stringForOID: kSurnameOID atInfoIndex: 5];}
1.115 +- (NSString*) description {return [self _stringForOID: kDescriptionOID atInfoIndex: 5];}
1.116 +- (NSString*) emailAddress {return [self _stringForOID: kEmailOID atInfoIndex: 5];}
1.117 +
1.118 +- (BOOL) isSigned {return [_root count] >= 3;}
1.119 +
1.120 +- (BOOL) isRoot {
1.121 + id issuer = $atIf(self._info,3);
1.122 + return $equal(issuer, $atIf(self._info,5)) || $equal(issuer, $array());
1.123 +}
1.124 +
1.125 +
1.126 - (MYPublicKey*) subjectPublicKey {
1.127 - NSArray *keyInfo = $cast(NSArray, $atIf(self.info, 6));
1.128 + NSArray *keyInfo = $cast(NSArray, $atIf(self._info, 6));
1.129 MYOID *keyAlgorithmID = $castIf(MYOID, $atIf($castIf(NSArray,$atIf(keyInfo,0)), 0));
1.130 if (!$equal(keyAlgorithmID, kRSAAlgorithmID))
1.131 return nil;
1.132 MYBitString *keyData = $cast(MYBitString, $atIf(keyInfo, 1));
1.133 if (!keyData) return nil;
1.134 return [[[MYPublicKey alloc] initWithKeyData: keyData.bits] autorelease];
1.135 - /*
1.136 - NSArray *keyParts = $castIf(NSArray, MYBERParse(keyData, nil));
1.137 - if (!keyParts) return nil;
1.138 - MYBitString *modulus = $castIf(MYBitString, $atIf(keyParts,0));
1.139 - int exponent = [$castIf(NSNumber, $atIf(keyParts,1)) intValue];
1.140 - if (!modulus || exponent<3) return nil;
1.141 - */
1.142 }
1.143
1.144 - (MYPublicKey*) issuerPublicKey {
1.145 if (_issuer)
1.146 return _issuer.publicKey;
1.147 - else if (self.isSelfSigned)
1.148 + else if (self.isRoot)
1.149 return self.subjectPublicKey;
1.150 else
1.151 return nil;
1.152 }
1.153
1.154 -
1.155 - (NSData*) signedData {
1.156 // The root object is a sequence; we want to extract the 1st object of that sequence.
1.157 const UInt8 *certStart = _data.bytes;
1.158 @@ -159,7 +193,7 @@
1.159 }
1.160
1.161 - (BOOL) validateSignature {
1.162 - if (!$equal(self.signatureAlgorithmID, [MYParsedCertificate RSAWithSHA1AlgorithmID]))
1.163 + if (!$equal(self.signatureAlgorithmID, kRSAWithSHA1AlgorithmID))
1.164 return NO;
1.165 NSData *signedData = self.signedData;
1.166 NSData *signature = self.signature;
1.167 @@ -169,45 +203,191 @@
1.168 }
1.169
1.170
1.171 +#pragma mark -
1.172 +#pragma mark CERTIFICATE GENERATION:
1.173 +
1.174 +
1.175 +- (id) initWithPublicKey: (MYPublicKey*)pubKey {
1.176 + Assert(pubKey);
1.177 + self = [super init];
1.178 + if (self != nil) {
1.179 + id empty = [NSNull null];
1.180 + id version = [[MYASN1Object alloc] initWithTag: 0 ofClass: 2 components: $array($object(0))];
1.181 + _root = $array( $marray(version,
1.182 + empty, // serial #
1.183 + $array(kRSAAlgorithmID),
1.184 + $marray(),
1.185 + $marray(empty, empty),
1.186 + $marray(),
1.187 + $array( $array(kRSAAlgorithmID, empty),
1.188 + [MYBitString bitStringWithData: pubKey.keyData] ) ) );
1.189 + [version release];
1.190 + [_root retain];
1.191 + }
1.192 + return self;
1.193 +}
1.194 +
1.195 +
1.196 +- (void) _setString: (NSString*)value forOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
1.197 + NSMutableArray *pair = (NSMutableArray*) [self _pairForOID: oid atInfoIndex: infoIndex];
1.198 + if (pair) {
1.199 + [pair replaceObjectAtIndex: 1 withObject: value];
1.200 + } else {
1.201 + NSMutableArray *names = $castIf(NSMutableArray, $atIf(self._info, infoIndex));
1.202 + [names addObject: [NSSet setWithObject: $marray(oid,value)]];
1.203 + }
1.204 +}
1.205 +
1.206 +
1.207 +- (void) setValidFrom: (NSDate*)validFrom {
1.208 + [(NSMutableArray*)self._validDates replaceObjectAtIndex: 0 withObject: validFrom];
1.209 +}
1.210 +
1.211 +- (void) setValidTo: (NSDate*)validTo {
1.212 + [(NSMutableArray*)self._validDates replaceObjectAtIndex: 1 withObject: validTo];
1.213 +}
1.214 +
1.215 +- (void) setCommonName: (NSString*)commonName {
1.216 + [self _setString: commonName forOID: kCommonNameOID atInfoIndex: 5];
1.217 +}
1.218 +
1.219 +- (void) setGivenName: (NSString*)givenName {
1.220 + [self _setString: givenName forOID: kGivenNameOID atInfoIndex: 5];
1.221 +}
1.222 +
1.223 +- (void) setSurname: (NSString*)surname {
1.224 + [self _setString: surname forOID: kSurnameOID atInfoIndex: 5];
1.225 +}
1.226 +
1.227 +- (void) setDescription: (NSString*)description {
1.228 + [self _setString: description forOID: kDescriptionOID atInfoIndex: 5];
1.229 +}
1.230 +
1.231 +- (void) setEmailAddress: (NSString*)emailAddress {
1.232 + [self _setString: emailAddress forOID: kEmailOID atInfoIndex: 5];
1.233 +}
1.234 +
1.235 +
1.236 +- (BOOL) selfSignWithPrivateKey: (MYPrivateKey*)privateKey error: (NSError**)outError {
1.237 + // Copy subject to issuer:
1.238 + NSMutableArray *info = (NSMutableArray*)self._info;
1.239 + [info replaceObjectAtIndex: 3 withObject: [info objectAtIndex: 5]];
1.240 +
1.241 + // Set serial number if there isn't one yet:
1.242 + if (!$castIf(NSNumber, [info objectAtIndex: 1])) {
1.243 + UInt64 serial = floor(CFAbsoluteTimeGetCurrent() * 1000);
1.244 + [info replaceObjectAtIndex: 1 withObject: $object(serial)];
1.245 + }
1.246 +
1.247 + // Set up valid date range if there isn't one yet:
1.248 + NSDate *validFrom = self.validFrom;
1.249 + if (!validFrom)
1.250 + validFrom = self.validFrom = [NSDate date];
1.251 + NSDate *validTo = self.validTo;
1.252 + if (!validTo)
1.253 + self.validTo = [validFrom addTimeInterval: kDefaultExpirationTime];
1.254 +
1.255 + // Append signature to cert structure:
1.256 + NSData *dataToSign = [MYDEREncoder encodeRootObject: info error: outError];
1.257 + if (!dataToSign)
1.258 + return NO;
1.259 + setObj(&_root, $array(info,
1.260 + $array(kRSAWithSHA1AlgorithmID, [NSNull null]),
1.261 + [MYBitString bitStringWithData: [privateKey signData: dataToSign]]));
1.262 +
1.263 + setObj(&_data, [MYDEREncoder encodeRootObject: _root error: outError]);
1.264 + return _data!=nil;
1.265 +}
1.266 +
1.267 +
1.268 @end
1.269
1.270
1.271
1.272
1.273 +#if DEBUG
1.274 +
1.275 +
1.276 +static MYParsedCertificate* testCert(NSString *filename, BOOL selfSigned) {
1.277 + Log(@"--- Creating MYParsedCertificate from %@", filename);
1.278 + NSData *certData = [NSData dataWithContentsOfFile: filename];
1.279 + //Log(@"Cert Data =\n%@", certData);
1.280 + NSError *error = nil;
1.281 + MYParsedCertificate *pcert = [[MYParsedCertificate alloc] initWithCertificateData: certData
1.282 + error: &error];
1.283 + CAssertNil(error);
1.284 + CAssert(pcert != nil);
1.285 +
1.286 + CAssertEq(pcert.isRoot, selfSigned);
1.287 +
1.288 + NSData *signedData = pcert.signedData;
1.289 + //Log(@"Signed Data = (length=%x)\n%@", signedData.length, signedData);
1.290 + CAssertEqual(signedData, [certData subdataWithRange: NSMakeRange(4,signedData.length)]);
1.291 +
1.292 + Log(@"AlgID = %@", pcert.signatureAlgorithmID);
1.293 + Log(@"Signature = %@", pcert.signature);
1.294 + CAssertEqual(pcert.signatureAlgorithmID, kRSAWithSHA1AlgorithmID);
1.295 + CAssert(pcert.signature != nil);
1.296 + Log(@"Subject Public Key = %@", pcert.subjectPublicKey);
1.297 + CAssert(pcert.subjectPublicKey);
1.298 + if (selfSigned) {
1.299 + Log(@"Issuer Public Key = %@", pcert.issuerPublicKey);
1.300 + CAssert(pcert.issuerPublicKey);
1.301 +
1.302 + CAssert(pcert.validateSignature);
1.303 + }
1.304 + Log(@"Common Name = %@", pcert.commonName);
1.305 + Log(@"Given Name = %@", pcert.givenName);
1.306 + Log(@"Surname = %@", pcert.surname);
1.307 + Log(@"Desc = %@", pcert.description);
1.308 + Log(@"Email = %@", pcert.emailAddress);
1.309 + return pcert;
1.310 +}
1.311 +
1.312 +
1.313 TestCase(ParsedCert) {
1.314 - auto void testCert(NSString *filename, BOOL selfSigned);
1.315 testCert(@"../../Tests/selfsigned.cer", YES);
1.316 testCert(@"../../Tests/iphonedev.cer", NO);
1.317 - auto void testCert(NSString *filename, BOOL selfSigned) {
1.318 - Log(@"--- Creating MYParsedCertificate from %@", filename);
1.319 - NSData *certData = [NSData dataWithContentsOfFile: filename];
1.320 - //Log(@"Cert Data =\n%@", certData);
1.321 - NSError *error = nil;
1.322 - MYParsedCertificate *pcert = [[MYParsedCertificate alloc] initWithCertificateData: certData
1.323 - error: &error];
1.324 - CAssertNil(error);
1.325 - CAssert(pcert != nil);
1.326 -
1.327 - CAssertEq(pcert.isSelfSigned, selfSigned);
1.328 -
1.329 - NSData *signedData = pcert.signedData;
1.330 - //Log(@"Signed Data = (length=%x)\n%@", signedData.length, signedData);
1.331 - CAssertEqual(signedData, [certData subdataWithRange: NSMakeRange(4,signedData.length)]);
1.332 -
1.333 - Log(@"AlgID = %@", pcert.signatureAlgorithmID);
1.334 - Log(@"Signature = %@", pcert.signature);
1.335 - CAssertEqual(pcert.signatureAlgorithmID, [MYParsedCertificate RSAWithSHA1AlgorithmID]);
1.336 - CAssert(pcert.signature != nil);
1.337 - Log(@"Subject Public Key = %@", pcert.subjectPublicKey);
1.338 - CAssert(pcert.subjectPublicKey);
1.339 - if (selfSigned) {
1.340 - Log(@"Issuer Public Key = %@", pcert.issuerPublicKey);
1.341 - CAssert(pcert.issuerPublicKey);
1.342 -
1.343 - CAssert(pcert.validateSignature);
1.344 - }
1.345 - }
1.346 -}
1.347 +}
1.348 +
1.349 +
1.350 +#import "MYKeychain.h"
1.351 +
1.352 +TestCase(CreateCert) {
1.353 + MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
1.354 + MYParsedCertificate *pcert = [[MYParsedCertificate alloc] initWithPublicKey: privateKey.publicKey];
1.355 + pcert.commonName = @"testcase";
1.356 + pcert.givenName = @"Test";
1.357 + pcert.surname = @"Case";
1.358 + pcert.description = @"Just a test certificate created by MYCrypto";
1.359 + pcert.emailAddress = @"testcase@example.com";
1.360 +
1.361 + CAssertEqual(pcert.commonName, @"testcase");
1.362 + CAssertEqual(pcert.givenName, @"Test");
1.363 + CAssertEqual(pcert.surname, @"Case");
1.364 + CAssertEqual(pcert.description, @"Just a test certificate created by MYCrypto");
1.365 + CAssertEqual(pcert.emailAddress, @"testcase@example.com");
1.366 +
1.367 + Log(@"Signing...");
1.368 + NSError *error;
1.369 + CAssert([pcert selfSignWithPrivateKey: privateKey error: &error]);
1.370 + CAssertNil(error);
1.371 + NSData *certData = pcert.certificateData;
1.372 + Log(@"Generated cert = \n%@", certData);
1.373 + CAssert(certData);
1.374 + [certData writeToFile: @"../../Tests/generated.cer" atomically: YES];
1.375 + MYParsedCertificate *pcert2 = testCert(@"../../Tests/generated.cer", YES);
1.376 +
1.377 + Log(@"Verifying...");
1.378 + CAssertEqual(pcert2.commonName, @"testcase");
1.379 + CAssertEqual(pcert2.givenName, @"Test");
1.380 + CAssertEqual(pcert2.surname, @"Case");
1.381 + CAssertEqual(pcert2.description, @"Just a test certificate created by MYCrypto");
1.382 + CAssertEqual(pcert2.emailAddress, @"testcase@example.com");
1.383 +}
1.384 +
1.385 +#endif
1.386 +
1.387
1.388
1.389
1.390 @@ -215,8 +395,8 @@
1.391
1.392 Sequence: <-- top
1.393 Sequence: <-- info
1.394 - MYASN1Object[2/0]: <-- version (int, constructed)
1.395 - 2
1.396 + MYASN1Object[2/0]: <-- version (tag=0, constructed)
1.397 + 2
1.398 1 <-- serial number
1.399 Sequence:
1.400 {1 2 840 113549 1 1 1} <-- algorithm ID
1.401 @@ -246,23 +426,23 @@
1.402 2010-04-13 21:54:35 -0700
1.403 Sequence: <-- subject
1.404 Set:
1.405 - Sequence:
1.406 + Sequence: <-- surname
1.407 {2 5 4 4}
1.408 Widdershins
1.409 Set:
1.410 - Sequence:
1.411 + Sequence: <-- email
1.412 {1 2 840 113549 1 9 1}
1.413 waldo@example.com
1.414 Set:
1.415 - Sequence:
1.416 + Sequence: <-- common name
1.417 {2 5 4 3}
1.418 waldo
1.419 Set:
1.420 - Sequence:
1.421 + Sequence: <-- first name
1.422 {2 5 4 42}
1.423 Waldo
1.424 Set:
1.425 - Sequence:
1.426 + Sequence: <-- description
1.427 {2 5 4 13}
1.428 Just a fictitious person
1.429 Sequence: <-- public key info