os/security/securityanddataprivacytools/securitytools/certapp/encdec/x509utils.cpp
First public contribution.
2 * Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
19 #include "openssl/x509.h"
20 #include "openssl/x509v3.h"
21 #include "openssl/pem.h"
23 #include "x509utils.h"
28 void EncodeHuman(REncodeWriteStream& aStream,const KeyIdentifierObject &aKeyId)
32 aStream.WriteCStr("auto");
33 if(aKeyId.iHash.Length() == 0)
35 return; // Empty value so no point in including it in a comment...
37 if(!aStream.Verbose())
39 return; // auto, and not in verbose mode so do not write value in comment
42 aStream.WriteCStr(" # ");
44 aStream.WriteByte('\'');
45 const TUint8 *ptr = aKeyId.iHash.Ptr();
46 TInt len = aKeyId.iHash.Length();
52 TUint8 ch = ((byte & 0xf0) >> 4);
53 ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
55 // Write MSB char of byte
59 ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
61 // Write LSB char of byte
64 aStream.WriteBin(buf, sizeof(buf));
67 aStream.WriteByte(':');
71 aStream.WriteByte('\'');
73 void DecodeHuman(RDecodeReadStream& aStream, KeyIdentifierObject &aKeyId)
75 aStream.ReadNextToken();
76 std::string tok = aStream.Token();
80 aKeyId.iAutoKey = true;
81 aKeyId.iHash.SetLength(0);
84 aKeyId.iAutoKey = false;
86 if((tok[0] != '\'') || (tok[tok.size()-1] != '\'') || (tok.size() < 2))
88 dbg << Log::Indent() << "KeyIdentifier not enclosed in single quotes, or contains spaces - " << tok << Log::Endl();
93 tok.erase(tok.size()-1,1);
97 aKeyId.iHash.SetLength(0);
101 if(TInt(tok.size()) != (aKeyId.iHash.MaxLength()*2) + (aKeyId.iHash.MaxLength()-1))
103 dbg << Log::Indent() << "WARNING: KeyIdentifier length not " << aKeyId.iHash.MaxLength()*2 << " hex digits" << Log::Endl();
104 dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
109 const char *hexDigit = tok.data();
110 TInt charsToRead = tok.size();
111 TUint8 *dest = const_cast<TUint8 *>(aKeyId.iHash.Ptr());
115 TUint8 byte = fromHex(*hexDigit++);
125 byte |= fromHex(*hexDigit++);
134 // Consume : separator
135 if(*hexDigit++ != ':')
144 if(bytesRead > aKeyId.iHash.MaxLength())
146 dbg << Log::Indent() << "Key Identifiier is too long" << Log::Endl();
153 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();
154 dbg << Log::Indent() << "This field should normally be set to auto or omitted" << Log::Endl();
155 dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
159 aKeyId.iHash.SetLength(bytesRead);
165 RWriteStream& operator<<(RWriteStream& aStream,const KeyIdentifierObject& aKeyId)
167 aStream << aKeyId.iHash;
171 RReadStream& operator>>(RReadStream& aStream, KeyIdentifierObject& aKeyId)
173 aKeyId.iAutoKey = false;
174 aStream >> aKeyId.iHash;
178 // It is illegal to pass a "X **" ptr to a function taking a "const X
179 // **" argument. This is because the function could change the callers
180 // pointer to point at a const object which the caller might then
181 // accidentally write to!
183 // Unfortunately openssl 0.9.7* defines d2i_X509 to take an "unsigned
184 // char **" and 0.9.8 takes "const unsigned char **", so neither
185 // caller choice will compile for both....
187 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
188 #define D2I_CONST const
193 bool X509SubjectKeyId(EUseCertificateExtension aUseExtension, bool aUseRfc3280Algorithm,
194 bool aIsCa, const std::string &aCert,
195 std::string &aSubject, TKeyIdentifier &aSubjectKeyId)
198 prog << Log::Indent() << "X509SubjectKeyId - aUseExtension " << aUseExtension << " aUseRfc3280Algorithm " << aUseRfc3280Algorithm << " :-" << Log::Endl();
199 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
201 // decode DER certificate into X509 structure
202 D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert.data();
203 X509 *x509 = d2i_X509(NULL, &p, aCert.size());
204 if(!x509 || ((const char *)p != aCert.data() + aCert.size()))
206 dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
210 // Return the Subject Name
211 prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
212 aSubject = std::string(x509->name);
213 TUint32 ver = X509_get_version(x509);
214 prog << Log::Indent() << "Cert version is '" << ver << "'" << Log::Endl();
216 // 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.
217 bool treatAsCa = false;
218 if ( ver < 3 || aIsCa )
223 if(treatAsCa && aUseExtension)
225 // Attempt to read Subject Key Id extension
226 ASN1_OCTET_STRING *subKeyId = (ASN1_OCTET_STRING *) X509_get_ext_d2i(x509, NID_subject_key_identifier, NULL, NULL);
229 prog << Log::Indent() << "Found SubjectKeyId extension" << Log::Endl();
230 if(subKeyId->length <= aSubjectKeyId.MaxLength())
232 aSubjectKeyId = TPtrC8(subKeyId->data, subKeyId->length);
237 prog << Log::Indent() << "but SubjectKeyId > 160 bits so ignoring it" << Log::Endl();
239 ASN1_OCTET_STRING_free(subKeyId);
245 // Subject Key Id extension was ignored, missing or too long...
246 if(aUseRfc3280Algorithm)
248 // We do not need to decode the public key just hash its
249 // data as per rfc3280 4.2.1.2 method 1
250 prog << Log::Indent() << "Calculating SubjectKeyId using RFC3280 4.2.1.2 method 1" << Log::Endl();
251 unsigned char sha1hash[SHA_DIGEST_LENGTH];
253 SHA1(x509->cert_info->key->public_key->data, x509->cert_info->key->public_key->length,
255 aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
260 // Calculate SubjectKeyId via Symbian algorithm
261 prog << Log::Indent() << "Calculating SubjectKeyId using Symbian algorithm" << Log::Endl();
262 EVP_PKEY *key = X509_PUBKEY_get(x509->cert_info->key);
265 dbg << Log::Indent() << "openssl failed to decode certificate public key" << Log::Endl();
273 TUint32 len = key->pkey.rsa->n->top*sizeof(BN_ULONG);
274 TUint8 *buf = new TUint8[len];
275 for(TUint32 i=0; i<len; ++i)
277 buf[i] = ((TUint8 *)key->pkey.rsa->n->d)[len-i-1];
280 unsigned char sha1hash[SHA_DIGEST_LENGTH];
281 SHA1(buf, len, sha1hash);
283 aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
289 TUint32 len = key->pkey.dsa->pub_key->top*sizeof(BN_ULONG);
290 TUint8 *buf = new TUint8[len];
291 for(TUint32 i=0; i<len; ++i)
293 buf[i] = ((TUint8 *)key->pkey.dsa->pub_key->d)[len-i-1];
296 unsigned char sha1hash[SHA_DIGEST_LENGTH];
297 SHA1(buf, len, sha1hash);
299 aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
304 // Unknown public key type.
305 prog << Log::Indent() << "Unknown public key type " << key->type << Log::Endl();
318 bool X509IssuerKeyId(EUseCertificateExtension aUseExtension,
319 const TUint8 *aCert, TUint32 aCertLength,
320 std::string &aIssuer, TKeyIdentifier &aIssuerKeyId)
322 prog << Log::Indent() << "X509IssuerKeyId :-" << Log::Endl();
323 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
326 // decode DER certificate into X509 structure
327 D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert;
328 X509 *x509 = d2i_X509(NULL, &p, aCertLength);
329 if(!x509 || (p != aCert+aCertLength))
331 dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
335 // Return the Subject Name
336 prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
337 char *issuerOne = X509_NAME_oneline(X509_get_issuer_name(x509),0,0);
338 prog << Log::Indent() << "Cert issuer is '" << issuerOne << "'" << Log::Endl();
340 OPENSSL_free(issuerOne);
344 // Attempt to read Subject Key Id extension
345 AUTHORITY_KEYID *authKeyId = (AUTHORITY_KEYID *) X509_get_ext_d2i(x509, NID_authority_key_identifier, NULL, NULL);
348 prog << Log::Indent() << "Found AuthorityKeyId extension" << Log::Endl();
351 if(authKeyId->keyid->length <= aIssuerKeyId.MaxLength())
353 aIssuerKeyId = TPtrC8(authKeyId->keyid->data, authKeyId->keyid->length);
358 prog << Log::Indent() << "but AuthroityKeyId > 160 bits so ignoring it" << Log::Endl();
363 prog << Log::Indent() << "but it does not include a key id, so ignoring it" << Log::Endl();
366 AUTHORITY_KEYID_free(authKeyId);
374 void Der2Pem(const std::string &aDerCert, std::string &aPemCert)
376 prog << Log::Indent() << "Converting DER to PEM:-" << Log::Endl();
377 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
379 // decode DER certificate into X509 structure
380 D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aDerCert.data();
381 X509 *x509 = d2i_X509(NULL, &p, aDerCert.size());
382 if(!x509 || ((const char *)p != aDerCert.data()+aDerCert.size()))
384 dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
388 BIO *memBio = BIO_new(BIO_s_mem());
392 dbg << Log::Indent() << "openssl failed to create BIO" << Log::Endl();
396 if(!PEM_write_bio_X509(memBio, x509))
398 dbg << Log::Indent() << "openssl failed to convert to PEM" << Log::Endl();
404 char *pemCertData = 0;
405 pemCertLen = BIO_get_mem_data(memBio, &pemCertData);
407 // Return the PEM cert
408 aPemCert.assign(pemCertData, pemCertLen);
414 prog << Log::Indent() << "Conversion ok" << Log::Endl();
418 static const char utf8Header[] =
423 bool Pem2Der(const std::string &aPemCert, std::string &aDerCert)
425 prog << Log::Indent() << "Try PEM to DER coversion :-" << Log::Endl();
426 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
428 TUint32 pemLength=aPemCert.size();
429 const char *pemData=aPemCert.data();
431 if((pemLength >= 3) && (memcmp(aPemCert.data(), utf8Header, sizeof(utf8Header)) == 0))
433 // PEM cert has a UTF8 header, so strip it
434 prog << Log::Indent() << "Certificate data file has a UTF-8 header" << Log::Endl();
435 pemLength -= sizeof(utf8Header);
436 pemData += sizeof(utf8Header);
440 // Read PEM to internal
442 BIO *memBioIn = BIO_new_mem_buf((void *)pemData, pemLength);
446 dbg << Log::Indent() << "openssl failed to create BIO for reading PEM" << Log::Endl();
451 X509 *x509 = PEM_read_bio_X509(memBioIn, NULL, 0, NULL);
454 prog << Log::Indent() << "Conversion failed - presumably DER" << Log::Endl();
461 // Write internal to DER
463 unsigned char *derCert = 0;
464 int derLen = i2d_X509(x509, &derCert);
467 dbg << Log::Indent() << "openssl failed to convert to DER" << Log::Endl();
471 // Return the DER cert
472 aDerCert.assign((char *)derCert, derLen);
475 prog << Log::Indent() << "Conversion ok" << Log::Endl();