|
1 /* |
|
2 * Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
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". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include <openssl/x509.h> |
|
20 #include <openssl/x509v3.h> |
|
21 #include <openssl/pem.h> |
|
22 #include "encdec.h" |
|
23 #include "x509utils.h" |
|
24 #include "logger.h" |
|
25 // |
|
26 // TKeyIdentifier |
|
27 // |
|
28 void EncodeHuman(REncodeWriteStream& aStream,const KeyIdentifierObject &aKeyId) |
|
29 { |
|
30 if(aKeyId.iAutoKey) |
|
31 { |
|
32 aStream.WriteCStr("auto"); |
|
33 if(aKeyId.iHash.Length() == 0) |
|
34 { |
|
35 return; // Empty value so no point in including it in a comment... |
|
36 } |
|
37 if(!aStream.Verbose()) |
|
38 { |
|
39 return; // auto, and not in verbose mode so do not write value in comment |
|
40 } |
|
41 |
|
42 aStream.WriteCStr(" # "); |
|
43 } |
|
44 aStream.WriteByte('\''); |
|
45 const TUint8 *ptr = aKeyId.iHash.Ptr(); |
|
46 TInt len = aKeyId.iHash.Length(); |
|
47 while(len--) |
|
48 { |
|
49 TUint8 byte = *ptr++; |
|
50 TUint8 buf[2]; |
|
51 |
|
52 TUint8 ch = ((byte & 0xf0) >> 4); |
|
53 ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A'); |
|
54 |
|
55 // Write MSB char of byte |
|
56 buf[0] = ch; |
|
57 |
|
58 ch = (byte & 0x0f); |
|
59 ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A'); |
|
60 |
|
61 // Write LSB char of byte |
|
62 buf[1] = ch; |
|
63 |
|
64 aStream.WriteBin(buf, sizeof(buf)); |
|
65 if(len) |
|
66 { |
|
67 aStream.WriteByte(':'); |
|
68 } |
|
69 |
|
70 } |
|
71 aStream.WriteByte('\''); |
|
72 } |
|
73 void DecodeHuman(RDecodeReadStream& aStream, KeyIdentifierObject &aKeyId) |
|
74 { |
|
75 aStream.ReadNextToken(); |
|
76 std::string tok = aStream.Token(); |
|
77 |
|
78 if(tok == "auto") |
|
79 { |
|
80 aKeyId.iAutoKey = true; |
|
81 aKeyId.iHash.SetLength(0); |
|
82 return; |
|
83 } |
|
84 aKeyId.iAutoKey = false; |
|
85 |
|
86 if((tok[0] != '\'') || (tok[tok.size()-1] != '\'') || (tok.size() < 2)) |
|
87 { |
|
88 dbg << Log::Indent() << "KeyIdentifier not enclosed in single quotes, or contains spaces - " << tok << Log::Endl(); |
|
89 FatalError(); |
|
90 } |
|
91 |
|
92 tok.erase(0,1); |
|
93 tok.erase(tok.size()-1,1); |
|
94 |
|
95 if(tok.size() == 0) |
|
96 { |
|
97 aKeyId.iHash.SetLength(0); |
|
98 return; |
|
99 } |
|
100 |
|
101 if(TInt(tok.size()) != (aKeyId.iHash.MaxLength()*2) + (aKeyId.iHash.MaxLength()-1)) |
|
102 { |
|
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(); |
|
105 } |
|
106 |
|
107 bool bad = false; |
|
108 TInt bytesRead = 0; |
|
109 const char *hexDigit = tok.data(); |
|
110 TInt charsToRead = tok.size(); |
|
111 TUint8 *dest = const_cast<TUint8 *>(aKeyId.iHash.Ptr()); |
|
112 while(charsToRead) |
|
113 { |
|
114 // Read MSB char |
|
115 TUint8 byte = fromHex(*hexDigit++); |
|
116 byte <<= 4; |
|
117 --charsToRead; |
|
118 |
|
119 // Read LSB char |
|
120 if(charsToRead == 0) |
|
121 { |
|
122 bad = true; |
|
123 break; |
|
124 } |
|
125 byte |= fromHex(*hexDigit++); |
|
126 --charsToRead; |
|
127 |
|
128 // Save decoded byte |
|
129 *dest++ = byte; |
|
130 ++bytesRead; |
|
131 |
|
132 if(charsToRead != 0) |
|
133 { |
|
134 // Consume : separator |
|
135 if(*hexDigit++ != ':') |
|
136 { |
|
137 bad = true; |
|
138 break; |
|
139 } |
|
140 --charsToRead; |
|
141 } |
|
142 } |
|
143 |
|
144 if(bytesRead > aKeyId.iHash.MaxLength()) |
|
145 { |
|
146 dbg << Log::Indent() << "Key Identifiier is too long" << Log::Endl(); |
|
147 bad = true; |
|
148 } |
|
149 |
|
150 |
|
151 if(bad) |
|
152 { |
|
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(); |
|
156 FatalError(); |
|
157 } |
|
158 |
|
159 aKeyId.iHash.SetLength(bytesRead); |
|
160 |
|
161 |
|
162 return; |
|
163 } |
|
164 |
|
165 RWriteStream& operator<<(RWriteStream& aStream,const KeyIdentifierObject& aKeyId) |
|
166 { |
|
167 aStream << aKeyId.iHash; |
|
168 return aStream; |
|
169 } |
|
170 |
|
171 RReadStream& operator>>(RReadStream& aStream, KeyIdentifierObject& aKeyId) |
|
172 { |
|
173 aKeyId.iAutoKey = false; |
|
174 aStream >> aKeyId.iHash; |
|
175 return aStream; |
|
176 } |
|
177 |
|
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! |
|
182 // |
|
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.... |
|
186 |
|
187 #if OPENSSL_VERSION_NUMBER >= 0x00908000L |
|
188 #define D2I_CONST const |
|
189 #else |
|
190 #define D2I_CONST |
|
191 #endif |
|
192 |
|
193 bool X509SubjectKeyId(EUseCertificateExtension aUseExtension, bool aUseRfc3280Algorithm, |
|
194 const std::string &aCert, |
|
195 std::string &aSubject, TKeyIdentifier &aSubjectKeyId) |
|
196 { |
|
197 bool done = false; |
|
198 prog << Log::Indent() << "X509SubjectKeyId - aUseExtension " << aUseExtension << " aUseRfc3280Algorithm " << aUseRfc3280Algorithm << " :-" << Log::Endl(); |
|
199 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope |
|
200 |
|
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())) |
|
205 { |
|
206 dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl(); |
|
207 FatalError(); |
|
208 } |
|
209 |
|
210 // Return the Subject Name |
|
211 prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl(); |
|
212 aSubject = std::string(x509->name); |
|
213 |
|
214 if(aUseExtension) |
|
215 { |
|
216 // Attempt to read Subject Key Id extension |
|
217 ASN1_OCTET_STRING *subKeyId = (ASN1_OCTET_STRING *) X509_get_ext_d2i(x509, NID_subject_key_identifier, NULL, NULL); |
|
218 if(subKeyId) |
|
219 { |
|
220 prog << Log::Indent() << "Found SubjectKeyId extension" << Log::Endl(); |
|
221 if(subKeyId->length <= aSubjectKeyId.MaxLength()) |
|
222 { |
|
223 aSubjectKeyId = TPtrC8(subKeyId->data, subKeyId->length); |
|
224 done = true; |
|
225 } |
|
226 else |
|
227 { |
|
228 prog << Log::Indent() << "but SubjectKeyId > 160 bits so ignoring it" << Log::Endl(); |
|
229 } |
|
230 ASN1_OCTET_STRING_free(subKeyId); |
|
231 } |
|
232 } |
|
233 |
|
234 if(!done) |
|
235 { |
|
236 // Subject Key Id extension was ignored, missing or too long... |
|
237 if(aUseRfc3280Algorithm) |
|
238 { |
|
239 // We do not need to decode the public key just hash its |
|
240 // data as per rfc3280 4.2.1.2 method 1 |
|
241 prog << Log::Indent() << "Calculating SubjectKeyId using RFC3280 4.2.1.2 method 1" << Log::Endl(); |
|
242 unsigned char sha1hash[SHA_DIGEST_LENGTH]; |
|
243 |
|
244 SHA1(x509->cert_info->key->public_key->data, x509->cert_info->key->public_key->length, |
|
245 sha1hash); |
|
246 aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH); |
|
247 done = true; |
|
248 } |
|
249 else |
|
250 { |
|
251 // Calculate SubjectKeyId via Symbian algorithm |
|
252 prog << Log::Indent() << "Calculating SubjectKeyId using Symbian algorithm" << Log::Endl(); |
|
253 EVP_PKEY *key = X509_PUBKEY_get(x509->cert_info->key); |
|
254 if(!key) |
|
255 { |
|
256 dbg << Log::Indent() << "openssl failed to decode certificate public key" << Log::Endl(); |
|
257 FatalError(); |
|
258 } |
|
259 |
|
260 switch(key->type) |
|
261 { |
|
262 case EVP_PKEY_RSA: |
|
263 { |
|
264 TUint32 len = key->pkey.rsa->n->top*sizeof(BN_ULONG); |
|
265 TUint8 *buf = new TUint8[len]; |
|
266 for(TUint32 i=0; i<len; ++i) |
|
267 { |
|
268 buf[i] = ((TUint8 *)key->pkey.rsa->n->d)[len-i-1]; |
|
269 } |
|
270 |
|
271 unsigned char sha1hash[SHA_DIGEST_LENGTH]; |
|
272 SHA1(buf, len, sha1hash); |
|
273 delete [] buf; |
|
274 aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH); |
|
275 done = true; |
|
276 break; |
|
277 } |
|
278 case EVP_PKEY_DSA: |
|
279 { |
|
280 TUint32 len = key->pkey.dsa->pub_key->top*sizeof(BN_ULONG); |
|
281 TUint8 *buf = new TUint8[len]; |
|
282 for(TUint32 i=0; i<len; ++i) |
|
283 { |
|
284 buf[i] = ((TUint8 *)key->pkey.dsa->pub_key->d)[len-i-1]; |
|
285 } |
|
286 |
|
287 unsigned char sha1hash[SHA_DIGEST_LENGTH]; |
|
288 SHA1(buf, len, sha1hash); |
|
289 delete [] buf; |
|
290 aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH); |
|
291 done = true; |
|
292 break; |
|
293 } |
|
294 default: |
|
295 // Unknown public key type. |
|
296 prog << Log::Indent() << "Unknown public key type " << key->type << Log::Endl(); |
|
297 break; |
|
298 } |
|
299 |
|
300 EVP_PKEY_free(key); |
|
301 } |
|
302 } |
|
303 |
|
304 X509_free(x509); |
|
305 return done; |
|
306 } |
|
307 |
|
308 |
|
309 bool X509IssuerKeyId(EUseCertificateExtension aUseExtension, |
|
310 const TUint8 *aCert, TUint32 aCertLength, |
|
311 std::string &aIssuer, TKeyIdentifier &aIssuerKeyId) |
|
312 { |
|
313 prog << Log::Indent() << "X509IssuerKeyId :-" << Log::Endl(); |
|
314 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope |
|
315 bool done = false; |
|
316 |
|
317 // decode DER certificate into X509 structure |
|
318 D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert; |
|
319 X509 *x509 = d2i_X509(NULL, &p, aCertLength); |
|
320 if(!x509 || (p != aCert+aCertLength)) |
|
321 { |
|
322 dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl(); |
|
323 FatalError(); |
|
324 } |
|
325 |
|
326 // Return the Subject Name |
|
327 prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl(); |
|
328 char *issuerOne = X509_NAME_oneline(X509_get_issuer_name(x509),0,0); |
|
329 prog << Log::Indent() << "Cert issuer is '" << issuerOne << "'" << Log::Endl(); |
|
330 aIssuer = issuerOne; |
|
331 OPENSSL_free(issuerOne); |
|
332 |
|
333 if(aUseExtension) |
|
334 { |
|
335 // Attempt to read Subject Key Id extension |
|
336 AUTHORITY_KEYID *authKeyId = (AUTHORITY_KEYID *) X509_get_ext_d2i(x509, NID_authority_key_identifier, NULL, NULL); |
|
337 if(authKeyId) |
|
338 { |
|
339 prog << Log::Indent() << "Found AuthorityKeyId extension" << Log::Endl(); |
|
340 if(authKeyId->keyid) |
|
341 { |
|
342 if(authKeyId->keyid->length <= aIssuerKeyId.MaxLength()) |
|
343 { |
|
344 aIssuerKeyId = TPtrC8(authKeyId->keyid->data, authKeyId->keyid->length); |
|
345 done = true; |
|
346 } |
|
347 else |
|
348 { |
|
349 prog << Log::Indent() << "but AuthroityKeyId > 160 bits so ignoring it" << Log::Endl(); |
|
350 } |
|
351 } |
|
352 else |
|
353 { |
|
354 prog << Log::Indent() << "but it does not include a key id, so ignoring it" << Log::Endl(); |
|
355 } |
|
356 |
|
357 AUTHORITY_KEYID_free(authKeyId); |
|
358 } |
|
359 } |
|
360 |
|
361 X509_free(x509); |
|
362 return done; |
|
363 } |
|
364 |
|
365 void Der2Pem(const std::string &aDerCert, std::string &aPemCert) |
|
366 { |
|
367 prog << Log::Indent() << "Converting DER to PEM:-" << Log::Endl(); |
|
368 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope |
|
369 |
|
370 // decode DER certificate into X509 structure |
|
371 D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aDerCert.data(); |
|
372 X509 *x509 = d2i_X509(NULL, &p, aDerCert.size()); |
|
373 if(!x509 || ((const char *)p != aDerCert.data()+aDerCert.size())) |
|
374 { |
|
375 dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl(); |
|
376 FatalError(); |
|
377 } |
|
378 |
|
379 BIO *memBio = BIO_new(BIO_s_mem()); |
|
380 BULLSEYE_OFF |
|
381 if(!memBio) |
|
382 { |
|
383 dbg << Log::Indent() << "openssl failed to create BIO" << Log::Endl(); |
|
384 FatalError(); |
|
385 } |
|
386 |
|
387 if(!PEM_write_bio_X509(memBio, x509)) |
|
388 { |
|
389 dbg << Log::Indent() << "openssl failed to convert to PEM" << Log::Endl(); |
|
390 FatalError(); |
|
391 } |
|
392 BULLSEYE_RESTORE |
|
393 |
|
394 long pemCertLen = 0; |
|
395 char *pemCertData = 0; |
|
396 pemCertLen = BIO_get_mem_data(memBio, &pemCertData); |
|
397 |
|
398 // Return the PEM cert |
|
399 aPemCert.assign(pemCertData, pemCertLen); |
|
400 |
|
401 BIO_free(memBio); |
|
402 |
|
403 X509_free(x509); |
|
404 |
|
405 prog << Log::Indent() << "Conversion ok" << Log::Endl(); |
|
406 return; |
|
407 } |
|
408 |
|
409 static const char utf8Header[] = |
|
410 { |
|
411 0xef, 0xbb, 0xbf |
|
412 }; |
|
413 |
|
414 bool Pem2Der(const std::string &aPemCert, std::string &aDerCert) |
|
415 { |
|
416 prog << Log::Indent() << "Try PEM to DER coversion :-" << Log::Endl(); |
|
417 AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope |
|
418 |
|
419 TUint32 pemLength=aPemCert.size(); |
|
420 const char *pemData=aPemCert.data(); |
|
421 |
|
422 if((pemLength >= 3) && (memcmp(aPemCert.data(), utf8Header, sizeof(utf8Header)) == 0)) |
|
423 { |
|
424 // PEM cert has a UTF8 header, so strip it |
|
425 prog << Log::Indent() << "Certificate data file has a UTF-8 header" << Log::Endl(); |
|
426 pemLength -= sizeof(utf8Header); |
|
427 pemData += sizeof(utf8Header); |
|
428 } |
|
429 |
|
430 // |
|
431 // Read PEM to internal |
|
432 // |
|
433 BIO *memBioIn = BIO_new_mem_buf((void *)pemData, pemLength); |
|
434 BULLSEYE_OFF |
|
435 if(!memBioIn) |
|
436 { |
|
437 dbg << Log::Indent() << "openssl failed to create BIO for reading PEM" << Log::Endl(); |
|
438 FatalError(); |
|
439 } |
|
440 BULLSEYE_RESTORE |
|
441 |
|
442 X509 *x509 = PEM_read_bio_X509(memBioIn, NULL, 0, NULL); |
|
443 if(!x509) |
|
444 { |
|
445 prog << Log::Indent() << "Conversion failed - presumably DER" << Log::Endl(); |
|
446 return false; |
|
447 } |
|
448 BIO_free(memBioIn); |
|
449 memBioIn = 0; |
|
450 |
|
451 // |
|
452 // Write internal to DER |
|
453 // |
|
454 unsigned char *derCert = 0; |
|
455 int derLen = i2d_X509(x509, &derCert); |
|
456 if(derLen <=0 ) |
|
457 { |
|
458 dbg << Log::Indent() << "openssl failed to convert to DER" << Log::Endl(); |
|
459 FatalError(); |
|
460 } |
|
461 |
|
462 // Return the DER cert |
|
463 aDerCert.assign((char *)derCert, derLen); |
|
464 |
|
465 X509_free(x509); |
|
466 prog << Log::Indent() << "Conversion ok" << Log::Endl(); |
|
467 return true; |
|
468 } |
|
469 |
|
470 |
|
471 |
|
472 // End of file |