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.
authorJens Alfke <jens@mooseyard.com>
Fri Jun 05 08:57:18 2009 -0700 (2009-06-05)
changeset 20df9da0f6b358
parent 19 f6c91b9da05b
child 21 2c300b15b381
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.
MYASN1Object.m
MYBERParser.m
MYDEREncoder.m
MYParsedCertificate.h
MYParsedCertificate.m
     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