sl@0: /* sl@0: * Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of the License "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * sl@0: */ sl@0: sl@0: sl@0: #include "openssl/x509.h" sl@0: #include "openssl/x509v3.h" sl@0: #include "openssl/pem.h" sl@0: #include "encdec.h" sl@0: #include "x509utils.h" sl@0: #include "logger.h" sl@0: // sl@0: // TKeyIdentifier sl@0: // sl@0: void EncodeHuman(REncodeWriteStream& aStream,const KeyIdentifierObject &aKeyId) sl@0: { sl@0: if(aKeyId.iAutoKey) sl@0: { sl@0: aStream.WriteCStr("auto"); sl@0: if(aKeyId.iHash.Length() == 0) sl@0: { sl@0: return; // Empty value so no point in including it in a comment... sl@0: } sl@0: if(!aStream.Verbose()) sl@0: { sl@0: return; // auto, and not in verbose mode so do not write value in comment sl@0: } sl@0: sl@0: aStream.WriteCStr(" # "); sl@0: } sl@0: aStream.WriteByte('\''); sl@0: const TUint8 *ptr = aKeyId.iHash.Ptr(); sl@0: TInt len = aKeyId.iHash.Length(); sl@0: while(len--) sl@0: { sl@0: TUint8 byte = *ptr++; sl@0: TUint8 buf[2]; sl@0: sl@0: TUint8 ch = ((byte & 0xf0) >> 4); sl@0: ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A'); sl@0: sl@0: // Write MSB char of byte sl@0: buf[0] = ch; sl@0: sl@0: ch = (byte & 0x0f); sl@0: ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A'); sl@0: sl@0: // Write LSB char of byte sl@0: buf[1] = ch; sl@0: sl@0: aStream.WriteBin(buf, sizeof(buf)); sl@0: if(len) sl@0: { sl@0: aStream.WriteByte(':'); sl@0: } sl@0: sl@0: } sl@0: aStream.WriteByte('\''); sl@0: } sl@0: void DecodeHuman(RDecodeReadStream& aStream, KeyIdentifierObject &aKeyId) sl@0: { sl@0: aStream.ReadNextToken(); sl@0: std::string tok = aStream.Token(); sl@0: sl@0: if(tok == "auto") sl@0: { sl@0: aKeyId.iAutoKey = true; sl@0: aKeyId.iHash.SetLength(0); sl@0: return; sl@0: } sl@0: aKeyId.iAutoKey = false; sl@0: sl@0: if((tok[0] != '\'') || (tok[tok.size()-1] != '\'') || (tok.size() < 2)) sl@0: { sl@0: dbg << Log::Indent() << "KeyIdentifier not enclosed in single quotes, or contains spaces - " << tok << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: tok.erase(0,1); sl@0: tok.erase(tok.size()-1,1); sl@0: sl@0: if(tok.size() == 0) sl@0: { sl@0: aKeyId.iHash.SetLength(0); sl@0: return; sl@0: } sl@0: sl@0: if(TInt(tok.size()) != (aKeyId.iHash.MaxLength()*2) + (aKeyId.iHash.MaxLength()-1)) sl@0: { sl@0: dbg << Log::Indent() << "WARNING: KeyIdentifier length not " << aKeyId.iHash.MaxLength()*2 << " hex digits" << Log::Endl(); sl@0: dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl(); sl@0: } sl@0: sl@0: bool bad = false; sl@0: TInt bytesRead = 0; sl@0: const char *hexDigit = tok.data(); sl@0: TInt charsToRead = tok.size(); sl@0: TUint8 *dest = const_cast(aKeyId.iHash.Ptr()); sl@0: while(charsToRead) sl@0: { sl@0: // Read MSB char sl@0: TUint8 byte = fromHex(*hexDigit++); sl@0: byte <<= 4; sl@0: --charsToRead; sl@0: sl@0: // Read LSB char sl@0: if(charsToRead == 0) sl@0: { sl@0: bad = true; sl@0: break; sl@0: } sl@0: byte |= fromHex(*hexDigit++); sl@0: --charsToRead; sl@0: sl@0: // Save decoded byte sl@0: *dest++ = byte; sl@0: ++bytesRead; sl@0: sl@0: if(charsToRead != 0) sl@0: { sl@0: // Consume : separator sl@0: if(*hexDigit++ != ':') sl@0: { sl@0: bad = true; sl@0: break; sl@0: } sl@0: --charsToRead; sl@0: } sl@0: } sl@0: sl@0: if(bytesRead > aKeyId.iHash.MaxLength()) sl@0: { sl@0: dbg << Log::Indent() << "Key Identifiier is too long" << Log::Endl(); sl@0: bad = true; sl@0: } sl@0: sl@0: sl@0: if(bad) sl@0: { sl@0: dbg << Log::Indent() << "KeyIdentifier invalid - It should be a single quoted string containing a series of 0 or more 2 digit hex numbers separated by : chars." << Log::Endl(); sl@0: dbg << Log::Indent() << "This field should normally be set to auto or omitted" << Log::Endl(); sl@0: dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: aKeyId.iHash.SetLength(bytesRead); sl@0: sl@0: sl@0: return; sl@0: } sl@0: sl@0: RWriteStream& operator<<(RWriteStream& aStream,const KeyIdentifierObject& aKeyId) sl@0: { sl@0: aStream << aKeyId.iHash; sl@0: return aStream; sl@0: } sl@0: sl@0: RReadStream& operator>>(RReadStream& aStream, KeyIdentifierObject& aKeyId) sl@0: { sl@0: aKeyId.iAutoKey = false; sl@0: aStream >> aKeyId.iHash; sl@0: return aStream; sl@0: } sl@0: sl@0: // It is illegal to pass a "X **" ptr to a function taking a "const X sl@0: // **" argument. This is because the function could change the callers sl@0: // pointer to point at a const object which the caller might then sl@0: // accidentally write to! sl@0: // sl@0: // Unfortunately openssl 0.9.7* defines d2i_X509 to take an "unsigned sl@0: // char **" and 0.9.8 takes "const unsigned char **", so neither sl@0: // caller choice will compile for both.... sl@0: sl@0: #if OPENSSL_VERSION_NUMBER >= 0x00908000L sl@0: #define D2I_CONST const sl@0: #else sl@0: #define D2I_CONST sl@0: #endif sl@0: sl@0: bool X509SubjectKeyId(EUseCertificateExtension aUseExtension, bool aUseRfc3280Algorithm, sl@0: bool aIsCa, const std::string &aCert, sl@0: std::string &aSubject, TKeyIdentifier &aSubjectKeyId) sl@0: { sl@0: bool done = false; sl@0: prog << Log::Indent() << "X509SubjectKeyId - aUseExtension " << aUseExtension << " aUseRfc3280Algorithm " << aUseRfc3280Algorithm << " :-" << Log::Endl(); sl@0: AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope sl@0: sl@0: // decode DER certificate into X509 structure sl@0: D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert.data(); sl@0: X509 *x509 = d2i_X509(NULL, &p, aCert.size()); sl@0: if(!x509 || ((const char *)p != aCert.data() + aCert.size())) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: // Return the Subject Name sl@0: prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl(); sl@0: aSubject = std::string(x509->name); sl@0: TUint32 ver = X509_get_version(x509); sl@0: prog << Log::Indent() << "Cert version is '" << ver << "'" << Log::Endl(); sl@0: sl@0: // if the ver is a v1 or v2 type then there is no way of knowing which is a CA, treat all certs as CA as done in the certificate recognizer. sl@0: bool treatAsCa = false; sl@0: if ( ver < 3 || aIsCa ) sl@0: { sl@0: treatAsCa = true; sl@0: } sl@0: sl@0: if(treatAsCa && aUseExtension) sl@0: { sl@0: // Attempt to read Subject Key Id extension sl@0: ASN1_OCTET_STRING *subKeyId = (ASN1_OCTET_STRING *) X509_get_ext_d2i(x509, NID_subject_key_identifier, NULL, NULL); sl@0: if(subKeyId) sl@0: { sl@0: prog << Log::Indent() << "Found SubjectKeyId extension" << Log::Endl(); sl@0: if(subKeyId->length <= aSubjectKeyId.MaxLength()) sl@0: { sl@0: aSubjectKeyId = TPtrC8(subKeyId->data, subKeyId->length); sl@0: done = true; sl@0: } sl@0: else sl@0: { sl@0: prog << Log::Indent() << "but SubjectKeyId > 160 bits so ignoring it" << Log::Endl(); sl@0: } sl@0: ASN1_OCTET_STRING_free(subKeyId); sl@0: } sl@0: } sl@0: sl@0: if(!done) sl@0: { sl@0: // Subject Key Id extension was ignored, missing or too long... sl@0: if(aUseRfc3280Algorithm) sl@0: { sl@0: // We do not need to decode the public key just hash its sl@0: // data as per rfc3280 4.2.1.2 method 1 sl@0: prog << Log::Indent() << "Calculating SubjectKeyId using RFC3280 4.2.1.2 method 1" << Log::Endl(); sl@0: unsigned char sha1hash[SHA_DIGEST_LENGTH]; sl@0: sl@0: SHA1(x509->cert_info->key->public_key->data, x509->cert_info->key->public_key->length, sl@0: sha1hash); sl@0: aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH); sl@0: done = true; sl@0: } sl@0: else sl@0: { sl@0: // Calculate SubjectKeyId via Symbian algorithm sl@0: prog << Log::Indent() << "Calculating SubjectKeyId using Symbian algorithm" << Log::Endl(); sl@0: EVP_PKEY *key = X509_PUBKEY_get(x509->cert_info->key); sl@0: if(!key) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to decode certificate public key" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: switch(key->type) sl@0: { sl@0: case EVP_PKEY_RSA: sl@0: { sl@0: TUint32 len = key->pkey.rsa->n->top*sizeof(BN_ULONG); sl@0: TUint8 *buf = new TUint8[len]; sl@0: for(TUint32 i=0; ipkey.rsa->n->d)[len-i-1]; sl@0: } sl@0: sl@0: unsigned char sha1hash[SHA_DIGEST_LENGTH]; sl@0: SHA1(buf, len, sha1hash); sl@0: delete [] buf; sl@0: aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH); sl@0: done = true; sl@0: break; sl@0: } sl@0: case EVP_PKEY_DSA: sl@0: { sl@0: TUint32 len = key->pkey.dsa->pub_key->top*sizeof(BN_ULONG); sl@0: TUint8 *buf = new TUint8[len]; sl@0: for(TUint32 i=0; ipkey.dsa->pub_key->d)[len-i-1]; sl@0: } sl@0: sl@0: unsigned char sha1hash[SHA_DIGEST_LENGTH]; sl@0: SHA1(buf, len, sha1hash); sl@0: delete [] buf; sl@0: aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH); sl@0: done = true; sl@0: break; sl@0: } sl@0: default: sl@0: // Unknown public key type. sl@0: prog << Log::Indent() << "Unknown public key type " << key->type << Log::Endl(); sl@0: break; sl@0: } sl@0: sl@0: EVP_PKEY_free(key); sl@0: } sl@0: } sl@0: sl@0: X509_free(x509); sl@0: return done; sl@0: } sl@0: sl@0: sl@0: bool X509IssuerKeyId(EUseCertificateExtension aUseExtension, sl@0: const TUint8 *aCert, TUint32 aCertLength, sl@0: std::string &aIssuer, TKeyIdentifier &aIssuerKeyId) sl@0: { sl@0: prog << Log::Indent() << "X509IssuerKeyId :-" << Log::Endl(); sl@0: AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope sl@0: bool done = false; sl@0: sl@0: // decode DER certificate into X509 structure sl@0: D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert; sl@0: X509 *x509 = d2i_X509(NULL, &p, aCertLength); sl@0: if(!x509 || (p != aCert+aCertLength)) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: // Return the Subject Name sl@0: prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl(); sl@0: char *issuerOne = X509_NAME_oneline(X509_get_issuer_name(x509),0,0); sl@0: prog << Log::Indent() << "Cert issuer is '" << issuerOne << "'" << Log::Endl(); sl@0: aIssuer = issuerOne; sl@0: OPENSSL_free(issuerOne); sl@0: sl@0: if(aUseExtension) sl@0: { sl@0: // Attempt to read Subject Key Id extension sl@0: AUTHORITY_KEYID *authKeyId = (AUTHORITY_KEYID *) X509_get_ext_d2i(x509, NID_authority_key_identifier, NULL, NULL); sl@0: if(authKeyId) sl@0: { sl@0: prog << Log::Indent() << "Found AuthorityKeyId extension" << Log::Endl(); sl@0: if(authKeyId->keyid) sl@0: { sl@0: if(authKeyId->keyid->length <= aIssuerKeyId.MaxLength()) sl@0: { sl@0: aIssuerKeyId = TPtrC8(authKeyId->keyid->data, authKeyId->keyid->length); sl@0: done = true; sl@0: } sl@0: else sl@0: { sl@0: prog << Log::Indent() << "but AuthroityKeyId > 160 bits so ignoring it" << Log::Endl(); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: prog << Log::Indent() << "but it does not include a key id, so ignoring it" << Log::Endl(); sl@0: } sl@0: sl@0: AUTHORITY_KEYID_free(authKeyId); sl@0: } sl@0: } sl@0: sl@0: X509_free(x509); sl@0: return done; sl@0: } sl@0: sl@0: void Der2Pem(const std::string &aDerCert, std::string &aPemCert) sl@0: { sl@0: prog << Log::Indent() << "Converting DER to PEM:-" << Log::Endl(); sl@0: AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope sl@0: sl@0: // decode DER certificate into X509 structure sl@0: D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aDerCert.data(); sl@0: X509 *x509 = d2i_X509(NULL, &p, aDerCert.size()); sl@0: if(!x509 || ((const char *)p != aDerCert.data()+aDerCert.size())) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: BIO *memBio = BIO_new(BIO_s_mem()); sl@0: BULLSEYE_OFF sl@0: if(!memBio) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to create BIO" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: if(!PEM_write_bio_X509(memBio, x509)) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to convert to PEM" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: BULLSEYE_RESTORE sl@0: sl@0: long pemCertLen = 0; sl@0: char *pemCertData = 0; sl@0: pemCertLen = BIO_get_mem_data(memBio, &pemCertData); sl@0: sl@0: // Return the PEM cert sl@0: aPemCert.assign(pemCertData, pemCertLen); sl@0: sl@0: BIO_free(memBio); sl@0: sl@0: X509_free(x509); sl@0: sl@0: prog << Log::Indent() << "Conversion ok" << Log::Endl(); sl@0: return; sl@0: } sl@0: sl@0: static const char utf8Header[] = sl@0: { sl@0: 0xef, 0xbb, 0xbf sl@0: }; sl@0: sl@0: bool Pem2Der(const std::string &aPemCert, std::string &aDerCert) sl@0: { sl@0: prog << Log::Indent() << "Try PEM to DER coversion :-" << Log::Endl(); sl@0: AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope sl@0: sl@0: TUint32 pemLength=aPemCert.size(); sl@0: const char *pemData=aPemCert.data(); sl@0: sl@0: if((pemLength >= 3) && (memcmp(aPemCert.data(), utf8Header, sizeof(utf8Header)) == 0)) sl@0: { sl@0: // PEM cert has a UTF8 header, so strip it sl@0: prog << Log::Indent() << "Certificate data file has a UTF-8 header" << Log::Endl(); sl@0: pemLength -= sizeof(utf8Header); sl@0: pemData += sizeof(utf8Header); sl@0: } sl@0: sl@0: // sl@0: // Read PEM to internal sl@0: // sl@0: BIO *memBioIn = BIO_new_mem_buf((void *)pemData, pemLength); sl@0: BULLSEYE_OFF sl@0: if(!memBioIn) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to create BIO for reading PEM" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: BULLSEYE_RESTORE sl@0: sl@0: X509 *x509 = PEM_read_bio_X509(memBioIn, NULL, 0, NULL); sl@0: if(!x509) sl@0: { sl@0: prog << Log::Indent() << "Conversion failed - presumably DER" << Log::Endl(); sl@0: return false; sl@0: } sl@0: BIO_free(memBioIn); sl@0: memBioIn = 0; sl@0: sl@0: // sl@0: // Write internal to DER sl@0: // sl@0: unsigned char *derCert = 0; sl@0: int derLen = i2d_X509(x509, &derCert); sl@0: if(derLen <=0 ) sl@0: { sl@0: dbg << Log::Indent() << "openssl failed to convert to DER" << Log::Endl(); sl@0: FatalError(); sl@0: } sl@0: sl@0: // Return the DER cert sl@0: aDerCert.assign((char *)derCert, derLen); sl@0: sl@0: X509_free(x509); sl@0: prog << Log::Indent() << "Conversion ok" << Log::Endl(); sl@0: return true; sl@0: } sl@0: sl@0: sl@0: sl@0: // End of file