diff options
Diffstat (limited to 'cpp/src/IceSSL/SecureTransportCertificateI.cpp')
-rw-r--r-- | cpp/src/IceSSL/SecureTransportCertificateI.cpp | 858 |
1 files changed, 858 insertions, 0 deletions
diff --git a/cpp/src/IceSSL/SecureTransportCertificateI.cpp b/cpp/src/IceSSL/SecureTransportCertificateI.cpp new file mode 100644 index 00000000000..f988de667a5 --- /dev/null +++ b/cpp/src/IceSSL/SecureTransportCertificateI.cpp @@ -0,0 +1,858 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceSSL/Plugin.h> +#include <IceSSL/SecureTransport.h> +#include <IceSSL/CertificateI.h> +#include <IceSSL/SecureTransportUtil.h> +#include <IceSSL/RFC2253.h> + +#include <Ice/UniqueRef.h> +#include <Ice/Base64.h> + +#include <IceUtil/MutexPtrLock.h> + +#include <Security/Security.h> + +using namespace Ice; +using namespace IceInternal; +using namespace IceSSL; +using namespace IceSSL::SecureTransport; + +using namespace std; + +namespace +{ + +string +certificateOIDAlias(const string& name) +{ + for(int i = 0; i < certificateOIDSSize; ++i) + { + const CertificateOID* certificateOID = &certificateOIDS[i]; + assert(certificateOID); + if(name == certificateOID->name) + { + return certificateOID->alias; + } + } + return name; +} + +string +escapeX509Name(const string& name) +{ + ostringstream os; + for(string::const_iterator i = name.begin(); i != name.end(); ++i) + { + switch(*i) + { + case ',': + case '=': + case '+': + case '<': + case '>': + case '#': + case ';': + { + os << '\\'; + } + default: + { + break; + } + } + os << *i; + } + return os.str(); +} + +#ifdef ICE_USE_SECURE_TRANSPORT_IOS +// +// ASN1Parser to pase the subject/issuer ASN.1 DER encoded attributes on iOS. +// +class ASN1Parser +{ +public: + + ASN1Parser(CFDataRef data) : _data(CFDataGetBytePtr(data)), _length(CFDataGetLength(data)), _p(_data), _next(0) + { + } + + list<pair<string, string> > + parse() + { + list<pair<string, string> > rdns; + while(_p < _data + _length) + { + switch(parseByte()) + { + case 0x06: // OID + { + _rdn.first = parseOID(); + break; + } + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x0C: // UTF8String + case 0x16: // IA5String + { + _rdn.second = escapeX509Name(parseUTF8String()); + break; + } + case 0x30: // SEQUENCE + case 0x31: // SET + { + int length = parseLength(0); + _next = _p + length; + if(_next > _data + _length) + { + throw CertificateEncodingException(__FILE__, __LINE__, "invalid length"); + } + break; + } + default: + { + // Unsupported tag, skip the SET. + if(!_next) + { + return rdns; + } + _p = _next; + _next = 0; + break; + } + } + if(_p == _next) + { + rdns.push_back(_rdn); + } + } + return rdns; + } + + string + parseOID() + { + int length = parseLength(1); + ostringstream oid; + unsigned char c = parseByte(); + oid << c / 40 << "." << c % 40; + while(--length > 0) + { + if((*_p & 0x80) == 0) + { + oid << "." << static_cast<int>(parseByte()); + } + else + { + uint64_t result = (uint64_t)(*_p & 127); + while(parseByte() & 128) + { + result = (result << 7) | (uint64_t)(*_p & 127); + --length; + } + oid << "." << result; + } + } + return certificateOIDAlias(oid.str()); + } + + string + parseUTF8String() + { + int length = parseLength(0); + string v(reinterpret_cast<const char*>(_p), length); + _p += length; + return v; + } + + int + parseLength(int required) + { + int length = 0; + if((*_p & 0x80) == 0) + { + length = static_cast<int>(parseByte()); + } + else + { + int nbytes = static_cast<int>(parseByte()); + for(int i = 0; i < nbytes; ++i) + { + length = length * 256 + parseByte(); + } + } + if((required > 0 && length < required) || (_p + length > _data + _length)) + { + throw CertificateEncodingException(__FILE__, __LINE__, "invalid length"); + } + return length; + } + + unsigned char + parseByte() + { + if(_p >= _data + _length) + { + throw CertificateEncodingException(__FILE__, __LINE__, "invalid length"); + } + unsigned char b = *_p++; + return b; + } + +private: + + const unsigned char* _data; + const size_t _length; + const unsigned char* _p; + const unsigned char* _next; + pair<string, string> _rdn; + list<pair<string, string> > _rdns; +}; + +#endif + + +class SecureTransportCertificateI : public IceSSL::SecureTransport::Certificate, + public IceSSL::CertificateI +{ +public: + + SecureTransportCertificateI(SecCertificateRef); + + virtual bool operator==(const IceSSL::Certificate&) const; + + virtual vector<Ice::Byte> getAuthorityKeyIdentifier() const; + virtual vector<Ice::Byte> getSubjectKeyIdentifier() const; + virtual bool verify(const IceSSL::CertificatePtr&) const; + virtual string encode() const; + +#ifdef ICE_CPP11_MAPPING + virtual chrono::system_clock::time_point getNotAfter() const; + virtual chrono::system_clock::time_point getNotBefore() const; +#else + virtual IceUtil::Time getNotAfter() const; + virtual IceUtil::Time getNotBefore() const; +#endif + + virtual string getSerialNumber() const; + virtual DistinguishedName getIssuerDN() const; + virtual vector<pair<int, string> > getIssuerAlternativeNames() const; + virtual DistinguishedName getSubjectDN() const; + virtual vector<pair<int, string> > getSubjectAlternativeNames() const; + virtual int getVersion() const; + virtual SecCertificateRef getCert() const; + +private: + + IceInternal::UniqueRef<SecCertificateRef> _cert; + +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + void initializeAttributes() const; + + mutable IceInternal::UniqueRef<CFDataRef> _subject; + mutable IceInternal::UniqueRef<CFDataRef> _issuer; + mutable std::string _serial; + mutable int _version; +#endif +}; + +#ifndef ICE_USE_SECURE_TRANSPORT_IOS + +// +// Map alternative name alias to its types. +// +const char* certificateAlternativeNameTypes[] = {"", "Email Address", "DNS Name", "", "Directory Name", "", "URI", + "IP Address"}; +const int certificateAlternativeNameTypesSize = sizeof(certificateAlternativeNameTypes) / sizeof(char*); + +int +certificateAlternativeNameType(const string& alias) +{ + if(!alias.empty()) + { + for(int i = 0; i < certificateAlternativeNameTypesSize; ++i) + { + if(alias == certificateAlternativeNameTypes[i]) + { + return i; + } + } + } + return -1; // Not supported +} + +DistinguishedName +getX509Name(SecCertificateRef cert, CFTypeRef key) +{ + assert(key == kSecOIDX509V1IssuerName || key == kSecOIDX509V1SubjectName); + list<pair<string, string> > rdnPairs; + UniqueRef<CFDictionaryRef> property(getCertificateProperty(cert, key)); + if(property) + { + CFArrayRef dn = static_cast<CFArrayRef>(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); + int size = CFArrayGetCount(dn); + for(int i = 0; i < size; ++i) + { + CFDictionaryRef dict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(dn, i)); + rdnPairs.push_front(make_pair( + certificateOIDAlias( + fromCFString((static_cast<CFStringRef>(CFDictionaryGetValue(dict, kSecPropertyKeyLabel))))), + escapeX509Name( + fromCFString(static_cast<CFStringRef>(CFDictionaryGetValue(dict, kSecPropertyKeyValue)))))); + } + } + return DistinguishedName(rdnPairs); +} + +vector<pair<int, string> > +getX509AltName(SecCertificateRef cert, CFTypeRef key) +{ + assert(key == kSecOIDIssuerAltName || key == kSecOIDSubjectAltName); + UniqueRef<CFDictionaryRef> property(getCertificateProperty(cert, key)); + + vector<pair<int, string> > pairs; + if(property) + { + CFArrayRef names = static_cast<CFArrayRef>(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); + int size = CFArrayGetCount(names); + + for(int i = 0; i < size; ++i) + { + CFDictionaryRef dict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(names, i)); + + int type = certificateAlternativeNameType( + fromCFString(static_cast<CFStringRef>(CFDictionaryGetValue(dict, kSecPropertyKeyLabel)))); + if(type != -1) + { + CFStringRef v = static_cast<CFStringRef>(CFDictionaryGetValue(dict, kSecPropertyKeyValue)); + CFStringRef t = static_cast<CFStringRef>(CFDictionaryGetValue(dict, kSecPropertyKeyType)); + if(CFEqual(t, kSecPropertyTypeString) || CFEqual(t, kSecPropertyTypeTitle)) + { + pairs.push_back(make_pair(type, fromCFString(v))); + } + else if(CFEqual(t, kSecPropertyTypeURL)) + { + pairs.push_back(make_pair(type, fromCFString(CFURLGetString((CFURLRef)v)))); + } + else if(CFEqual(t, kSecPropertyTypeSection)) + { + CFArrayRef section = (CFArrayRef)v; + ostringstream os; + for(int i = 0, count = CFArrayGetCount(section); i < count;) + { + CFDictionaryRef d = (CFDictionaryRef)CFArrayGetValueAtIndex(section, i); + + CFStringRef sectionLabel = static_cast<CFStringRef>(CFDictionaryGetValue(d, kSecPropertyKeyLabel)); + CFStringRef sectionValue = static_cast<CFStringRef>(CFDictionaryGetValue(d, kSecPropertyKeyValue)); + + os << certificateOIDAlias(fromCFString(sectionLabel)) << "=" << fromCFString(sectionValue); + if(++i < count) + { + os << ","; + } + } + pairs.push_back(make_pair(type, os.str())); + } + } + } + } + return pairs; +} + +#ifdef ICE_CPP11_MAPPING +chrono::system_clock::time_point +#else +IceUtil::Time +#endif +getX509Date(SecCertificateRef cert, CFTypeRef key) +{ + assert(key == kSecOIDX509V1ValidityNotAfter || key == kSecOIDX509V1ValidityNotBefore); + UniqueRef<CFDictionaryRef> property(getCertificateProperty(cert, key)); + CFAbsoluteTime seconds = 0; + if(property) + { + CFNumberRef date = static_cast<CFNumberRef>(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue)); + CFNumberGetValue(date, kCFNumberDoubleType, &seconds); + } + + IceUtil::Time time = IceUtil::Time::secondsDouble(kCFAbsoluteTimeIntervalSince1970 + seconds); + +#ifdef ICE_CPP11_MAPPING + return chrono::system_clock::time_point(chrono::microseconds(time.toMicroSeconds())); +#else + return time; +#endif +} + +string +getX509String(SecCertificateRef cert, CFTypeRef key) +{ + assert(key == kSecOIDX509V1SerialNumber || key == kSecOIDX509V1Version); + UniqueRef<CFDictionaryRef> property(getCertificateProperty(cert, key)); + return property ? + fromCFString(static_cast<CFStringRef>(CFDictionaryGetValue(property.get(), kSecPropertyKeyValue))) : ""; +} +#endif + +} // end anonymous namespace + +SecureTransportCertificateI::SecureTransportCertificateI(SecCertificateRef cert) : _cert(cert) +{ + if(!_cert) + { + throw IceUtil::IllegalArgumentException(__FILE__, __LINE__, "Invalid certificate reference"); + } +} + +bool +SecureTransportCertificateI::operator==(const IceSSL::Certificate& r) const +{ + const SecureTransportCertificateI* p = dynamic_cast<const SecureTransportCertificateI*>(&r); + if(!p) + { + return false; + } + return CFEqual(_cert.get(), p->_cert.get()); +} + +vector<Ice::Byte> +SecureTransportCertificateI::getAuthorityKeyIdentifier() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); +#else // MacOS + vector<Ice::Byte> keyid; + + UniqueRef<CFDictionaryRef> property(getCertificateProperty(_cert.get(), kSecOIDAuthorityKeyIdentifier)); + if(property) + { + CFTypeRef type = 0; + CFTypeRef value; + if(CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyType, &type)) + { + if(CFEqual(type, kSecPropertyTypeSection)) + { + if(CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyValue, &value)) + { + if(CFArrayGetCount(static_cast<CFArrayRef>(value)) >= 0) + { + value = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(value), 1); + type = CFDictionaryGetValue(static_cast<CFDictionaryRef>(value), kSecPropertyKeyType); + } + } + } + + if(CFEqual(type, kSecPropertyTypeData)) + { + CFDataRef data = static_cast<CFDataRef>( + CFDictionaryGetValue(static_cast<CFDictionaryRef>(value), kSecPropertyKeyValue)); + keyid.resize(CFDataGetLength(data)); + memcpy(&keyid[0], CFDataGetBytePtr(data), CFDataGetLength(data)); + } + } + } + return keyid; +#endif +} + +vector<Ice::Byte> +SecureTransportCertificateI::getSubjectKeyIdentifier() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); +#else // MacOS + vector<Ice::Byte> keyid; + UniqueRef<CFDictionaryRef> property(getCertificateProperty(_cert.get(), kSecOIDSubjectKeyIdentifier)); + if(property) + { + CFTypeRef type = 0; + CFTypeRef value; + if(CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyType, &type)) + { + if(CFEqual(type, kSecPropertyTypeSection)) + { + if(CFDictionaryGetValueIfPresent(property.get(), kSecPropertyKeyValue, &value)) + { + if(CFArrayGetCount(static_cast<CFArrayRef>(value)) >= 0) + { + value = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(value), 1); + type = CFDictionaryGetValue(static_cast<CFDictionaryRef>(value), kSecPropertyKeyType); + } + } + } + + if(CFEqual(type, kSecPropertyTypeData)) + { + CFDataRef data = static_cast<CFDataRef>( + CFDictionaryGetValue(static_cast<CFDictionaryRef>(value), kSecPropertyKeyValue)); + keyid.resize(CFDataGetLength(data)); + memcpy(&keyid[0], CFDataGetBytePtr(data), CFDataGetLength(data)); + } + } + } + return keyid; +#endif +} + +bool +SecureTransportCertificateI::verify(const IceSSL::CertificatePtr& cert) const +{ + bool valid = false; + SecureTransportCertificateI* c = dynamic_cast<SecureTransportCertificateI*>(cert.get()); + if(c) + { + // + // We first check if the given certificate subject match our certificate + // issuer. Otherwhise when checking a certificate against itself + // SecTrustEvaluate always returns it is valid. + // +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + initializeAttributes(); + c->initializeAttributes(); + valid = CFEqual(_issuer.get(), c->_subject.get()); +#else // MacOS + UniqueRef<CFErrorRef> error; + UniqueRef<CFDataRef> issuer(SecCertificateCopyNormalizedIssuerContent(_cert.get(), &error.get())); + if(error) + { + throw CertificateEncodingException(__FILE__, __LINE__, "certificate error:\n" + sslErrorToString(error.get())); + } + UniqueRef<CFDataRef> subject(SecCertificateCopyNormalizedSubjectContent(c->getCert(), &error.get())); + if(error) + { + throw CertificateEncodingException(__FILE__, __LINE__, "certificate error:\n" + sslErrorToString(error.get())); + } + + // + // The certificate issuer must match the CA subject. + // + valid = CFEqual(issuer.get(), subject.get()); +#endif + if(valid) + { + UniqueRef<SecPolicyRef> policy(SecPolicyCreateBasicX509()); + UniqueRef<SecTrustRef> trust; + OSStatus err = 0;; + if((err = SecTrustCreateWithCertificates(_cert.get(), policy.get(), &trust.get()))) + { + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + + SecCertificateRef certs[1] = { c->getCert() }; + UniqueRef<CFArrayRef> anchorCertificates( + CFArrayCreate(kCFAllocatorDefault, (const void**)&certs, 1, &kCFTypeArrayCallBacks)); + if((err = SecTrustSetAnchorCertificates(trust.get(), anchorCertificates.get()))) + { + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + + SecTrustResultType trustResult = kSecTrustResultInvalid; + if((err = SecTrustEvaluate(trust.get(), &trustResult))) + { + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + + valid = trustResult == kSecTrustResultUnspecified; + } + } + return valid; +} + +string +SecureTransportCertificateI::encode() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + UniqueRef<CFDataRef> cert(SecCertificateCopyData(_cert.get())); + vector<unsigned char> data(CFDataGetBytePtr(cert.get()), CFDataGetBytePtr(cert.get()) + CFDataGetLength(cert.get())); + ostringstream os; + os << "-----BEGIN CERTIFICATE-----\n"; + os << IceInternal::Base64::encode(data); + os << "-----END CERTIFICATE-----\n"; + return os.str(); +#else // MacOS + UniqueRef<CFDataRef> exported; + OSStatus err = SecItemExport(_cert.get(), kSecFormatPEMSequence, kSecItemPemArmour, 0, &exported.get()); + if(err != noErr) + { + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + return string(reinterpret_cast<const char*>(CFDataGetBytePtr(exported.get())), CFDataGetLength(exported.get())); +#endif +} + +#ifdef ICE_CPP11_MAPPING +chrono::system_clock::time_point +#else +IceUtil::Time +#endif +SecureTransportCertificateI::getNotAfter() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); +#else // MacOS + return getX509Date(_cert.get(), kSecOIDX509V1ValidityNotAfter); +#endif +} + +#ifdef ICE_CPP11_MAPPING +chrono::system_clock::time_point +#else +IceUtil::Time +#endif +SecureTransportCertificateI::getNotBefore() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + throw Ice::FeatureNotSupportedException(__FILE__, __LINE__); +#else // MacOS + return getX509Date(_cert.get(), kSecOIDX509V1ValidityNotBefore); +#endif +} + +string +SecureTransportCertificateI::getSerialNumber() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + initializeAttributes(); + return _serial; +#else // MacOS + return getX509String(_cert.get(), kSecOIDX509V1SerialNumber); +#endif +} + +DistinguishedName +SecureTransportCertificateI::getIssuerDN() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + initializeAttributes(); + return _issuer ? DistinguishedName(ASN1Parser(_issuer.get()).parse()) : DistinguishedName(""); +#else // MacOS + return getX509Name(_cert.get(), kSecOIDX509V1IssuerName); +#endif +} + +vector<pair<int, string> > +SecureTransportCertificateI::getIssuerAlternativeNames() const +{ +#if defined(ICE_USE_SECURE_TRANSPORT_IOS) + throw FeatureNotSupportedException(__FILE__, __LINE__); +#else // MacOS + return getX509AltName(_cert.get(), kSecOIDIssuerAltName); +#endif +} + +DistinguishedName +SecureTransportCertificateI::getSubjectDN() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + initializeAttributes(); + if(_subject) + { + return DistinguishedName(ASN1Parser(_subject.get()).parse()); + } + else + { + UniqueRef<CFStringRef> subjectSummary(SecCertificateCopySubjectSummary(_cert.get())); + return DistinguishedName("CN=" + fromCFString(subjectSummary.get())); + } +#else // MacOS + return getX509Name(_cert.get(), kSecOIDX509V1SubjectName); +#endif +} + +vector<pair<int, string> > +SecureTransportCertificateI::getSubjectAlternativeNames() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + throw FeatureNotSupportedException(__FILE__, __LINE__); +#else // MacOS + return getX509AltName(_cert.get(), kSecOIDSubjectAltName); +#endif +} + +int +SecureTransportCertificateI::getVersion() const +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + initializeAttributes(); + return _version; +#else // MacOS + return atoi(getX509String(_cert.get(), kSecOIDX509V1Version).c_str()) - 1; +#endif +} + +SecCertificateRef +SecureTransportCertificateI::getCert() const +{ + return _cert.get(); +} + +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + +IceUtil::Mutex* globalMutex = 0; + +class Init +{ +public: + + Init() + { + globalMutex = new IceUtil::Mutex; + } + + ~Init() + { + delete globalMutex; + globalMutex = 0; + } +}; + +Init init; + +void +SecureTransportCertificateI::initializeAttributes() const +{ + // + // We need to temporarily add the certificate to the keychain in order to + // retrieve its attributes. Unfortunately kSecMatchItemList doesn't work + // on iOS. We make sure only one thread adds/removes a cert at a time here. + // + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex); + + if(_subject) + { + return; + } + + UniqueRef<CFMutableDictionaryRef> query( + CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CFDictionarySetValue(query.get(), kSecValueRef, _cert.get()); + CFDictionarySetValue(query.get(), kSecReturnAttributes, kCFBooleanTrue); + + UniqueRef<CFDictionaryRef> attributes(0); + OSStatus err; + if((err = SecItemAdd(query.get(), reinterpret_cast<CFTypeRef*>(&attributes.get()))) == errSecDuplicateItem) + { + CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate); + err = SecItemCopyMatching(query.get(), reinterpret_cast<CFTypeRef*>(&attributes.get())); + } + else + { + query.reset(CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate); + CFDictionarySetValue(query.get(), kSecValueRef, _cert.get()); + err = SecItemDelete(query.get()); + } + + if(err != noErr) + { + _subject.reset(0); + _issuer.reset(0); + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + + _subject.retain(static_cast<CFDataRef>(CFDictionaryGetValue(attributes.get(), kSecAttrSubject))); + _issuer.retain(static_cast<CFDataRef>(CFDictionaryGetValue(attributes.get(), kSecAttrIssuer))); + CFDataRef serial = static_cast<CFDataRef>(CFDictionaryGetValue(attributes.get(), kSecAttrSerialNumber)); + ostringstream os; + for(int i = 0; i < CFDataGetLength(serial); ++i) + { + int c = static_cast<int>(CFDataGetBytePtr(serial)[i]); + if(i) + { + os << ' '; + } + os.fill('0'); + os.width(2); + os << hex << c; + } + _serial = os.str(); + CFNumberRef version = static_cast<CFNumberRef>(CFDictionaryGetValue(attributes.get(), kSecAttrCertificateType)); + if(!CFNumberGetValue(version, kCFNumberIntType, &_version)) + { + _version = -1; + } +} +#endif + +IceSSL::SecureTransport::CertificatePtr +IceSSL::SecureTransport::Certificate::create(SecCertificateRef cert) +{ + return ICE_MAKE_SHARED(SecureTransportCertificateI, cert); +} + +IceSSL::SecureTransport::CertificatePtr +IceSSL::SecureTransport::Certificate::load(const std::string& file) +{ + string resolved; + if(checkPath(file, "", false, resolved)) + { + return ICE_MAKE_SHARED(SecureTransportCertificateI, loadCertificate(resolved)); + } + else + { + throw CertificateReadException(__FILE__, __LINE__, "error opening file " + file); + } +} + +IceSSL::SecureTransport::CertificatePtr +IceSSL::SecureTransport::Certificate::decode(const std::string& encoding) +{ +#ifdef ICE_USE_SECURE_TRANSPORT_IOS + string::size_type size, startpos, endpos = 0; + startpos = encoding.find("-----BEGIN CERTIFICATE-----", endpos); + if(startpos != string::npos) + { + startpos += sizeof("-----BEGIN CERTIFICATE-----"); + endpos = encoding.find("-----END CERTIFICATE-----", startpos); + size = endpos - startpos; + } + else + { + startpos = 0; + endpos = string::npos; + size = encoding.size(); + } + + vector<unsigned char> data(IceInternal::Base64::decode(string(&encoding[startpos], size))); + UniqueRef<CFDataRef> certdata(CFDataCreate(kCFAllocatorDefault, &data[0], data.size())); + SecCertificateRef cert = SecCertificateCreateWithData(0, certdata.get()); + if(!cert) + { + assert(false); + throw CertificateEncodingException(__FILE__, __LINE__, "certificate is not a valid PEM-encoded certificate"); + } + return ICE_MAKE_SHARED(SecureTransportCertificateI, cert); +#else // MacOS + UniqueRef<CFDataRef> data( + CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, + reinterpret_cast<const UInt8*>(encoding.c_str()), + encoding.size(), kCFAllocatorNull)); + + SecExternalFormat format = kSecFormatUnknown; + SecExternalItemType type = kSecItemTypeCertificate; + + SecItemImportExportKeyParameters params; + memset(¶ms, 0, sizeof(params)); + params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + + UniqueRef<CFArrayRef> items; + OSStatus err = SecItemImport(data.get(), 0, &format, &type, 0, ¶ms, 0, &items.get()); + if(err) + { + throw CertificateEncodingException(__FILE__, __LINE__, sslErrorToString(err)); + } + + UniqueRef<SecKeychainItemRef> item; + item.retain(static_cast<SecKeychainItemRef>(const_cast<void*>(CFArrayGetValueAtIndex(items.get(), 0)))); + assert(SecCertificateGetTypeID() == CFGetTypeID(item.get())); + return ICE_MAKE_SHARED(SecureTransportCertificateI, reinterpret_cast<SecCertificateRef>(item.release())); +#endif +} |