os/security/securityanddataprivacytools/securitytools/certapp/encdec/x509utils.cpp
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/security/securityanddataprivacytools/securitytools/certapp/encdec/x509utils.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,481 @@
1.4 +/*
1.5 +* Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
1.6 +* All rights reserved.
1.7 +* This component and the accompanying materials are made available
1.8 +* under the terms of the License "Eclipse Public License v1.0"
1.9 +* which accompanies this distribution, and is available
1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.11 +*
1.12 +* Initial Contributors:
1.13 +* Nokia Corporation - initial contribution.
1.14 +*
1.15 +* Contributors:
1.16 +*
1.17 +* Description:
1.18 +*
1.19 +*/
1.20 +
1.21 +
1.22 +#include "openssl/x509.h"
1.23 +#include "openssl/x509v3.h"
1.24 +#include "openssl/pem.h"
1.25 +#include "encdec.h"
1.26 +#include "x509utils.h"
1.27 +#include "logger.h"
1.28 +//
1.29 +// TKeyIdentifier
1.30 +//
1.31 +void EncodeHuman(REncodeWriteStream& aStream,const KeyIdentifierObject &aKeyId)
1.32 +{
1.33 + if(aKeyId.iAutoKey)
1.34 + {
1.35 + aStream.WriteCStr("auto");
1.36 + if(aKeyId.iHash.Length() == 0)
1.37 + {
1.38 + return; // Empty value so no point in including it in a comment...
1.39 + }
1.40 + if(!aStream.Verbose())
1.41 + {
1.42 + return; // auto, and not in verbose mode so do not write value in comment
1.43 + }
1.44 +
1.45 + aStream.WriteCStr(" # ");
1.46 + }
1.47 + aStream.WriteByte('\'');
1.48 + const TUint8 *ptr = aKeyId.iHash.Ptr();
1.49 + TInt len = aKeyId.iHash.Length();
1.50 + while(len--)
1.51 + {
1.52 + TUint8 byte = *ptr++;
1.53 + TUint8 buf[2];
1.54 +
1.55 + TUint8 ch = ((byte & 0xf0) >> 4);
1.56 + ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
1.57 +
1.58 + // Write MSB char of byte
1.59 + buf[0] = ch;
1.60 +
1.61 + ch = (byte & 0x0f);
1.62 + ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
1.63 +
1.64 + // Write LSB char of byte
1.65 + buf[1] = ch;
1.66 +
1.67 + aStream.WriteBin(buf, sizeof(buf));
1.68 + if(len)
1.69 + {
1.70 + aStream.WriteByte(':');
1.71 + }
1.72 +
1.73 + }
1.74 + aStream.WriteByte('\'');
1.75 +}
1.76 +void DecodeHuman(RDecodeReadStream& aStream, KeyIdentifierObject &aKeyId)
1.77 +{
1.78 + aStream.ReadNextToken();
1.79 + std::string tok = aStream.Token();
1.80 +
1.81 + if(tok == "auto")
1.82 + {
1.83 + aKeyId.iAutoKey = true;
1.84 + aKeyId.iHash.SetLength(0);
1.85 + return;
1.86 + }
1.87 + aKeyId.iAutoKey = false;
1.88 +
1.89 + if((tok[0] != '\'') || (tok[tok.size()-1] != '\'') || (tok.size() < 2))
1.90 + {
1.91 + dbg << Log::Indent() << "KeyIdentifier not enclosed in single quotes, or contains spaces - " << tok << Log::Endl();
1.92 + FatalError();
1.93 + }
1.94 +
1.95 + tok.erase(0,1);
1.96 + tok.erase(tok.size()-1,1);
1.97 +
1.98 + if(tok.size() == 0)
1.99 + {
1.100 + aKeyId.iHash.SetLength(0);
1.101 + return;
1.102 + }
1.103 +
1.104 + if(TInt(tok.size()) != (aKeyId.iHash.MaxLength()*2) + (aKeyId.iHash.MaxLength()-1))
1.105 + {
1.106 + dbg << Log::Indent() << "WARNING: KeyIdentifier length not " << aKeyId.iHash.MaxLength()*2 << " hex digits" << Log::Endl();
1.107 + dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
1.108 + }
1.109 +
1.110 + bool bad = false;
1.111 + TInt bytesRead = 0;
1.112 + const char *hexDigit = tok.data();
1.113 + TInt charsToRead = tok.size();
1.114 + TUint8 *dest = const_cast<TUint8 *>(aKeyId.iHash.Ptr());
1.115 + while(charsToRead)
1.116 + {
1.117 + // Read MSB char
1.118 + TUint8 byte = fromHex(*hexDigit++);
1.119 + byte <<= 4;
1.120 + --charsToRead;
1.121 +
1.122 + // Read LSB char
1.123 + if(charsToRead == 0)
1.124 + {
1.125 + bad = true;
1.126 + break;
1.127 + }
1.128 + byte |= fromHex(*hexDigit++);
1.129 + --charsToRead;
1.130 +
1.131 + // Save decoded byte
1.132 + *dest++ = byte;
1.133 + ++bytesRead;
1.134 +
1.135 + if(charsToRead != 0)
1.136 + {
1.137 + // Consume : separator
1.138 + if(*hexDigit++ != ':')
1.139 + {
1.140 + bad = true;
1.141 + break;
1.142 + }
1.143 + --charsToRead;
1.144 + }
1.145 + }
1.146 +
1.147 + if(bytesRead > aKeyId.iHash.MaxLength())
1.148 + {
1.149 + dbg << Log::Indent() << "Key Identifiier is too long" << Log::Endl();
1.150 + bad = true;
1.151 + }
1.152 +
1.153 +
1.154 + if(bad)
1.155 + {
1.156 + 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();
1.157 + dbg << Log::Indent() << "This field should normally be set to auto or omitted" << Log::Endl();
1.158 + dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
1.159 + FatalError();
1.160 + }
1.161 +
1.162 + aKeyId.iHash.SetLength(bytesRead);
1.163 +
1.164 +
1.165 + return;
1.166 +}
1.167 +
1.168 +RWriteStream& operator<<(RWriteStream& aStream,const KeyIdentifierObject& aKeyId)
1.169 +{
1.170 + aStream << aKeyId.iHash;
1.171 + return aStream;
1.172 +}
1.173 +
1.174 +RReadStream& operator>>(RReadStream& aStream, KeyIdentifierObject& aKeyId)
1.175 +{
1.176 + aKeyId.iAutoKey = false;
1.177 + aStream >> aKeyId.iHash;
1.178 + return aStream;
1.179 +}
1.180 +
1.181 +// It is illegal to pass a "X **" ptr to a function taking a "const X
1.182 +// **" argument. This is because the function could change the callers
1.183 +// pointer to point at a const object which the caller might then
1.184 +// accidentally write to!
1.185 +//
1.186 +// Unfortunately openssl 0.9.7* defines d2i_X509 to take an "unsigned
1.187 +// char **" and 0.9.8 takes "const unsigned char **", so neither
1.188 +// caller choice will compile for both....
1.189 +
1.190 +#if OPENSSL_VERSION_NUMBER >= 0x00908000L
1.191 +#define D2I_CONST const
1.192 +#else
1.193 +#define D2I_CONST
1.194 +#endif
1.195 +
1.196 +bool X509SubjectKeyId(EUseCertificateExtension aUseExtension, bool aUseRfc3280Algorithm,
1.197 + bool aIsCa, const std::string &aCert,
1.198 + std::string &aSubject, TKeyIdentifier &aSubjectKeyId)
1.199 +{
1.200 + bool done = false;
1.201 + prog << Log::Indent() << "X509SubjectKeyId - aUseExtension " << aUseExtension << " aUseRfc3280Algorithm " << aUseRfc3280Algorithm << " :-" << Log::Endl();
1.202 + AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
1.203 +
1.204 + // decode DER certificate into X509 structure
1.205 + D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert.data();
1.206 + X509 *x509 = d2i_X509(NULL, &p, aCert.size());
1.207 + if(!x509 || ((const char *)p != aCert.data() + aCert.size()))
1.208 + {
1.209 + dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
1.210 + FatalError();
1.211 + }
1.212 +
1.213 + // Return the Subject Name
1.214 + prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
1.215 + aSubject = std::string(x509->name);
1.216 + TUint32 ver = X509_get_version(x509);
1.217 + prog << Log::Indent() << "Cert version is '" << ver << "'" << Log::Endl();
1.218 +
1.219 + // 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.
1.220 + bool treatAsCa = false;
1.221 + if ( ver < 3 || aIsCa )
1.222 + {
1.223 + treatAsCa = true;
1.224 + }
1.225 +
1.226 + if(treatAsCa && aUseExtension)
1.227 + {
1.228 + // Attempt to read Subject Key Id extension
1.229 + ASN1_OCTET_STRING *subKeyId = (ASN1_OCTET_STRING *) X509_get_ext_d2i(x509, NID_subject_key_identifier, NULL, NULL);
1.230 + if(subKeyId)
1.231 + {
1.232 + prog << Log::Indent() << "Found SubjectKeyId extension" << Log::Endl();
1.233 + if(subKeyId->length <= aSubjectKeyId.MaxLength())
1.234 + {
1.235 + aSubjectKeyId = TPtrC8(subKeyId->data, subKeyId->length);
1.236 + done = true;
1.237 + }
1.238 + else
1.239 + {
1.240 + prog << Log::Indent() << "but SubjectKeyId > 160 bits so ignoring it" << Log::Endl();
1.241 + }
1.242 + ASN1_OCTET_STRING_free(subKeyId);
1.243 + }
1.244 + }
1.245 +
1.246 + if(!done)
1.247 + {
1.248 + // Subject Key Id extension was ignored, missing or too long...
1.249 + if(aUseRfc3280Algorithm)
1.250 + {
1.251 + // We do not need to decode the public key just hash its
1.252 + // data as per rfc3280 4.2.1.2 method 1
1.253 + prog << Log::Indent() << "Calculating SubjectKeyId using RFC3280 4.2.1.2 method 1" << Log::Endl();
1.254 + unsigned char sha1hash[SHA_DIGEST_LENGTH];
1.255 +
1.256 + SHA1(x509->cert_info->key->public_key->data, x509->cert_info->key->public_key->length,
1.257 + sha1hash);
1.258 + aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
1.259 + done = true;
1.260 + }
1.261 + else
1.262 + {
1.263 + // Calculate SubjectKeyId via Symbian algorithm
1.264 + prog << Log::Indent() << "Calculating SubjectKeyId using Symbian algorithm" << Log::Endl();
1.265 + EVP_PKEY *key = X509_PUBKEY_get(x509->cert_info->key);
1.266 + if(!key)
1.267 + {
1.268 + dbg << Log::Indent() << "openssl failed to decode certificate public key" << Log::Endl();
1.269 + FatalError();
1.270 + }
1.271 +
1.272 + switch(key->type)
1.273 + {
1.274 + case EVP_PKEY_RSA:
1.275 + {
1.276 + TUint32 len = key->pkey.rsa->n->top*sizeof(BN_ULONG);
1.277 + TUint8 *buf = new TUint8[len];
1.278 + for(TUint32 i=0; i<len; ++i)
1.279 + {
1.280 + buf[i] = ((TUint8 *)key->pkey.rsa->n->d)[len-i-1];
1.281 + }
1.282 +
1.283 + unsigned char sha1hash[SHA_DIGEST_LENGTH];
1.284 + SHA1(buf, len, sha1hash);
1.285 + delete [] buf;
1.286 + aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
1.287 + done = true;
1.288 + break;
1.289 + }
1.290 + case EVP_PKEY_DSA:
1.291 + {
1.292 + TUint32 len = key->pkey.dsa->pub_key->top*sizeof(BN_ULONG);
1.293 + TUint8 *buf = new TUint8[len];
1.294 + for(TUint32 i=0; i<len; ++i)
1.295 + {
1.296 + buf[i] = ((TUint8 *)key->pkey.dsa->pub_key->d)[len-i-1];
1.297 + }
1.298 +
1.299 + unsigned char sha1hash[SHA_DIGEST_LENGTH];
1.300 + SHA1(buf, len, sha1hash);
1.301 + delete [] buf;
1.302 + aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
1.303 + done = true;
1.304 + break;
1.305 + }
1.306 + default:
1.307 + // Unknown public key type.
1.308 + prog << Log::Indent() << "Unknown public key type " << key->type << Log::Endl();
1.309 + break;
1.310 + }
1.311 +
1.312 + EVP_PKEY_free(key);
1.313 + }
1.314 + }
1.315 +
1.316 + X509_free(x509);
1.317 + return done;
1.318 +}
1.319 +
1.320 +
1.321 +bool X509IssuerKeyId(EUseCertificateExtension aUseExtension,
1.322 + const TUint8 *aCert, TUint32 aCertLength,
1.323 + std::string &aIssuer, TKeyIdentifier &aIssuerKeyId)
1.324 +{
1.325 + prog << Log::Indent() << "X509IssuerKeyId :-" << Log::Endl();
1.326 + AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
1.327 + bool done = false;
1.328 +
1.329 + // decode DER certificate into X509 structure
1.330 + D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert;
1.331 + X509 *x509 = d2i_X509(NULL, &p, aCertLength);
1.332 + if(!x509 || (p != aCert+aCertLength))
1.333 + {
1.334 + dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
1.335 + FatalError();
1.336 + }
1.337 +
1.338 + // Return the Subject Name
1.339 + prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
1.340 + char *issuerOne = X509_NAME_oneline(X509_get_issuer_name(x509),0,0);
1.341 + prog << Log::Indent() << "Cert issuer is '" << issuerOne << "'" << Log::Endl();
1.342 + aIssuer = issuerOne;
1.343 + OPENSSL_free(issuerOne);
1.344 +
1.345 + if(aUseExtension)
1.346 + {
1.347 + // Attempt to read Subject Key Id extension
1.348 + AUTHORITY_KEYID *authKeyId = (AUTHORITY_KEYID *) X509_get_ext_d2i(x509, NID_authority_key_identifier, NULL, NULL);
1.349 + if(authKeyId)
1.350 + {
1.351 + prog << Log::Indent() << "Found AuthorityKeyId extension" << Log::Endl();
1.352 + if(authKeyId->keyid)
1.353 + {
1.354 + if(authKeyId->keyid->length <= aIssuerKeyId.MaxLength())
1.355 + {
1.356 + aIssuerKeyId = TPtrC8(authKeyId->keyid->data, authKeyId->keyid->length);
1.357 + done = true;
1.358 + }
1.359 + else
1.360 + {
1.361 + prog << Log::Indent() << "but AuthroityKeyId > 160 bits so ignoring it" << Log::Endl();
1.362 + }
1.363 + }
1.364 + else
1.365 + {
1.366 + prog << Log::Indent() << "but it does not include a key id, so ignoring it" << Log::Endl();
1.367 + }
1.368 +
1.369 + AUTHORITY_KEYID_free(authKeyId);
1.370 + }
1.371 + }
1.372 +
1.373 + X509_free(x509);
1.374 + return done;
1.375 +}
1.376 +
1.377 +void Der2Pem(const std::string &aDerCert, std::string &aPemCert)
1.378 +{
1.379 + prog << Log::Indent() << "Converting DER to PEM:-" << Log::Endl();
1.380 + AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
1.381 +
1.382 + // decode DER certificate into X509 structure
1.383 + D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aDerCert.data();
1.384 + X509 *x509 = d2i_X509(NULL, &p, aDerCert.size());
1.385 + if(!x509 || ((const char *)p != aDerCert.data()+aDerCert.size()))
1.386 + {
1.387 + dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
1.388 + FatalError();
1.389 + }
1.390 +
1.391 + BIO *memBio = BIO_new(BIO_s_mem());
1.392 +BULLSEYE_OFF
1.393 + if(!memBio)
1.394 + {
1.395 + dbg << Log::Indent() << "openssl failed to create BIO" << Log::Endl();
1.396 + FatalError();
1.397 + }
1.398 +
1.399 + if(!PEM_write_bio_X509(memBio, x509))
1.400 + {
1.401 + dbg << Log::Indent() << "openssl failed to convert to PEM" << Log::Endl();
1.402 + FatalError();
1.403 + }
1.404 +BULLSEYE_RESTORE
1.405 +
1.406 + long pemCertLen = 0;
1.407 + char *pemCertData = 0;
1.408 + pemCertLen = BIO_get_mem_data(memBio, &pemCertData);
1.409 +
1.410 + // Return the PEM cert
1.411 + aPemCert.assign(pemCertData, pemCertLen);
1.412 +
1.413 + BIO_free(memBio);
1.414 +
1.415 + X509_free(x509);
1.416 +
1.417 + prog << Log::Indent() << "Conversion ok" << Log::Endl();
1.418 + return;
1.419 +}
1.420 +
1.421 +static const char utf8Header[] =
1.422 + {
1.423 + 0xef, 0xbb, 0xbf
1.424 + };
1.425 +
1.426 +bool Pem2Der(const std::string &aPemCert, std::string &aDerCert)
1.427 +{
1.428 + prog << Log::Indent() << "Try PEM to DER coversion :-" << Log::Endl();
1.429 + AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
1.430 +
1.431 + TUint32 pemLength=aPemCert.size();
1.432 + const char *pemData=aPemCert.data();
1.433 +
1.434 + if((pemLength >= 3) && (memcmp(aPemCert.data(), utf8Header, sizeof(utf8Header)) == 0))
1.435 + {
1.436 + // PEM cert has a UTF8 header, so strip it
1.437 + prog << Log::Indent() << "Certificate data file has a UTF-8 header" << Log::Endl();
1.438 + pemLength -= sizeof(utf8Header);
1.439 + pemData += sizeof(utf8Header);
1.440 + }
1.441 +
1.442 + //
1.443 + // Read PEM to internal
1.444 + //
1.445 + BIO *memBioIn = BIO_new_mem_buf((void *)pemData, pemLength);
1.446 +BULLSEYE_OFF
1.447 + if(!memBioIn)
1.448 + {
1.449 + dbg << Log::Indent() << "openssl failed to create BIO for reading PEM" << Log::Endl();
1.450 + FatalError();
1.451 + }
1.452 +BULLSEYE_RESTORE
1.453 +
1.454 + X509 *x509 = PEM_read_bio_X509(memBioIn, NULL, 0, NULL);
1.455 + if(!x509)
1.456 + {
1.457 + prog << Log::Indent() << "Conversion failed - presumably DER" << Log::Endl();
1.458 + return false;
1.459 + }
1.460 + BIO_free(memBioIn);
1.461 + memBioIn = 0;
1.462 +
1.463 + //
1.464 + // Write internal to DER
1.465 + //
1.466 + unsigned char *derCert = 0;
1.467 + int derLen = i2d_X509(x509, &derCert);
1.468 + if(derLen <=0 )
1.469 + {
1.470 + dbg << Log::Indent() << "openssl failed to convert to DER" << Log::Endl();
1.471 + FatalError();
1.472 + }
1.473 +
1.474 + // Return the DER cert
1.475 + aDerCert.assign((char *)derCert, derLen);
1.476 +
1.477 + X509_free(x509);
1.478 + prog << Log::Indent() << "Conversion ok" << Log::Endl();
1.479 + return true;
1.480 +}
1.481 +
1.482 +
1.483 +
1.484 +// End of file