/* * Copyright (c) 2015-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include #include #include #include #include namespace { template T bridge_cast(S source) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" return (__bridge T)(source); #pragma clang diagnostic pop } } namespace Swift { SecureTransportCertificate::SecureTransportCertificate(SecCertificateRef certificate) { assert(certificate); CFRetain(certificate); certificateHandle_ = boost::shared_ptr(certificate, CFRelease); parse(); } SecureTransportCertificate::SecureTransportCertificate(const ByteArray& der) { CFDataRef derData = CFDataCreateWithBytesNoCopy(NULL, der.data(), static_cast(der.size()), NULL); // certificate will take ownership of derData and free it on its release. SecCertificateRef certificate = SecCertificateCreateWithData(NULL, derData); if (certificate) { certificateHandle_ = boost::shared_ptr(certificate, CFRelease); parse(); } } SecureTransportCertificate::~SecureTransportCertificate() { } #define NS2STDSTRING(a) (a == nil ? std::string() : std::string([a cStringUsingEncoding:NSUTF8StringEncoding])) void SecureTransportCertificate::parse() { assert(certificateHandle_); CFErrorRef error = NULL; // The SecCertificateCopyValues function is not part of the iOS Secure Transport API. CFDictionaryRef valueDict = SecCertificateCopyValues(certificateHandle_.get(), 0, &error); if (valueDict) { // Handle subject. CFStringRef subject = SecCertificateCopySubjectSummary(certificateHandle_.get()); if (subject) { NSString* subjectStr = bridge_cast(subject); subjectName_ = NS2STDSTRING(subjectStr); CFRelease(subject); } // Handle a single Common Name. CFStringRef commonName = NULL; OSStatus error = SecCertificateCopyCommonName(certificateHandle_.get(), &commonName); if (!error && commonName) { NSString* commonNameStr = bridge_cast(commonName); commonNames_.push_back(NS2STDSTRING(commonNameStr)); } if (commonName) { CFRelease(commonName); } // Handle Subject Alternative Names NSDictionary* certDict = bridge_cast(valueDict); NSDictionary* subjectAltNamesDict = certDict[@"2.5.29.17"][@"value"]; for (NSDictionary* entry in subjectAltNamesDict) { if ([entry[@"label"] isEqualToString:[NSString stringWithUTF8String:ID_ON_XMPPADDR_OID]]) { xmppAddresses_.push_back(NS2STDSTRING(entry[@"value"])); } else if ([entry[@"label"] isEqualToString:[NSString stringWithUTF8String:ID_ON_DNSSRV_OID]]) { srvNames_.push_back(NS2STDSTRING(entry[@"value"])); } else if ([entry[@"label"] isEqualToString:@"DNS Name"]) { dnsNames_.push_back(NS2STDSTRING(entry[@"value"])); } } CFRelease(valueDict); } if (error) { CFRelease(error); } } std::string SecureTransportCertificate::getSubjectName() const { return subjectName_; } std::vector SecureTransportCertificate::getCommonNames() const { return commonNames_; } std::vector SecureTransportCertificate::getSRVNames() const { return srvNames_; } std::vector SecureTransportCertificate::getDNSNames() const { return dnsNames_; } std::vector SecureTransportCertificate::getXMPPAddresses() const { return xmppAddresses_; } ByteArray SecureTransportCertificate::toDER() const { ByteArray der; if (certificateHandle_) { CFDataRef derData = SecCertificateCopyData(certificateHandle_.get()); if (derData) { try { size_t dataSize = boost::numeric_cast(CFDataGetLength(derData)); der.resize(dataSize); CFDataGetBytes(derData, CFRangeMake(0,CFDataGetLength(derData)), der.data()); } catch (...) { } CFRelease(derData); } } return der; } }