Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
authorJens Alfke <jens@mooseyard.com>
Thu Jun 04 18:36:30 2009 -0700 (2009-06-04)
changeset 19f6c91b9da05b
parent 18 a06e44b9b898
child 20 df9da0f6b358
Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
MYASN1Object.h
MYASN1Object.m
MYBERParser.h
MYBERParser.m
MYCrypto-iPhone.xcodeproj/project.pbxproj
MYCrypto.xcodeproj/project.pbxproj
MYDEREncoder.h
MYDEREncoder.m
MYOID.h
MYParsedCertificate.h
MYParsedCertificate.m
     1.1 --- a/MYASN1Object.h	Wed Jun 03 17:22:42 2009 -0700
     1.2 +++ b/MYASN1Object.h	Thu Jun 04 18:36:30 2009 -0700
     1.3 @@ -9,8 +9,9 @@
     1.4  #import <Foundation/Foundation.h>
     1.5  
     1.6  
     1.7 -/** A generic ASN.1 data value. The BER parser instantiates these to represent parsed values that
     1.8 -    it doesn't know how to represent otherwise. */
     1.9 +/* A generic ASN.1 data value. The BER parser instantiates these to represent parsed values that
    1.10 +    it doesn't know how to represent otherwise.
    1.11 +    This is mostly used internally by MYParsedCertificate. */
    1.12  @interface MYASN1Object : NSObject
    1.13  {
    1.14      @private
    1.15 @@ -40,22 +41,26 @@
    1.16  @end
    1.17  
    1.18  
    1.19 -/** An ASN.1 "big" (arbitrary-length) integer.
    1.20 -    The value contains the bytes of the integer, in big-endian order. */
    1.21 +/* An ASN.1 "big" (arbitrary-length) integer.
    1.22 +    The value contains the bytes of the integer, in big-endian order.
    1.23 +    This is mostly used internally by MYParsedCertificate. */
    1.24  @interface MYASN1BigInteger : MYASN1Object
    1.25  @end
    1.26  
    1.27  
    1.28 -/** An ordered string of bits, as used in ASN.1.
    1.29 +/* An ordered string of bits, as used in ASN.1.
    1.30      This differs from NSData in that it need not occupy a whole number of bytes;
    1.31 -    that is, the number of bits need not be a multiple of 8. */
    1.32 +    that is, the number of bits need not be a multiple of 8.
    1.33 +    This is mostly used internally by MYParsedCertificate. */
    1.34  @interface MYBitString : NSObject 
    1.35  {
    1.36 +    @private
    1.37      NSData *_bits;
    1.38      NSUInteger _bitCount;
    1.39  }
    1.40  
    1.41  - (id)initWithBits: (NSData*)bits count: (NSUInteger)bitCount;
    1.42 ++ (MYBitString*) bitStringWithData: (NSData*)bits;
    1.43  
    1.44  @property (readonly, nonatomic) NSData *bits;
    1.45  @property (readonly, nonatomic) NSUInteger bitCount;
     2.1 --- a/MYASN1Object.m	Wed Jun 03 17:22:42 2009 -0700
     2.2 +++ b/MYASN1Object.m	Thu Jun 04 18:36:30 2009 -0700
     2.3 @@ -61,6 +61,15 @@
     2.4          return $sprintf(@"%@[%hhu/%u/%u, %u bytes]", self.class, _tagClass,(unsigned)_constructed,_tag, _value.length);
     2.5  }
     2.6  
     2.7 +- (BOOL) isEqual: (id)object {
     2.8 +    return [object isKindOfClass: [MYASN1Object class]] 
     2.9 +        && _tag==[object tag] 
    2.10 +        && _tagClass==[object tagClass] 
    2.11 +        && _constructed==[object constructed] 
    2.12 +        && $equal(_value,[object value])
    2.13 +        && $equal(_components,[object components]);
    2.14 +}
    2.15 +
    2.16  static void dump(id object, NSMutableString *output, NSString *indent) {
    2.17      if ([object isKindOfClass: [MYASN1Object class]]) {
    2.18          MYASN1Object *asn1Obj = object;
    2.19 @@ -108,8 +117,7 @@
    2.20  @implementation MYBitString
    2.21  
    2.22  
    2.23 -- (id)initWithBits: (NSData*)bits count: (unsigned)bitCount;
    2.24 -{
    2.25 +- (id)initWithBits: (NSData*)bits count: (unsigned)bitCount {
    2.26      Assert(bits);
    2.27      Assert(bitCount <= 8*bits.length);
    2.28      self = [super init];
    2.29 @@ -120,6 +128,10 @@
    2.30      return self;
    2.31  }
    2.32  
    2.33 ++ (MYBitString*) bitStringWithData: (NSData*)bits {
    2.34 +    return [[[self alloc] initWithBits: bits count: 8*bits.length] autorelease];
    2.35 +}
    2.36 +
    2.37  - (void) dealloc
    2.38  {
    2.39      [_bits release];
    2.40 @@ -132,4 +144,14 @@
    2.41      return $sprintf(@"%@%@", [self class], _bits);
    2.42  }
    2.43  
    2.44 +- (unsigned) hash {
    2.45 +    return _bits.hash ^ _bitCount;
    2.46 +}
    2.47 +
    2.48 +- (BOOL) isEqual: (id)object {
    2.49 +    return [object isKindOfClass: [MYBitString class]] 
    2.50 +        && _bitCount==[object bitCount] 
    2.51 +        && [_bits isEqual: [object bits]];
    2.52 +}
    2.53 +
    2.54  @end
     3.1 --- a/MYBERParser.h	Wed Jun 03 17:22:42 2009 -0700
     3.2 +++ b/MYBERParser.h	Thu Jun 04 18:36:30 2009 -0700
     3.3 @@ -6,13 +6,14 @@
     3.4  //  Copyright 2009 Jens Alfke. All rights reserved.
     3.5  //
     3.6  
     3.7 -#import <Cocoa/Cocoa.h>
     3.8 +#import <Foundation/Foundation.h>
     3.9  
    3.10  
    3.11  #define MYASN1ErrorDomain @"MYASN1ErrorDomain"
    3.12  
    3.13  
    3.14 -/** Parses a block of BER-formatted data into an object tree. */
    3.15 +/** Parses a block of BER-formatted data into an object tree.
    3.16 +    This is mostly used internally by MYParsedCertificate. */
    3.17  id MYBERParse (NSData *ber, NSError **outError);
    3.18  
    3.19  size_t MYBERGetLength (NSData *ber, NSError **outError);
     4.1 --- a/MYBERParser.m	Wed Jun 03 17:22:42 2009 -0700
     4.2 +++ b/MYBERParser.m	Thu Jun 04 18:36:30 2009 -0700
     4.3 @@ -194,7 +194,15 @@
     4.4                  return readStringOrDie(input,length,NSUTF8StringEncoding);
     4.5              case 18: // numeric string
     4.6              case 19: // printable string:
     4.7 -                return readStringOrDie(input,length,NSASCIIStringEncoding);
     4.8 +            case 22: // IA5 string:
     4.9 +            case 20: // T61 string:
    4.10 +            {
    4.11 +                NSString *string = readStringOrDie(input,length,NSASCIIStringEncoding);
    4.12 +                if (string)
    4.13 +                    return string;
    4.14 +                else
    4.15 +                    break;  // if decoding fails, fall back to generic MYASN1Object
    4.16 +            }
    4.17              case 23: // UTC time:
    4.18              case 24: // Generalized time:
    4.19                  return parseDate(readStringOrDie(input,length,NSASCIIStringEncoding), header.tag);
    4.20 @@ -328,9 +336,7 @@
    4.21      
    4.22      MYCertificate *myCert = [[MYCertificate alloc] initWithCertificateData: cert];
    4.23      CAssert(myCert);
    4.24 -    const CSSM_KEY *pubKey = myCert.publicKey.cssmKey;
    4.25 -    CSSM_DATA pubKeyData = pubKey->KeyData;
    4.26 -    id parsedPubKey = MYBERParse([NSData dataWithBytes: pubKeyData.Data length: pubKeyData.Length],NULL);
    4.27 +    id parsedPubKey = MYBERParse(myCert.publicKey.keyData, NULL);
    4.28      Log(@"Parsed public key:\n%@", [MYASN1Object dump: parsedPubKey]);
    4.29  
    4.30      cert = [NSData dataWithContentsOfFile: @"../../Tests/iphonedev.cer"];
     5.1 --- a/MYCrypto-iPhone.xcodeproj/project.pbxproj	Wed Jun 03 17:22:42 2009 -0700
     5.2 +++ b/MYCrypto-iPhone.xcodeproj/project.pbxproj	Thu Jun 04 18:36:30 2009 -0700
     5.3 @@ -16,6 +16,11 @@
     5.4  		273392120F8283B8009414D9 /* MYKeychain-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 273392110F8283B8009414D9 /* MYKeychain-iPhone.m */; };
     5.5  		274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */; };
     5.6  		2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 2748607E0F8D5E0600FE617B /* MYPrivateKey.m */; };
     5.7 +		275D9FEE0FD8795300D85A86 /* MYParsedCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FE40FD8795300D85A86 /* MYParsedCertificate.m */; };
     5.8 +		275D9FEF0FD8795300D85A86 /* MYASN1Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FE60FD8795300D85A86 /* MYASN1Object.m */; };
     5.9 +		275D9FF00FD8795300D85A86 /* MYBERParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FE80FD8795300D85A86 /* MYBERParser.m */; };
    5.10 +		275D9FF10FD8795300D85A86 /* MYDEREncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FEA0FD8795300D85A86 /* MYDEREncoder.m */; };
    5.11 +		275D9FF20FD8795300D85A86 /* MYOID.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FEC0FD8795300D85A86 /* MYOID.m */; };
    5.12  		276FB13F0F84090900CB326E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 276FB13E0F84090900CB326E /* Security.framework */; };
    5.13  		276FB16E0F84152B00CB326E /* MYCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB16D0F84152B00CB326E /* MYCryptoTest.m */; };
    5.14  		276FB3190F856AA700CB326E /* MYPublicKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB3180F856AA700CB326E /* MYPublicKey.m */; };
    5.15 @@ -23,7 +28,6 @@
    5.16  		276FB34B0F856CA400CB326E /* MYCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB34A0F856CA400CB326E /* MYCertificate.m */; };
    5.17  		27A430120F87C6C10063D362 /* MYKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E823110F81D56E0019BE60 /* MYKey.m */; };
    5.18  		27A430140F87C6D50063D362 /* MYSymmetricKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A430130F87C6D50063D362 /* MYSymmetricKey.m */; };
    5.19 -		27B8522D0FCEE6F9005631F9 /* MYASN1.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B8522C0FCEE6F9005631F9 /* MYASN1.m */; };
    5.20  		27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */; };
    5.21  		27E823240F81D56E0019BE60 /* MYKeychainItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E823150F81D56E0019BE60 /* MYKeychainItem.m */; };
    5.22  		27E823270F81D56E0019BE60 /* MYDigest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E8231C0F81D56E0019BE60 /* MYDigest.m */; };
    5.23 @@ -53,6 +57,16 @@
    5.24  		274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYSymmetricKey-iPhone.m"; sourceTree = "<group>"; };
    5.25  		2748607D0F8D5DF200FE617B /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = "<group>"; };
    5.26  		2748607E0F8D5E0600FE617B /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = "<group>"; };
    5.27 +		275D9FE30FD8795300D85A86 /* MYParsedCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYParsedCertificate.h; sourceTree = "<group>"; };
    5.28 +		275D9FE40FD8795300D85A86 /* MYParsedCertificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYParsedCertificate.m; sourceTree = "<group>"; };
    5.29 +		275D9FE50FD8795300D85A86 /* MYASN1Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYASN1Object.h; sourceTree = "<group>"; };
    5.30 +		275D9FE60FD8795300D85A86 /* MYASN1Object.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYASN1Object.m; sourceTree = "<group>"; };
    5.31 +		275D9FE70FD8795300D85A86 /* MYBERParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYBERParser.h; sourceTree = "<group>"; };
    5.32 +		275D9FE80FD8795300D85A86 /* MYBERParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYBERParser.m; sourceTree = "<group>"; };
    5.33 +		275D9FE90FD8795300D85A86 /* MYDEREncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYDEREncoder.h; sourceTree = "<group>"; };
    5.34 +		275D9FEA0FD8795300D85A86 /* MYDEREncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYDEREncoder.m; sourceTree = "<group>"; };
    5.35 +		275D9FEB0FD8795300D85A86 /* MYOID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYOID.h; sourceTree = "<group>"; };
    5.36 +		275D9FEC0FD8795300D85A86 /* MYOID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYOID.m; sourceTree = "<group>"; };
    5.37  		276FB13E0F84090900CB326E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
    5.38  		276FB16D0F84152B00CB326E /* MYCryptoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptoTest.m; sourceTree = "<group>"; };
    5.39  		276FB3180F856AA700CB326E /* MYPublicKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPublicKey.m; sourceTree = "<group>"; };
    5.40 @@ -61,8 +75,6 @@
    5.41  		27A430130F87C6D50063D362 /* MYSymmetricKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYSymmetricKey.m; sourceTree = "<group>"; };
    5.42  		27A430150F87C6DB0063D362 /* MYSymmetricKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYSymmetricKey.h; sourceTree = "<group>"; };
    5.43  		27AAD9710F8927DB0064DD7C /* MYCryptoConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptoConfig.h; sourceTree = "<group>"; };
    5.44 -		27B8522B0FCEE6F9005631F9 /* MYASN1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYASN1.h; sourceTree = "<group>"; };
    5.45 -		27B8522C0FCEE6F9005631F9 /* MYASN1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYASN1.m; sourceTree = "<group>"; };
    5.46  		27E3A6A60F91B8B30079D4D9 /* MYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptor.h; sourceTree = "<group>"; };
    5.47  		27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptor.m; sourceTree = "<group>"; };
    5.48  		27E8230C0F81D56E0019BE60 /* MYCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCertificate.h; sourceTree = "<group>"; };
    5.49 @@ -130,6 +142,23 @@
    5.50  			name = Products;
    5.51  			sourceTree = "<group>";
    5.52  		};
    5.53 +		275D9FE00FD8795300D85A86 /* Certificates */ = {
    5.54 +			isa = PBXGroup;
    5.55 +			children = (
    5.56 +				275D9FE30FD8795300D85A86 /* MYParsedCertificate.h */,
    5.57 +				275D9FE40FD8795300D85A86 /* MYParsedCertificate.m */,
    5.58 +				275D9FE50FD8795300D85A86 /* MYASN1Object.h */,
    5.59 +				275D9FE60FD8795300D85A86 /* MYASN1Object.m */,
    5.60 +				275D9FE70FD8795300D85A86 /* MYBERParser.h */,
    5.61 +				275D9FE80FD8795300D85A86 /* MYBERParser.m */,
    5.62 +				275D9FE90FD8795300D85A86 /* MYDEREncoder.h */,
    5.63 +				275D9FEA0FD8795300D85A86 /* MYDEREncoder.m */,
    5.64 +				275D9FEB0FD8795300D85A86 /* MYOID.h */,
    5.65 +				275D9FEC0FD8795300D85A86 /* MYOID.m */,
    5.66 +			);
    5.67 +			name = Certificates;
    5.68 +			sourceTree = "<group>";
    5.69 +		};
    5.70  		27E3A6A10F91B8B30079D4D9 /* Encryption */ = {
    5.71  			isa = PBXGroup;
    5.72  			children = (
    5.73 @@ -142,8 +171,6 @@
    5.74  		27E8230B0F81D56E0019BE60 /* Source */ = {
    5.75  			isa = PBXGroup;
    5.76  			children = (
    5.77 -				27B8522B0FCEE6F9005631F9 /* MYASN1.h */,
    5.78 -				27B8522C0FCEE6F9005631F9 /* MYASN1.m */,
    5.79  				27AAD9710F8927DB0064DD7C /* MYCryptoConfig.h */,
    5.80  				27E8230C0F81D56E0019BE60 /* MYCertificate.h */,
    5.81  				276FB34A0F856CA400CB326E /* MYCertificate.m */,
    5.82 @@ -198,6 +225,7 @@
    5.83  			children = (
    5.84  				27E8230B0F81D56E0019BE60 /* Source */,
    5.85  				27E3A6A10F91B8B30079D4D9 /* Encryption */,
    5.86 +				275D9FE00FD8795300D85A86 /* Certificates */,
    5.87  				27E8232B0F81D5760019BE60 /* MYUtilities */,
    5.88  				080E96DDFE201D6D7F000001 /* iPhone */,
    5.89  				29B97323FDCFA39411CA2CEA /* Frameworks */,
    5.90 @@ -295,7 +323,11 @@
    5.91  				27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */,
    5.92  				27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */,
    5.93  				274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */,
    5.94 -				27B8522D0FCEE6F9005631F9 /* MYASN1.m in Sources */,
    5.95 +				275D9FEE0FD8795300D85A86 /* MYParsedCertificate.m in Sources */,
    5.96 +				275D9FEF0FD8795300D85A86 /* MYASN1Object.m in Sources */,
    5.97 +				275D9FF00FD8795300D85A86 /* MYBERParser.m in Sources */,
    5.98 +				275D9FF10FD8795300D85A86 /* MYDEREncoder.m in Sources */,
    5.99 +				275D9FF20FD8795300D85A86 /* MYOID.m in Sources */,
   5.100  			);
   5.101  			runOnlyForDeploymentPostprocessing = 0;
   5.102  		};
     6.1 --- a/MYCrypto.xcodeproj/project.pbxproj	Wed Jun 03 17:22:42 2009 -0700
     6.2 +++ b/MYCrypto.xcodeproj/project.pbxproj	Thu Jun 04 18:36:30 2009 -0700
     6.3 @@ -278,10 +278,10 @@
     6.4  		274861440F8D757600FE617B /* Certificates */ = {
     6.5  			isa = PBXGroup;
     6.6  			children = (
     6.7 +				27A42ECC0F8689D30063D362 /* MYCertGen.h */,
     6.8 +				27A42ECD0F8689D30063D362 /* MYCertGen.m */,
     6.9  				275D9D900FD5FB4100D85A86 /* MYParsedCertificate.h */,
    6.10  				275D9D910FD5FB4100D85A86 /* MYParsedCertificate.m */,
    6.11 -				27A42ECC0F8689D30063D362 /* MYCertGen.h */,
    6.12 -				27A42ECD0F8689D30063D362 /* MYCertGen.m */,
    6.13  				27B852F30FCF4EB6005631F9 /* MYASN1Object.h */,
    6.14  				27B852F40FCF4EB7005631F9 /* MYASN1Object.m */,
    6.15  				270A7A710FD58FF200770C4D /* MYBERParser.h */,
     7.1 --- a/MYDEREncoder.h	Wed Jun 03 17:22:42 2009 -0700
     7.2 +++ b/MYDEREncoder.h	Thu Jun 04 18:36:30 2009 -0700
     7.3 @@ -11,9 +11,11 @@
     7.4  
     7.5  @interface MYDEREncoder : NSObject
     7.6  {
     7.7 +    @private
     7.8      id _rootObject;
     7.9      NSMutableData *_output;
    7.10      NSError *_error;
    7.11 +    BOOL _forcePrintableStrings;
    7.12  }
    7.13  
    7.14  - (id) initWithRootObject: (id)object;
     8.1 --- a/MYDEREncoder.m	Wed Jun 03 17:22:42 2009 -0700
     8.2 +++ b/MYDEREncoder.m	Thu Jun 04 18:36:30 2009 -0700
     8.3 @@ -19,6 +19,7 @@
     8.4  @interface MYDEREncoder ()
     8.5  - (void) _encode: (id)object;
     8.6  @property (retain) NSError *error;
     8.7 +@property BOOL _forcePrintableStrings;
     8.8  @end
     8.9  
    8.10  
    8.11 @@ -46,9 +47,15 @@
    8.12  {
    8.13      [_rootObject release];
    8.14      [_output release];
    8.15 +    [_error release];
    8.16      [super dealloc];
    8.17  }
    8.18  
    8.19 +- (id) copyWithZone: (NSZone*)zone {
    8.20 +    MYDEREncoder *copy = [[[self class] alloc] init];
    8.21 +    copy->_forcePrintableStrings = _forcePrintableStrings;
    8.22 +    return copy;
    8.23 +}
    8.24  
    8.25  
    8.26  static unsigned sizeOfUnsignedInt (UInt64 n) {
    8.27 @@ -185,11 +192,22 @@
    8.28  
    8.29  
    8.30  - (void) _encodeString: (NSString*)string {
    8.31 +    static NSMutableCharacterSet *kNotPrintableCharSet;
    8.32 +    if (!kNotPrintableCharSet) {
    8.33 +        kNotPrintableCharSet = [[NSMutableCharacterSet characterSetWithCharactersInString: @" '()+,-./:=?"] retain];
    8.34 +        [kNotPrintableCharSet formUnionWithCharacterSet: [NSCharacterSet alphanumericCharacterSet]];
    8.35 +        [kNotPrintableCharSet invert];
    8.36 +    }
    8.37      NSData *data = [string dataUsingEncoding: NSASCIIStringEncoding];
    8.38 -    if (data)
    8.39 -        [self _writeTag: 19 class: 0 constructed: NO data: data];
    8.40 -    else
    8.41 +    if (data) {
    8.42 +        unsigned tag = 19; // printablestring (a silly arbitrary subset of ASCII defined by ASN.1)
    8.43 +        if (!_forcePrintableStrings && [string rangeOfCharacterFromSet: kNotPrintableCharSet].length > 0)
    8.44 +            tag = 20; // IA5string (full 7-bit ASCII)
    8.45 +        [self _writeTag: tag class: 0 constructed: NO data: data];
    8.46 +    } else {
    8.47 +        // fall back to UTF-8:
    8.48          [self _writeTag: 12 class: 0 constructed: NO data: [string dataUsingEncoding: NSUTF8StringEncoding]];
    8.49 +    }
    8.50  }
    8.51  
    8.52  
    8.53 @@ -209,7 +227,7 @@
    8.54  
    8.55  
    8.56  - (void) _encodeCollection: (id)collection tag: (unsigned)tag class: (unsigned)tagClass {
    8.57 -    MYDEREncoder *subEncoder = [[[self class] alloc] init];
    8.58 +    MYDEREncoder *subEncoder = [self copy];
    8.59      for (id object in collection)
    8.60          [subEncoder _encode: object];
    8.61      [self _writeTag: tag class: tagClass constructed: YES data: subEncoder.output];
    8.62 @@ -268,7 +286,7 @@
    8.63      return _output;
    8.64  }
    8.65  
    8.66 -@synthesize error=_error;
    8.67 +@synthesize error=_error, _forcePrintableStrings;
    8.68  
    8.69  
    8.70  @end
    8.71 @@ -342,11 +360,13 @@
    8.72      id certObjects = MYBERParse(cert,&error);
    8.73      CAssertNil(error);
    8.74      Log(@"Decoded as:\n%@", [MYASN1Object dump: certObjects]);
    8.75 -    NSData *encoded = [MYDEREncoder encodeRootObject: certObjects error: &error];
    8.76 +    MYDEREncoder *encoder = [[MYDEREncoder alloc] initWithRootObject: certObjects];
    8.77 +    encoder._forcePrintableStrings = YES;       // hack for compatibility with the way CDSA writes ASN.1
    8.78 +    NSData *encoded = encoder.output;
    8.79      CAssertNil(error);
    8.80      id reDecoded = MYBERParse(encoded, &error);
    8.81      CAssertNil(error);
    8.82      Log(@"Re-decoded as:\n%@", [MYASN1Object dump: reDecoded]);
    8.83 -    [encoded writeToFile: @"../../Tests/iphonedev_reencoded.cer" atomically: YES];
    8.84 +    [encoded writeToFile: @"../../Tests/selfsigned_reencoded.cer" atomically: YES];
    8.85      CAssertEqual(encoded,cert);
    8.86  }
     9.1 --- a/MYOID.h	Wed Jun 03 17:22:42 2009 -0700
     9.2 +++ b/MYOID.h	Thu Jun 04 18:36:30 2009 -0700
     9.3 @@ -6,10 +6,11 @@
     9.4  //  Copyright 2009 Jens Alfke. All rights reserved.
     9.5  //
     9.6  
     9.7 -#import <Cocoa/Cocoa.h>
     9.8 +#import <Foundation/Foundation.h>
     9.9  
    9.10  
    9.11 -/** An ASN.1 Object-ID, which is a sequence of integer components that define namespaces. */
    9.12 +/* An ASN.1 Object-ID, which is a sequence of integer components that define namespaces.
    9.13 +    This is mostly used internally by MYParsedCertificate. */
    9.14  @interface MYOID : NSObject <NSCopying>
    9.15  {
    9.16      NSData *_data;
    10.1 --- a/MYParsedCertificate.h	Wed Jun 03 17:22:42 2009 -0700
    10.2 +++ b/MYParsedCertificate.h	Thu Jun 04 18:36:30 2009 -0700
    10.3 @@ -7,23 +7,79 @@
    10.4  //
    10.5  
    10.6  #import <Foundation/Foundation.h>
    10.7 -@class MYCertificate, MYOID;
    10.8 +@class MYCertificate, MYPublicKey, MYPrivateKey, MYOID;
    10.9  
   10.10  /** A parsed X.509 certificate. Can be used to get more info about an existing cert,
   10.11      or to modify a self-signed cert and regenerate it. */
   10.12  @interface MYParsedCertificate : NSObject 
   10.13  {
   10.14 +    @private
   10.15      NSData *_data;
   10.16 -    id _root;
   10.17 +    NSArray *_root;
   10.18      MYCertificate *_issuer;
   10.19  }
   10.20  
   10.21 -+ (MYOID*) RSAWithSHA1AlgorithmID;
   10.22 -
   10.23 +/** Initializes an instance by parsing an existing X.509 certificate's data. */
   10.24  - (id) initWithCertificateData: (NSData*)data error: (NSError**)outError;
   10.25  
   10.26 +/** The raw data of the certificate. */
   10.27 +@property (readonly) NSData* certificateData;
   10.28 +
   10.29 +/** The date/time at which the certificate first becomes valid. */
   10.30 +@property (retain) NSDate *validFrom;
   10.31 +
   10.32 +/** The date/time at which the certificate expires. */
   10.33 +@property (retain) NSDate *validTo;
   10.34 +
   10.35 +/** The "common name" (nickname, whatever) of the subject/owner of the certificate. */
   10.36 +@property (copy) NSString *commonName;
   10.37 +
   10.38 +/** The given/first name of the subject/owner of the certificate. */
   10.39 +@property (copy) NSString *givenName;
   10.40 +
   10.41 +/** The surname / last name / family name of the subject/owner of the certificate. */
   10.42 +@property (copy) NSString *surname;
   10.43 +
   10.44 +/** A description of the subject/owner of the certificate. */
   10.45 +@property (copy) NSString *description;
   10.46 +
   10.47 +/** The raw email address of the subject of the certificate. */
   10.48 +@property (copy) NSString *emailAddress;
   10.49 +
   10.50 +/** The public key of the subject of the certificate. */
   10.51 +@property (readonly) MYPublicKey *subjectPublicKey;
   10.52 +
   10.53 +/** Returns YES if the issuer is the same as the subject. (Aka a "self-signed" certificate.) */
   10.54 +@property (readonly) BOOL isRoot;
   10.55 +
   10.56  /** Associates the certificate to its issuer.
   10.57 -    If the cert is not self-signed, you must manually set this property before verifying. */
   10.58 +    If the cert is not self-signed, you must manually set this property before validating. */
   10.59  @property (retain) MYCertificate* issuer;
   10.60  
   10.61 +/** Checks that the issuer's signature is valid and hasn't been tampered with.
   10.62 +    If the certificate is root/self-signed, the subjectPublicKey is used to check the signature;
   10.63 +    otherwise, the issuer property needs to have been set and its publicKey will be used. */
   10.64 +- (BOOL) validateSignature;
   10.65 +
   10.66 +
   10.67 +// Generating certificates:
   10.68 +
   10.69 +/** Initializes a blank instance which can be used to create a new certificate.
   10.70 +    The certificate will not contain anything yet other than the public key.
   10.71 +    The desired attributes should be set, and then the -selfSignWithPrivateKey:error method called. */
   10.72 +- (id) initWithPublicKey: (MYPublicKey*)pubKey;
   10.73 +
   10.74 +/** Has the certificate been signed yet? */
   10.75 +@property (readonly) BOOL isSigned;
   10.76 +
   10.77 +/** Signs the certificate using the given private key, which must be the counterpart of the
   10.78 +    public key stored in the certificate.
   10.79 +    The subject attributes will be copied to the issuer attributes.
   10.80 +    If no valid date range has been set yet, it will be set to a range of one year starting from
   10.81 +    the current time.
   10.82 +    A unique serial number based on the current time will be set.
   10.83 +    After this method returns successfully, access the certificateData property to get the
   10.84 +    encoded certificate. */
   10.85 +- (BOOL) selfSignWithPrivateKey: (MYPrivateKey*)privateKey error: (NSError**)outError;
   10.86 +
   10.87  @end
    11.1 --- a/MYParsedCertificate.m	Wed Jun 03 17:22:42 2009 -0700
    11.2 +++ b/MYParsedCertificate.m	Thu Jun 04 18:36:30 2009 -0700
    11.3 @@ -7,6 +7,7 @@
    11.4  //
    11.5  
    11.6  // References:
    11.7 +// <http://www.columbia.edu/~ariel/ssleay/layman.html>
    11.8  // <http://en.wikipedia.org/wiki/X.509>
    11.9  // <http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt>
   11.10  
   11.11 @@ -17,10 +18,14 @@
   11.12  #import "MYBERParser.h"
   11.13  #import "MYDEREncoder.h"
   11.14  #import "MYPublicKey.h"
   11.15 +#import "MYPrivateKey.h"
   11.16  #import "MYCertificate.h"
   11.17  #import "MYErrorUtils.h"
   11.18  
   11.19  
   11.20 +#define kDefaultExpirationTime (60.0 * 60.0 * 24.0 * 365.0)
   11.21 +
   11.22 +
   11.23  static id $atIf(NSArray *array, NSUInteger index) {
   11.24      return index < array.count ?[array objectAtIndex: index] :nil;
   11.25  }
   11.26 @@ -29,24 +34,30 @@
   11.27  @implementation MYParsedCertificate
   11.28  
   11.29  
   11.30 -static MYOID *kRSAAlgorithmID, *kRSAWithSHA1AlgorithmID;
   11.31 +static MYOID *kRSAAlgorithmID, *kRSAWithSHA1AlgorithmID, *kCommonNameOID,
   11.32 +            *kGivenNameOID, *kSurnameOID, *kDescriptionOID, *kEmailOID;
   11.33  
   11.34  
   11.35  + (void) initialize {
   11.36 -    if (!kRSAAlgorithmID) {
   11.37 -        UInt32 components[7] = {1, 2, 840, 113549, 1, 1, 1,};
   11.38 -        kRSAAlgorithmID = [[MYOID alloc] initWithComponents: components count: 7];
   11.39 +    if (!kEmailOID) {
   11.40 +        kRSAAlgorithmID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 1, 1,}
   11.41 +                                                      count: 7];
   11.42 +        kRSAWithSHA1AlgorithmID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 1, 5}
   11.43 +                                                              count: 7];
   11.44 +        kCommonNameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 3}
   11.45 +                                                     count: 4];
   11.46 +        kGivenNameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 42}
   11.47 +                                                    count: 4];
   11.48 +        kSurnameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 4}
   11.49 +                                                  count: 4];
   11.50 +        kDescriptionOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 13}
   11.51 +                                                count: 7];
   11.52 +        kEmailOID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 9, 1}
   11.53 +                                                count: 7];
   11.54      }
   11.55 -    if (!kRSAWithSHA1AlgorithmID) {
   11.56 -        UInt32 components[7] = {1, 2, 840, 113549, 1, 1, 5};
   11.57 -        kRSAWithSHA1AlgorithmID = [[MYOID alloc] initWithComponents: components count: 7];
   11.58 -    }
   11.59 +    
   11.60  }
   11.61  
   11.62 -+ (MYOID*) RSAAlgorithmID           {return kRSAAlgorithmID;}
   11.63 -+ (MYOID*) RSAWithSHA1AlgorithmID   {return kRSAWithSHA1AlgorithmID;}
   11.64 -
   11.65 -
   11.66  + (NSString*) validate: (id)root {
   11.67      NSArray *top = $castIf(NSArray,root);
   11.68      if (top.count < 3)
   11.69 @@ -91,48 +102,71 @@
   11.70      
   11.71      [_root release];
   11.72      [_issuer release];
   11.73 +    [_data release];
   11.74      [super dealloc];
   11.75  }
   11.76  
   11.77  
   11.78 -@synthesize issuer=_issuer;
   11.79 +- (NSArray*) _info       {return $castIf(NSArray,$atIf(_root,0));}
   11.80  
   11.81 +- (NSArray*) _validDates {return $castIf(NSArray, [self._info objectAtIndex: 4]);}
   11.82  
   11.83 -- (NSArray*) info    {return $castIf(NSArray,$atIf(_root,0));}
   11.84 -
   11.85 -- (BOOL) isSelfSigned {
   11.86 -    id issuer  = $atIf(self.info,3);
   11.87 -    id subject = $atIf(self.info,5);
   11.88 -    return $equal(issuer,subject);
   11.89 +- (NSArray*) _pairForOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
   11.90 +    NSArray *names = $castIf(NSArray, $atIf(self._info, infoIndex));
   11.91 +    for (id nameEntry in names) {
   11.92 +        for (id pair in $castIf(NSSet,nameEntry)) {
   11.93 +            if ([pair isKindOfClass: [NSArray class]] && [pair count] == 2) {
   11.94 +                if ($equal(oid, [pair objectAtIndex: 0]))
   11.95 +                    return pair;
   11.96 +            }
   11.97 +        }
   11.98 +    }
   11.99 +    return nil;
  11.100  }
  11.101  
  11.102 +- (NSString*) _stringForOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
  11.103 +    return [[self _pairForOID: oid atInfoIndex: infoIndex] objectAtIndex: 1];
  11.104 +}
  11.105 +
  11.106 +
  11.107 +@synthesize issuer=_issuer, certificateData=_data;
  11.108 +
  11.109 +
  11.110 +- (NSDate*) validFrom       {return $castIf(NSDate, $atIf(self._validDates, 0));}
  11.111 +- (NSDate*) validTo         {return $castIf(NSDate, $atIf(self._validDates, 1));}
  11.112 +- (NSString*) commonName    {return [self _stringForOID: kCommonNameOID atInfoIndex: 5];}
  11.113 +- (NSString*) givenName     {return [self _stringForOID: kGivenNameOID atInfoIndex: 5];}
  11.114 +- (NSString*) surname       {return [self _stringForOID: kSurnameOID atInfoIndex: 5];}
  11.115 +- (NSString*) description   {return [self _stringForOID: kDescriptionOID atInfoIndex: 5];}
  11.116 +- (NSString*) emailAddress  {return [self _stringForOID: kEmailOID atInfoIndex: 5];}
  11.117 +
  11.118 +- (BOOL) isSigned           {return [_root count] >= 3;}
  11.119 +
  11.120 +- (BOOL) isRoot {
  11.121 +    id issuer = $atIf(self._info,3);
  11.122 +    return $equal(issuer, $atIf(self._info,5)) || $equal(issuer, $array());
  11.123 +}
  11.124 +
  11.125 +
  11.126  - (MYPublicKey*) subjectPublicKey {
  11.127 -    NSArray *keyInfo = $cast(NSArray, $atIf(self.info, 6));
  11.128 +    NSArray *keyInfo = $cast(NSArray, $atIf(self._info, 6));
  11.129      MYOID *keyAlgorithmID = $castIf(MYOID, $atIf($castIf(NSArray,$atIf(keyInfo,0)), 0));
  11.130      if (!$equal(keyAlgorithmID, kRSAAlgorithmID))
  11.131          return nil;
  11.132      MYBitString *keyData = $cast(MYBitString, $atIf(keyInfo, 1));
  11.133      if (!keyData) return nil;
  11.134      return [[[MYPublicKey alloc] initWithKeyData: keyData.bits] autorelease];
  11.135 -    /*
  11.136 -    NSArray *keyParts = $castIf(NSArray, MYBERParse(keyData, nil));
  11.137 -    if (!keyParts) return nil;
  11.138 -    MYBitString *modulus = $castIf(MYBitString, $atIf(keyParts,0));
  11.139 -    int exponent = [$castIf(NSNumber, $atIf(keyParts,1)) intValue];
  11.140 -    if (!modulus || exponent<3) return nil;
  11.141 -    */
  11.142  }
  11.143  
  11.144  - (MYPublicKey*) issuerPublicKey {
  11.145      if (_issuer)
  11.146          return _issuer.publicKey;
  11.147 -    else if (self.isSelfSigned)
  11.148 +    else if (self.isRoot)
  11.149          return self.subjectPublicKey;
  11.150      else
  11.151          return nil;
  11.152  }
  11.153  
  11.154 -
  11.155  - (NSData*) signedData {
  11.156      // The root object is a sequence; we want to extract the 1st object of that sequence.
  11.157      const UInt8 *certStart = _data.bytes;
  11.158 @@ -159,7 +193,7 @@
  11.159  }
  11.160  
  11.161  - (BOOL) validateSignature {
  11.162 -    if (!$equal(self.signatureAlgorithmID, [MYParsedCertificate RSAWithSHA1AlgorithmID]))
  11.163 +    if (!$equal(self.signatureAlgorithmID, kRSAWithSHA1AlgorithmID))
  11.164          return NO;
  11.165      NSData *signedData = self.signedData;
  11.166      NSData *signature = self.signature;
  11.167 @@ -169,45 +203,191 @@
  11.168  }
  11.169  
  11.170  
  11.171 +#pragma mark -
  11.172 +#pragma mark CERTIFICATE GENERATION:
  11.173 +
  11.174 +
  11.175 +- (id) initWithPublicKey: (MYPublicKey*)pubKey {
  11.176 +    Assert(pubKey);
  11.177 +    self = [super init];
  11.178 +    if (self != nil) {
  11.179 +        id empty = [NSNull null];
  11.180 +        id version = [[MYASN1Object alloc] initWithTag: 0 ofClass: 2 components: $array($object(0))];
  11.181 +        _root = $array( $marray(version,
  11.182 +                                empty,       // serial #
  11.183 +                                $array(kRSAAlgorithmID),
  11.184 +                                $marray(),
  11.185 +                                $marray(empty, empty),
  11.186 +                                $marray(),
  11.187 +                                $array( $array(kRSAAlgorithmID, empty),
  11.188 +                                       [MYBitString bitStringWithData: pubKey.keyData] ) ) );
  11.189 +        [version release];
  11.190 +        [_root retain];
  11.191 +    }
  11.192 +    return self;
  11.193 +}
  11.194 +
  11.195 +
  11.196 +- (void) _setString: (NSString*)value forOID: (MYOID*)oid atInfoIndex: (unsigned)infoIndex {
  11.197 +    NSMutableArray *pair = (NSMutableArray*) [self _pairForOID: oid atInfoIndex: infoIndex];
  11.198 +    if (pair) {
  11.199 +        [pair replaceObjectAtIndex: 1 withObject: value];
  11.200 +    } else {
  11.201 +        NSMutableArray *names = $castIf(NSMutableArray, $atIf(self._info, infoIndex));
  11.202 +        [names addObject: [NSSet setWithObject: $marray(oid,value)]];
  11.203 +    }
  11.204 +}
  11.205 +
  11.206 +
  11.207 +- (void) setValidFrom: (NSDate*)validFrom {
  11.208 +    [(NSMutableArray*)self._validDates replaceObjectAtIndex: 0 withObject: validFrom];
  11.209 +}
  11.210 +
  11.211 +- (void) setValidTo: (NSDate*)validTo {
  11.212 +    [(NSMutableArray*)self._validDates replaceObjectAtIndex: 1 withObject: validTo];
  11.213 +}
  11.214 +
  11.215 +- (void) setCommonName: (NSString*)commonName {
  11.216 +    [self _setString: commonName forOID: kCommonNameOID atInfoIndex: 5];
  11.217 +}
  11.218 +
  11.219 +- (void) setGivenName: (NSString*)givenName {
  11.220 +    [self _setString: givenName forOID: kGivenNameOID atInfoIndex: 5];
  11.221 +}
  11.222 +
  11.223 +- (void) setSurname: (NSString*)surname {
  11.224 +    [self _setString: surname forOID: kSurnameOID atInfoIndex: 5];
  11.225 +}
  11.226 +
  11.227 +- (void) setDescription: (NSString*)description {
  11.228 +    [self _setString: description forOID: kDescriptionOID atInfoIndex: 5];
  11.229 +}
  11.230 +
  11.231 +- (void) setEmailAddress: (NSString*)emailAddress {
  11.232 +    [self _setString: emailAddress forOID: kEmailOID atInfoIndex: 5];
  11.233 +}
  11.234 +
  11.235 +
  11.236 +- (BOOL) selfSignWithPrivateKey: (MYPrivateKey*)privateKey error: (NSError**)outError {
  11.237 +    // Copy subject to issuer:
  11.238 +    NSMutableArray *info = (NSMutableArray*)self._info;
  11.239 +    [info replaceObjectAtIndex: 3 withObject: [info objectAtIndex: 5]];
  11.240 +    
  11.241 +    // Set serial number if there isn't one yet:
  11.242 +    if (!$castIf(NSNumber, [info objectAtIndex: 1])) {
  11.243 +        UInt64 serial = floor(CFAbsoluteTimeGetCurrent() * 1000);
  11.244 +        [info replaceObjectAtIndex: 1 withObject: $object(serial)];
  11.245 +    }
  11.246 +    
  11.247 +    // Set up valid date range if there isn't one yet:
  11.248 +    NSDate *validFrom = self.validFrom;
  11.249 +    if (!validFrom)
  11.250 +        validFrom = self.validFrom = [NSDate date];
  11.251 +    NSDate *validTo = self.validTo;
  11.252 +    if (!validTo)
  11.253 +        self.validTo = [validFrom addTimeInterval: kDefaultExpirationTime]; 
  11.254 +    
  11.255 +    // Append signature to cert structure:
  11.256 +    NSData *dataToSign = [MYDEREncoder encodeRootObject: info error: outError];
  11.257 +    if (!dataToSign)
  11.258 +        return NO;
  11.259 +    setObj(&_root, $array(info, 
  11.260 +                          $array(kRSAWithSHA1AlgorithmID, [NSNull null]),
  11.261 +                          [MYBitString bitStringWithData: [privateKey signData: dataToSign]]));
  11.262 +    
  11.263 +    setObj(&_data, [MYDEREncoder encodeRootObject: _root error: outError]);
  11.264 +    return _data!=nil;
  11.265 +}
  11.266 +
  11.267 +
  11.268  @end
  11.269  
  11.270  
  11.271  
  11.272  
  11.273 +#if DEBUG
  11.274 +
  11.275 +
  11.276 +static MYParsedCertificate* testCert(NSString *filename, BOOL selfSigned) {
  11.277 +    Log(@"--- Creating MYParsedCertificate from %@", filename);
  11.278 +    NSData *certData = [NSData dataWithContentsOfFile: filename];
  11.279 +    //Log(@"Cert Data =\n%@", certData);
  11.280 +    NSError *error = nil;
  11.281 +    MYParsedCertificate *pcert = [[MYParsedCertificate alloc] initWithCertificateData: certData 
  11.282 +                                                                                error: &error];
  11.283 +    CAssertNil(error);
  11.284 +    CAssert(pcert != nil);
  11.285 +    
  11.286 +    CAssertEq(pcert.isRoot, selfSigned);
  11.287 +    
  11.288 +    NSData *signedData = pcert.signedData;
  11.289 +    //Log(@"Signed Data = (length=%x)\n%@", signedData.length, signedData);
  11.290 +    CAssertEqual(signedData, [certData subdataWithRange: NSMakeRange(4,signedData.length)]);
  11.291 +    
  11.292 +    Log(@"AlgID = %@", pcert.signatureAlgorithmID);
  11.293 +    Log(@"Signature = %@", pcert.signature);
  11.294 +    CAssertEqual(pcert.signatureAlgorithmID, kRSAWithSHA1AlgorithmID);
  11.295 +    CAssert(pcert.signature != nil);
  11.296 +    Log(@"Subject Public Key = %@", pcert.subjectPublicKey);
  11.297 +    CAssert(pcert.subjectPublicKey);
  11.298 +    if (selfSigned) {
  11.299 +        Log(@"Issuer Public Key = %@", pcert.issuerPublicKey);
  11.300 +        CAssert(pcert.issuerPublicKey);
  11.301 +        
  11.302 +        CAssert(pcert.validateSignature);
  11.303 +    }
  11.304 +    Log(@"Common Name = %@", pcert.commonName);
  11.305 +    Log(@"Given Name  = %@", pcert.givenName);
  11.306 +    Log(@"Surname     = %@", pcert.surname);
  11.307 +    Log(@"Desc        = %@", pcert.description);
  11.308 +    Log(@"Email       = %@", pcert.emailAddress);
  11.309 +    return pcert;
  11.310 +}
  11.311 +
  11.312 +
  11.313  TestCase(ParsedCert) {
  11.314 -    auto void testCert(NSString *filename, BOOL selfSigned);
  11.315      testCert(@"../../Tests/selfsigned.cer", YES);
  11.316      testCert(@"../../Tests/iphonedev.cer", NO);
  11.317 -    auto void testCert(NSString *filename, BOOL selfSigned) {
  11.318 -        Log(@"--- Creating MYParsedCertificate from %@", filename);
  11.319 -        NSData *certData = [NSData dataWithContentsOfFile: filename];
  11.320 -        //Log(@"Cert Data =\n%@", certData);
  11.321 -        NSError *error = nil;
  11.322 -        MYParsedCertificate *pcert = [[MYParsedCertificate alloc] initWithCertificateData: certData 
  11.323 -                                                                                    error: &error];
  11.324 -        CAssertNil(error);
  11.325 -        CAssert(pcert != nil);
  11.326 -        
  11.327 -        CAssertEq(pcert.isSelfSigned, selfSigned);
  11.328 -        
  11.329 -        NSData *signedData = pcert.signedData;
  11.330 -        //Log(@"Signed Data = (length=%x)\n%@", signedData.length, signedData);
  11.331 -        CAssertEqual(signedData, [certData subdataWithRange: NSMakeRange(4,signedData.length)]);
  11.332 -        
  11.333 -        Log(@"AlgID = %@", pcert.signatureAlgorithmID);
  11.334 -        Log(@"Signature = %@", pcert.signature);
  11.335 -        CAssertEqual(pcert.signatureAlgorithmID, [MYParsedCertificate RSAWithSHA1AlgorithmID]);
  11.336 -        CAssert(pcert.signature != nil);
  11.337 -        Log(@"Subject Public Key = %@", pcert.subjectPublicKey);
  11.338 -        CAssert(pcert.subjectPublicKey);
  11.339 -        if (selfSigned) {
  11.340 -            Log(@"Issuer Public Key = %@", pcert.issuerPublicKey);
  11.341 -            CAssert(pcert.issuerPublicKey);
  11.342 -            
  11.343 -            CAssert(pcert.validateSignature);
  11.344 -        }
  11.345 -    }
  11.346 -}    
  11.347 +}
  11.348 +
  11.349 +
  11.350 +#import "MYKeychain.h"
  11.351 +
  11.352 +TestCase(CreateCert) {
  11.353 +    MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
  11.354 +    MYParsedCertificate *pcert = [[MYParsedCertificate alloc] initWithPublicKey: privateKey.publicKey];
  11.355 +    pcert.commonName = @"testcase";
  11.356 +    pcert.givenName = @"Test";
  11.357 +    pcert.surname = @"Case";
  11.358 +    pcert.description = @"Just a test certificate created by MYCrypto";
  11.359 +    pcert.emailAddress = @"testcase@example.com";
  11.360 +
  11.361 +    CAssertEqual(pcert.commonName, @"testcase");
  11.362 +    CAssertEqual(pcert.givenName, @"Test");
  11.363 +    CAssertEqual(pcert.surname, @"Case");
  11.364 +    CAssertEqual(pcert.description, @"Just a test certificate created by MYCrypto");
  11.365 +    CAssertEqual(pcert.emailAddress, @"testcase@example.com");
  11.366 +    
  11.367 +    Log(@"Signing...");
  11.368 +    NSError *error;
  11.369 +    CAssert([pcert selfSignWithPrivateKey: privateKey error: &error]);
  11.370 +    CAssertNil(error);
  11.371 +    NSData *certData = pcert.certificateData;
  11.372 +    Log(@"Generated cert = \n%@", certData);
  11.373 +    CAssert(certData);
  11.374 +    [certData writeToFile: @"../../Tests/generated.cer" atomically: YES];
  11.375 +    MYParsedCertificate *pcert2 = testCert(@"../../Tests/generated.cer", YES);
  11.376 +    
  11.377 +    Log(@"Verifying...");
  11.378 +    CAssertEqual(pcert2.commonName, @"testcase");
  11.379 +    CAssertEqual(pcert2.givenName, @"Test");
  11.380 +    CAssertEqual(pcert2.surname, @"Case");
  11.381 +    CAssertEqual(pcert2.description, @"Just a test certificate created by MYCrypto");
  11.382 +    CAssertEqual(pcert2.emailAddress, @"testcase@example.com");
  11.383 +}
  11.384 +
  11.385 +#endif
  11.386 +
  11.387  
  11.388  
  11.389  
  11.390 @@ -215,8 +395,8 @@
  11.391   
  11.392  Sequence:                           <-- top
  11.393      Sequence:                       <-- info
  11.394 -        MYASN1Object[2/0]:          <-- version (int, constructed)
  11.395 -            2
  11.396 +        MYASN1Object[2/0]:          <-- version (tag=0, constructed)
  11.397 +            2                       
  11.398          1                           <-- serial number
  11.399          Sequence:
  11.400              {1 2 840 113549 1 1 1}  <-- algorithm ID
  11.401 @@ -246,23 +426,23 @@
  11.402              2010-04-13 21:54:35 -0700
  11.403          Sequence:                       <-- subject
  11.404              Set:
  11.405 -                Sequence:
  11.406 +                Sequence:                   <-- surname
  11.407                      {2 5 4 4}
  11.408                      Widdershins
  11.409              Set:
  11.410 -                Sequence:
  11.411 +                Sequence:                   <-- email
  11.412                      {1 2 840 113549 1 9 1}
  11.413                      waldo@example.com
  11.414              Set:
  11.415 -                Sequence:
  11.416 +                Sequence:                   <-- common name
  11.417                      {2 5 4 3}
  11.418                      waldo
  11.419              Set:
  11.420 -                Sequence:
  11.421 +                Sequence:                   <-- first name
  11.422                      {2 5 4 42}
  11.423                      Waldo
  11.424              Set:
  11.425 -                Sequence:
  11.426 +                Sequence:                   <-- description
  11.427                      {2 5 4 13}
  11.428                      Just a fictitious person
  11.429          Sequence:                               <-- public key info