Factored out the name accessors of MYParsedCertificate into a new class MYCertificateName, so that both subject and issuer can be accessed. A bit of other cleanup too.
1.1 --- a/MYASN1Object.m Thu Jun 04 18:36:30 2009 -0700
1.2 +++ b/MYASN1Object.m Fri Jun 05 08:57:18 2009 -0700
1.3 @@ -6,6 +6,9 @@
1.4 // Copyright 2009 Jens Alfke. All rights reserved.
1.5 //
1.6
1.7 +// Reference:
1.8 +// <http://www.columbia.edu/~ariel/ssleay/layman.html> "Layman's Guide To ASN.1/BER/DER"
1.9 +
1.10 #import "MYASN1Object.h"
1.11
1.12
2.1 --- a/MYBERParser.m Thu Jun 04 18:36:30 2009 -0700
2.2 +++ b/MYBERParser.m Fri Jun 05 08:57:18 2009 -0700
2.3 @@ -6,6 +6,9 @@
2.4 // Copyright 2009 Jens Alfke. All rights reserved.
2.5 //
2.6
2.7 +// Reference:
2.8 +// <http://www.columbia.edu/~ariel/ssleay/layman.html> "Layman's Guide To ASN.1/BER/DER"
2.9 +
2.10 #import "MYBERParser.h"
2.11 #import "MYASN1Object.h"
2.12 #import "MYOID.h"
2.13 @@ -103,6 +106,7 @@
2.14 }
2.15
2.16 static NSDate* parseDate (NSString *dateStr, unsigned tag) {
2.17 + //FIX: There are more date formats possible; need to try them all. (see "Layman's Guide", 5.17)
2.18 NSDateFormatter *fmt = (tag==23 ?MYBERUTCTimeFormatter() :MYBERGeneralizedTimeFormatter());
2.19 NSDate *date = [fmt dateFromString: dateStr];
2.20 if (!date)
3.1 --- a/MYDEREncoder.m Thu Jun 04 18:36:30 2009 -0700
3.2 +++ b/MYDEREncoder.m Fri Jun 05 08:57:18 2009 -0700
3.3 @@ -6,6 +6,9 @@
3.4 // Copyright 2009 Jens Alfke. All rights reserved.
3.5 //
3.6
3.7 +// Reference:
3.8 +// <http://www.columbia.edu/~ariel/ssleay/layman.html> "Layman's Guide To ASN.1/BER/DER"
3.9 +
3.10 #import "MYDEREncoder.h"
3.11 #import "MYASN1Object.h"
3.12 #import "MYBERParser.h"
3.13 @@ -19,6 +22,10 @@
3.14 @interface MYDEREncoder ()
3.15 - (void) _encode: (id)object;
3.16 @property (retain) NSError *error;
3.17 +
3.18 +/* Forces use of PrintableString tag for ASCII strings that contain characters not valid
3.19 + for that encoding (notably '@'). Provided to get byte-for-byte compatibility with certs
3.20 + generated by CDSA, for test cases that check this. */
3.21 @property BOOL _forcePrintableStrings;
3.22 @end
3.23
4.1 --- a/MYParsedCertificate.h Thu Jun 04 18:36:30 2009 -0700
4.2 +++ b/MYParsedCertificate.h Fri Jun 05 08:57:18 2009 -0700
4.3 @@ -7,16 +7,16 @@
4.4 //
4.5
4.6 #import <Foundation/Foundation.h>
4.7 -@class MYCertificate, MYPublicKey, MYPrivateKey, MYOID;
4.8 +@class MYCertificateName, MYCertificate, MYPublicKey, MYPrivateKey, MYOID;
4.9
4.10 /** A parsed X.509 certificate. Can be used to get more info about an existing cert,
4.11 - or to modify a self-signed cert and regenerate it. */
4.12 + to modify and regenerate a self-signed cert, or to create a new self-signed cert. */
4.13 @interface MYParsedCertificate : NSObject
4.14 {
4.15 @private
4.16 NSData *_data;
4.17 NSArray *_root;
4.18 - MYCertificate *_issuer;
4.19 + MYCertificate *_issuerCertificate;
4.20 }
4.21
4.22 /** Initializes an instance by parsing an existing X.509 certificate's data. */
4.23 @@ -31,30 +31,21 @@
4.24 /** The date/time at which the certificate expires. */
4.25 @property (retain) NSDate *validTo;
4.26
4.27 -/** The "common name" (nickname, whatever) of the subject/owner of the certificate. */
4.28 -@property (copy) NSString *commonName;
4.29 +/** Information about the identity of the owner of this certificate. */
4.30 +@property (readonly) MYCertificateName *subject;
4.31
4.32 -/** The given/first name of the subject/owner of the certificate. */
4.33 -@property (copy) NSString *givenName;
4.34 +/** Information about the identity that signed/authorized this certificate. */
4.35 +@property (readonly) MYCertificateName *issuer;
4.36
4.37 -/** The surname / last name / family name of the subject/owner of the certificate. */
4.38 -@property (copy) NSString *surname;
4.39 -
4.40 -/** A description of the subject/owner of the certificate. */
4.41 -@property (copy) NSString *description;
4.42 -
4.43 -/** The raw email address of the subject of the certificate. */
4.44 -@property (copy) NSString *emailAddress;
4.45 +/** Returns YES if the issuer is the same as the subject. (Aka a "self-signed" certificate.) */
4.46 +@property (readonly) BOOL isRoot;
4.47
4.48 /** The public key of the subject of the certificate. */
4.49 @property (readonly) MYPublicKey *subjectPublicKey;
4.50
4.51 -/** Returns YES if the issuer is the same as the subject. (Aka a "self-signed" certificate.) */
4.52 -@property (readonly) BOOL isRoot;
4.53 -
4.54 /** Associates the certificate to its issuer.
4.55 If the cert is not self-signed, you must manually set this property before validating. */
4.56 -@property (retain) MYCertificate* issuer;
4.57 +@property (retain) MYCertificate* issuerCertificate;
4.58
4.59 /** Checks that the issuer's signature is valid and hasn't been tampered with.
4.60 If the certificate is root/self-signed, the subjectPublicKey is used to check the signature;
4.61 @@ -83,3 +74,37 @@
4.62 - (BOOL) selfSignWithPrivateKey: (MYPrivateKey*)privateKey error: (NSError**)outError;
4.63
4.64 @end
4.65 +
4.66 +
4.67 +
4.68 +/** An X.509 Name structure, describing the subject or issuer of a certificate.
4.69 + Changing a property value of an instance associated with an already-signed certificate will
4.70 + raise an exception. */
4.71 +@interface MYCertificateName : NSObject
4.72 +{
4.73 + @private
4.74 + NSArray *_components;
4.75 +}
4.76 +
4.77 +/** The "common name" (nickname, whatever). */
4.78 +@property (copy) NSString *commonName;
4.79 +
4.80 +/** The given/first name. */
4.81 +@property (copy) NSString *givenName;
4.82 +
4.83 +/** The surname / last name / family name. */
4.84 +@property (copy) NSString *surname;
4.85 +
4.86 +/** A description. */
4.87 +@property (copy) NSString *nameDescription;
4.88 +
4.89 +/** The raw email address. */
4.90 +@property (copy) NSString *emailAddress;
4.91 +
4.92 +/** Lower-level accessor that returns the value associated with the given OID. */
4.93 +- (NSString*) stringForOID: (MYOID*)oid;
4.94 +
4.95 +/** Lower-level accessor that sets the value associated with the given OID. */
4.96 +- (void) setString: (NSString*)value forOID: (MYOID*)oid;
4.97 +
4.98 +@end
5.1 --- a/MYParsedCertificate.m Thu Jun 04 18:36:30 2009 -0700
5.2 +++ b/MYParsedCertificate.m Fri Jun 05 08:57:18 2009 -0700
5.3 @@ -7,9 +7,9 @@
5.4 //
5.5
5.6 // References:
5.7 -// <http://www.columbia.edu/~ariel/ssleay/layman.html>
5.8 -// <http://en.wikipedia.org/wiki/X.509>
5.9 -// <http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt>
5.10 +// <http://www.columbia.edu/~ariel/ssleay/layman.html> "Layman's Guide To ASN.1/BER/DER"
5.11 +// <http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt> "X.509 Style Guide"
5.12 +// <http://en.wikipedia.org/wiki/X.509> Wikipedia article on X.509
5.13
5.14
5.15 #import "MYParsedCertificate.h"
5.16 @@ -31,6 +31,12 @@
5.17 }
5.18
5.19
5.20 +@interface MYCertificateName ()
5.21 +- (id) _initWithComponents: (NSArray*)components;
5.22 +@end
5.23 +
5.24 +
5.25 +#pragma mark -
5.26 @implementation MYParsedCertificate
5.27
5.28
5.29 @@ -101,7 +107,7 @@
5.30 {
5.31
5.32 [_root release];
5.33 - [_issuer release];
5.34 + [_issuerCertificate release];
5.35 [_data release];
5.36 [super dealloc];
5.37 }
5.38 @@ -111,34 +117,19 @@
5.39
5.40 - (NSArray*) _validDates {return $castIf(NSArray, [self._info objectAtIndex: 4]);}
5.41
5.42 -- (NSArray*) _pairForOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
5.43 - NSArray *names = $castIf(NSArray, $atIf(self._info, infoIndex));
5.44 - for (id nameEntry in names) {
5.45 - for (id pair in $castIf(NSSet,nameEntry)) {
5.46 - if ([pair isKindOfClass: [NSArray class]] && [pair count] == 2) {
5.47 - if ($equal(oid, [pair objectAtIndex: 0]))
5.48 - return pair;
5.49 - }
5.50 - }
5.51 - }
5.52 - return nil;
5.53 -}
5.54 -
5.55 -- (NSString*) _stringForOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
5.56 - return [[self _pairForOID: oid atInfoIndex: infoIndex] objectAtIndex: 1];
5.57 -}
5.58 -
5.59 -
5.60 -@synthesize issuer=_issuer, certificateData=_data;
5.61 +@synthesize issuerCertificate=_issuerCertificate, certificateData=_data;
5.62
5.63
5.64 - (NSDate*) validFrom {return $castIf(NSDate, $atIf(self._validDates, 0));}
5.65 - (NSDate*) validTo {return $castIf(NSDate, $atIf(self._validDates, 1));}
5.66 -- (NSString*) commonName {return [self _stringForOID: kCommonNameOID atInfoIndex: 5];}
5.67 -- (NSString*) givenName {return [self _stringForOID: kGivenNameOID atInfoIndex: 5];}
5.68 -- (NSString*) surname {return [self _stringForOID: kSurnameOID atInfoIndex: 5];}
5.69 -- (NSString*) description {return [self _stringForOID: kDescriptionOID atInfoIndex: 5];}
5.70 -- (NSString*) emailAddress {return [self _stringForOID: kEmailOID atInfoIndex: 5];}
5.71 +
5.72 +- (MYCertificateName*) subject {
5.73 + return [[[MYCertificateName alloc] _initWithComponents: [self._info objectAtIndex: 5]] autorelease];
5.74 +}
5.75 +
5.76 +- (MYCertificateName*) issuer {
5.77 + return [[[MYCertificateName alloc] _initWithComponents: [self._info objectAtIndex: 3]] autorelease];
5.78 +}
5.79
5.80 - (BOOL) isSigned {return [_root count] >= 3;}
5.81
5.82 @@ -159,8 +150,8 @@
5.83 }
5.84
5.85 - (MYPublicKey*) issuerPublicKey {
5.86 - if (_issuer)
5.87 - return _issuer.publicKey;
5.88 + if (_issuerCertificate)
5.89 + return _issuerCertificate.publicKey;
5.90 else if (self.isRoot)
5.91 return self.subjectPublicKey;
5.92 else
5.93 @@ -228,17 +219,6 @@
5.94 }
5.95
5.96
5.97 -- (void) _setString: (NSString*)value forOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
5.98 - NSMutableArray *pair = (NSMutableArray*) [self _pairForOID: oid atInfoIndex: infoIndex];
5.99 - if (pair) {
5.100 - [pair replaceObjectAtIndex: 1 withObject: value];
5.101 - } else {
5.102 - NSMutableArray *names = $castIf(NSMutableArray, $atIf(self._info, infoIndex));
5.103 - [names addObject: [NSSet setWithObject: $marray(oid,value)]];
5.104 - }
5.105 -}
5.106 -
5.107 -
5.108 - (void) setValidFrom: (NSDate*)validFrom {
5.109 [(NSMutableArray*)self._validDates replaceObjectAtIndex: 0 withObject: validFrom];
5.110 }
5.111 @@ -247,26 +227,6 @@
5.112 [(NSMutableArray*)self._validDates replaceObjectAtIndex: 1 withObject: validTo];
5.113 }
5.114
5.115 -- (void) setCommonName: (NSString*)commonName {
5.116 - [self _setString: commonName forOID: kCommonNameOID atInfoIndex: 5];
5.117 -}
5.118 -
5.119 -- (void) setGivenName: (NSString*)givenName {
5.120 - [self _setString: givenName forOID: kGivenNameOID atInfoIndex: 5];
5.121 -}
5.122 -
5.123 -- (void) setSurname: (NSString*)surname {
5.124 - [self _setString: surname forOID: kSurnameOID atInfoIndex: 5];
5.125 -}
5.126 -
5.127 -- (void) setDescription: (NSString*)description {
5.128 - [self _setString: description forOID: kDescriptionOID atInfoIndex: 5];
5.129 -}
5.130 -
5.131 -- (void) setEmailAddress: (NSString*)emailAddress {
5.132 - [self _setString: emailAddress forOID: kEmailOID atInfoIndex: 5];
5.133 -}
5.134 -
5.135
5.136 - (BOOL) selfSignWithPrivateKey: (MYPrivateKey*)privateKey error: (NSError**)outError {
5.137 // Copy subject to issuer:
5.138 @@ -304,6 +264,72 @@
5.139
5.140
5.141
5.142 +#pragma mark -
5.143 +@implementation MYCertificateName
5.144 +
5.145 +- (id) _initWithComponents: (NSArray*)components
5.146 +{
5.147 + self = [super init];
5.148 + if (self != nil) {
5.149 + _components = [components retain];
5.150 + }
5.151 + return self;
5.152 +}
5.153 +
5.154 +- (void) dealloc
5.155 +{
5.156 + [_components release];
5.157 + [super dealloc];
5.158 +}
5.159 +
5.160 +- (BOOL) isEqual: (id)object {
5.161 + return [object isKindOfClass: [MYCertificateName class]]
5.162 + && [_components isEqual: ((MYCertificateName*)object)->_components];
5.163 +}
5.164 +
5.165 +- (NSArray*) _pairForOID: (MYOID*)oid {
5.166 + for (id nameEntry in _components) {
5.167 + for (id pair in $castIf(NSSet,nameEntry)) {
5.168 + if ([pair isKindOfClass: [NSArray class]] && [pair count] == 2) {
5.169 + if ($equal(oid, [pair objectAtIndex: 0]))
5.170 + return pair;
5.171 + }
5.172 + }
5.173 + }
5.174 + return nil;
5.175 +}
5.176 +
5.177 +- (NSString*) stringForOID: (MYOID*)oid {
5.178 + return [[self _pairForOID: oid] objectAtIndex: 1];
5.179 +}
5.180 +
5.181 +- (void) setString: (NSString*)value forOID: (MYOID*)oid {
5.182 + NSMutableArray *pair = (NSMutableArray*) [self _pairForOID: oid];
5.183 + if (pair)
5.184 + [pair replaceObjectAtIndex: 1 withObject: value];
5.185 + else
5.186 + [(NSMutableArray*)_components addObject: [NSSet setWithObject: $marray(oid,value)]];
5.187 +}
5.188 +
5.189 +- (NSString*) commonName {return [self stringForOID: kCommonNameOID];}
5.190 +- (NSString*) givenName {return [self stringForOID: kGivenNameOID];}
5.191 +- (NSString*) surname {return [self stringForOID: kSurnameOID];}
5.192 +- (NSString*) nameDescription {return [self stringForOID: kDescriptionOID];}
5.193 +- (NSString*) emailAddress {return [self stringForOID: kEmailOID];}
5.194 +
5.195 +- (void) setCommonName: (NSString*)commonName {[self setString: commonName forOID: kCommonNameOID];}
5.196 +- (void) setGivenName: (NSString*)givenName {[self setString: givenName forOID: kGivenNameOID];}
5.197 +- (void) setSurname: (NSString*)surname {[self setString: surname forOID: kSurnameOID];}
5.198 +- (void) setNameDescription: (NSString*)desc {[self setString: desc forOID: kDescriptionOID];}
5.199 +- (void) setEmailAddress: (NSString*)email {[self setString: email forOID: kEmailOID];}
5.200 +
5.201 +
5.202 +@end
5.203 +
5.204 +
5.205 +
5.206 +#pragma mark -
5.207 +#pragma mark TEST CASES:
5.208
5.209 #if DEBUG
5.210
5.211 @@ -336,11 +362,12 @@
5.212
5.213 CAssert(pcert.validateSignature);
5.214 }
5.215 - Log(@"Common Name = %@", pcert.commonName);
5.216 - Log(@"Given Name = %@", pcert.givenName);
5.217 - Log(@"Surname = %@", pcert.surname);
5.218 - Log(@"Desc = %@", pcert.description);
5.219 - Log(@"Email = %@", pcert.emailAddress);
5.220 + MYCertificateName *subject = pcert.subject;
5.221 + Log(@"Common Name = %@", subject.commonName);
5.222 + Log(@"Given Name = %@", subject.givenName);
5.223 + Log(@"Surname = %@", subject.surname);
5.224 + Log(@"Desc = %@", subject.nameDescription);
5.225 + Log(@"Email = %@", subject.emailAddress);
5.226 return pcert;
5.227 }
5.228
5.229 @@ -356,17 +383,19 @@
5.230 TestCase(CreateCert) {
5.231 MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
5.232 MYParsedCertificate *pcert = [[MYParsedCertificate alloc] initWithPublicKey: privateKey.publicKey];
5.233 - pcert.commonName = @"testcase";
5.234 - pcert.givenName = @"Test";
5.235 - pcert.surname = @"Case";
5.236 - pcert.description = @"Just a test certificate created by MYCrypto";
5.237 - pcert.emailAddress = @"testcase@example.com";
5.238 + MYCertificateName *subject = pcert.subject;
5.239 + subject.commonName = @"testcase";
5.240 + subject.givenName = @"Test";
5.241 + subject.surname = @"Case";
5.242 + subject.nameDescription = @"Just a test certificate created by MYCrypto";
5.243 + subject.emailAddress = @"testcase@example.com";
5.244
5.245 - CAssertEqual(pcert.commonName, @"testcase");
5.246 - CAssertEqual(pcert.givenName, @"Test");
5.247 - CAssertEqual(pcert.surname, @"Case");
5.248 - CAssertEqual(pcert.description, @"Just a test certificate created by MYCrypto");
5.249 - CAssertEqual(pcert.emailAddress, @"testcase@example.com");
5.250 + subject = pcert.subject;
5.251 + CAssertEqual(subject.commonName, @"testcase");
5.252 + CAssertEqual(subject.givenName, @"Test");
5.253 + CAssertEqual(subject.surname, @"Case");
5.254 + CAssertEqual(subject.nameDescription, @"Just a test certificate created by MYCrypto");
5.255 + CAssertEqual(subject.emailAddress, @"testcase@example.com");
5.256
5.257 Log(@"Signing...");
5.258 NSError *error;
5.259 @@ -379,11 +408,13 @@
5.260 MYParsedCertificate *pcert2 = testCert(@"../../Tests/generated.cer", YES);
5.261
5.262 Log(@"Verifying...");
5.263 - CAssertEqual(pcert2.commonName, @"testcase");
5.264 - CAssertEqual(pcert2.givenName, @"Test");
5.265 - CAssertEqual(pcert2.surname, @"Case");
5.266 - CAssertEqual(pcert2.description, @"Just a test certificate created by MYCrypto");
5.267 - CAssertEqual(pcert2.emailAddress, @"testcase@example.com");
5.268 + MYCertificateName *subject2 = pcert2.subject;
5.269 + CAssertEqual(subject2,subject);
5.270 + CAssertEqual(subject2.commonName, @"testcase");
5.271 + CAssertEqual(subject2.givenName, @"Test");
5.272 + CAssertEqual(subject2.surname, @"Case");
5.273 + CAssertEqual(subject2.nameDescription, @"Just a test certificate created by MYCrypto");
5.274 + CAssertEqual(subject2.emailAddress, @"testcase@example.com");
5.275 }
5.276
5.277 #endif