From 3b0fbe786817772fcb94320e2d8af62567791337 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Tue, 1 Nov 2011 16:09:29 +0100 Subject: [PATCH] Add methods for easier certificate verification --- src/X509Certificate.h | 8 +++- src/X509Certificate.m | 102 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/X509Certificate.h b/src/X509Certificate.h index fa82496..3d53756 100644 --- a/src/X509Certificate.h +++ b/src/X509Certificate.h @@ -55,7 +55,13 @@ - (OFDictionary*)issuer; - (OFDictionary*)subject; - (OFDictionary*)subjectAlternativeName; +- (BOOL)hasCommonNameMatchingDomain: (OFString*)domain; +- (BOOL)hasDNSNameMatchingDomain: (OFString*)domain; +- (BOOL)hasSRVNameMatchingDomain: (OFString*)domain + service: (OFString*)service; +- (BOOL)X509_isAssertedDomain: (OFString*)asserted + equalDomain: (OFString*)domain; - (OFDictionary*)X509_dictionaryFromX509Name: (X509_NAME*)name; - (OFString*)X509_stringFromASN1Object: (ASN1_OBJECT*)obj; -- (OFString*) X509_stringFromASN1String: (ASN1_STRING*)str; +- (OFString*)X509_stringFromASN1String: (ASN1_STRING*)str; @end diff --git a/src/X509Certificate.m b/src/X509Certificate.m index 1f21796..aab1fce 100644 --- a/src/X509Certificate.m +++ b/src/X509Certificate.m @@ -241,6 +241,108 @@ return (subjectAlternativeName = ret); } +- (BOOL)hasCommonNameMatchingDomain: (OFString*)domain +{ + OFString *name; + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFList *CNs = [[self subject] objectForKey: OID_commonName]; + + for (name in CNs) { + if ([self X509_isAssertedDomain: name + equalDomain: domain]) { + [pool release]; + return YES; + } + } + + [pool release]; + return NO; +} + +- (BOOL)hasDNSNameMatchingDomain: (OFString*)domain +{ + OFString *name; + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFDictionary *SANs = [self subjectAlternativeName]; + OFList *assertedNames = [SANs objectForKey: @"dNSName"]; + + for (name in assertedNames) { + if ([self X509_isAssertedDomain: name + equalDomain: domain]) { + [pool release]; + return YES; + } + } + + [pool release]; + return NO; +} + +- (BOOL)hasSRVNameMatchingDomain: (OFString*)domain + service: (OFString*)service +{ + size_t serviceLength; + OFString *name; + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFDictionary *SANs = [self subjectAlternativeName]; + OFList *assertedNames = [[SANs objectForKey: @"otherName"] + objectForKey: OID_SRVName]; + + if (![service hasPrefix: @"_"]) + service = [service stringByPrependingString: @"_"]; + + service = [service stringByAppendingString: @"."]; + serviceLength = [service length]; + + for (name in assertedNames) { + if ([name hasPrefix: service]) { + OFString *asserted; + asserted = [name substringWithRange: + of_range(serviceLength, + [name length] - serviceLength)]; + if ([self X509_isAssertedDomain: asserted + equalDomain: domain]) { + [pool release]; + return YES; + } + } + } + + [pool release]; + return NO; +} + +- (BOOL) X509_isAssertedDomain: (OFString*)asserted + equalDomain: (OFString*)domain +{ + /* + * In accordance with RFC 6125 this only allows a wildcard as the + * left-most label and matches only the left-most label with it. + * E.g. *.example.com matches foo.example.com, + * but not foo.bar.example.com + */ + size_t firstDot; + if (![asserted caseInsensitiveCompare: domain]) + return YES; + + if (![asserted hasPrefix: @"*."]) + return NO; + + asserted = [asserted substringWithRange: of_range(2, + [asserted length] - 2)]; + + firstDot = [domain indexOfFirstOccurrenceOfString: @"."]; + if (firstDot == OF_INVALID_INDEX) + return NO; + domain = [domain substringWithRange: of_range(firstDot + 1, + [domain length] - firstDot - 1)]; + + if (![asserted caseInsensitiveCompare: domain]) + return YES; + + return NO; +} + - (OFDictionary*)X509_dictionaryFromX509Name: (X509_NAME*)name { int i;