VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/CertificateImpl.cpp@ 70772

Last change on this file since 70772 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: CertificateImpl.cpp 69500 2017-10-28 15:14:05Z vboxsync $ */
2/** @file
3 * ICertificate COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2008-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/path.h>
19#include <iprt/cpp/utils.h>
20#include <VBox/com/array.h>
21#include <iprt/crypto/x509.h>
22
23#include "ProgressImpl.h"
24#include "CertificateImpl.h"
25#include "AutoCaller.h"
26#include "Global.h"
27#include "Logging.h"
28
29using namespace std;
30
31
32/**
33 * Private instance data for the #Certificate class.
34 * @see Certificate::m
35 */
36struct Certificate::Data
37{
38 Data()
39 : fTrusted(false)
40 , fExpired(false)
41 , fValidX509(false)
42 {
43 RT_ZERO(X509);
44 }
45
46 ~Data()
47 {
48 if (fValidX509)
49 {
50 RTCrX509Certificate_Delete(&X509);
51 RT_ZERO(X509);
52 fValidX509 = false;
53 }
54 }
55
56 /** Whether the certificate is trusted. */
57 bool fTrusted;
58 /** Whether the certificate is trusted. */
59 bool fExpired;
60 /** Valid data in mX509. */
61 bool fValidX509;
62 /** Clone of the X.509 certificate. */
63 RTCRX509CERTIFICATE X509;
64
65private:
66 Data(const Certificate::Data &rTodo) { AssertFailed(); NOREF(rTodo); }
67 Data &operator=(const Certificate::Data &rTodo) { AssertFailed(); NOREF(rTodo); return *this; }
68};
69
70
71///////////////////////////////////////////////////////////////////////////////////
72//
73// Certificate constructor / destructor
74//
75// ////////////////////////////////////////////////////////////////////////////////
76
77DEFINE_EMPTY_CTOR_DTOR(Certificate)
78
79HRESULT Certificate::FinalConstruct()
80{
81 return BaseFinalConstruct();
82}
83
84void Certificate::FinalRelease()
85{
86 uninit();
87 BaseFinalRelease();
88}
89
90/**
91 * Initializes a certificate instance.
92 *
93 * @returns COM status code.
94 * @param a_pCert The certificate.
95 * @param a_fTrusted Whether the caller trusts the certificate or not.
96 * @param a_fExpired Whether the caller consideres the certificate to be
97 * expired.
98 */
99HRESULT Certificate::initCertificate(PCRTCRX509CERTIFICATE a_pCert, bool a_fTrusted, bool a_fExpired)
100{
101 HRESULT rc = S_OK;
102 LogFlowThisFuncEnter();
103
104 AutoInitSpan autoInitSpan(this);
105 AssertReturn(autoInitSpan.isOk(), E_FAIL);
106
107 m = new Data();
108
109 int vrc = RTCrX509Certificate_Clone(&m->X509, a_pCert, &g_RTAsn1DefaultAllocator);
110 if (RT_SUCCESS(vrc))
111 {
112 m->fValidX509 = true;
113 m->fTrusted = a_fTrusted;
114 m->fExpired = a_fExpired;
115 autoInitSpan.setSucceeded();
116 }
117 else
118 rc = Global::vboxStatusCodeToCOM(vrc);
119
120 LogFlowThisFunc(("returns rc=%Rhrc\n", rc));
121 return rc;
122}
123
124void Certificate::uninit()
125{
126 /* Enclose the state transition Ready->InUninit->NotReady */
127 AutoUninitSpan autoUninitSpan(this);
128 if (autoUninitSpan.uninitDone())
129 return;
130
131 delete m;
132 m = NULL;
133}
134
135
136/** @name Wrapped ICertificate properties
137 * @{
138 */
139
140HRESULT Certificate::getVersionNumber(CertificateVersion_T *aVersionNumber)
141{
142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
143
144 Assert(m->fValidX509);
145 switch (m->X509.TbsCertificate.T0.Version.uValue.u)
146 {
147 case RTCRX509TBSCERTIFICATE_V1: *aVersionNumber = (CertificateVersion_T)CertificateVersion_V1; break;
148 case RTCRX509TBSCERTIFICATE_V2: *aVersionNumber = (CertificateVersion_T)CertificateVersion_V2; break;
149 case RTCRX509TBSCERTIFICATE_V3: *aVersionNumber = (CertificateVersion_T)CertificateVersion_V3; break;
150 default: AssertFailed(); *aVersionNumber = (CertificateVersion_T)CertificateVersion_Unknown; break;
151 }
152 return S_OK;
153}
154
155HRESULT Certificate::getSerialNumber(com::Utf8Str &aSerialNumber)
156{
157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
158
159 Assert(m->fValidX509);
160
161 char szTmp[_2K];
162 int vrc = RTAsn1Integer_ToString(&m->X509.TbsCertificate.SerialNumber, szTmp, sizeof(szTmp), 0, NULL);
163 if (RT_SUCCESS(vrc))
164 aSerialNumber = szTmp;
165 else
166 return Global::vboxStatusCodeToCOM(vrc);
167
168 return S_OK;
169}
170
171HRESULT Certificate::getSignatureAlgorithmOID(com::Utf8Str &aSignatureAlgorithmOID)
172{
173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
174
175 Assert(m->fValidX509);
176 aSignatureAlgorithmOID = m->X509.TbsCertificate.Signature.Algorithm.szObjId;
177
178 return S_OK;
179}
180
181HRESULT Certificate::getSignatureAlgorithmName(com::Utf8Str &aSignatureAlgorithmName)
182{
183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
184
185 Assert(m->fValidX509);
186 return i_getAlgorithmName(&m->X509.TbsCertificate.Signature, aSignatureAlgorithmName);
187}
188
189HRESULT Certificate::getIssuerName(std::vector<com::Utf8Str> &aIssuerName)
190{
191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
192
193 Assert(m->fValidX509);
194 return i_getX509Name(&m->X509.TbsCertificate.Issuer, aIssuerName);
195}
196
197HRESULT Certificate::getSubjectName(std::vector<com::Utf8Str> &aSubjectName)
198{
199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
200
201 Assert(m->fValidX509);
202 return i_getX509Name(&m->X509.TbsCertificate.Subject, aSubjectName);
203}
204
205HRESULT Certificate::getFriendlyName(com::Utf8Str &aFriendlyName)
206{
207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
208
209 Assert(m->fValidX509);
210
211 PCRTCRX509NAME pName = &m->X509.TbsCertificate.Subject;
212
213 /*
214 * Enumerate the subject name and pick interesting attributes we can use to
215 * form a name more friendly than the RTCrX509Name_FormatAsString output.
216 */
217 const char *pszOrg = NULL;
218 const char *pszOrgUnit = NULL;
219 const char *pszGivenName = NULL;
220 const char *pszSurname = NULL;
221 const char *pszEmail = NULL;
222 for (uint32_t i = 0; i < pName->cItems; i++)
223 {
224 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = pName->papItems[i];
225 for (uint32_t j = 0; j < pRdn->cItems; j++)
226 {
227 PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
228 AssertContinue(pComponent->Value.enmType == RTASN1TYPE_STRING);
229
230 /* Select interesting components based on the short RDN prefix
231 string (easier to read and write than OIDs, for now). */
232 const char *pszPrefix = RTCrX509Name_GetShortRdn(&pComponent->Type);
233 if (pszPrefix)
234 {
235 const char *pszUtf8;
236 int vrc = RTAsn1String_QueryUtf8(&pComponent->Value.u.String, &pszUtf8, NULL);
237 if (RT_SUCCESS(vrc) && *pszUtf8)
238 {
239 if (!strcmp(pszPrefix, "Email"))
240 pszEmail = pszUtf8;
241 else if (!strcmp(pszPrefix, "O"))
242 pszOrg = pszUtf8;
243 else if (!strcmp(pszPrefix, "OU"))
244 pszOrgUnit = pszUtf8;
245 else if (!strcmp(pszPrefix, "S"))
246 pszSurname = pszUtf8;
247 else if (!strcmp(pszPrefix, "G"))
248 pszGivenName = pszUtf8;
249 }
250 }
251 }
252 }
253
254 if (pszGivenName && pszSurname)
255 {
256 if (pszEmail)
257 aFriendlyName = Utf8StrFmt("%s, %s <%s>", pszSurname, pszGivenName, pszEmail);
258 else if (pszOrg)
259 aFriendlyName = Utf8StrFmt("%s, %s (%s)", pszSurname, pszGivenName, pszOrg);
260 else if (pszOrgUnit)
261 aFriendlyName = Utf8StrFmt("%s, %s (%s)", pszSurname, pszGivenName, pszOrgUnit);
262 else
263 aFriendlyName = Utf8StrFmt("%s, %s", pszSurname, pszGivenName);
264 }
265 else if (pszOrg && pszOrgUnit)
266 aFriendlyName = Utf8StrFmt("%s, %s", pszOrg, pszOrgUnit);
267 else if (pszOrg)
268 aFriendlyName = Utf8StrFmt("%s", pszOrg);
269 else if (pszOrgUnit)
270 aFriendlyName = Utf8StrFmt("%s", pszOrgUnit);
271 else
272 {
273 /*
274 * Fall back on unfriendly but accurate.
275 */
276 char szTmp[_8K];
277 RT_ZERO(szTmp);
278 RTCrX509Name_FormatAsString(pName, szTmp, sizeof(szTmp) - 1, NULL);
279 aFriendlyName = szTmp;
280 }
281
282 return S_OK;
283}
284
285HRESULT Certificate::getValidityPeriodNotBefore(com::Utf8Str &aValidityPeriodNotBefore)
286{
287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
288
289 Assert(m->fValidX509);
290 return i_getTime(&m->X509.TbsCertificate.Validity.NotBefore, aValidityPeriodNotBefore);
291}
292
293HRESULT Certificate::getValidityPeriodNotAfter(com::Utf8Str &aValidityPeriodNotAfter)
294{
295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
296
297 Assert(m->fValidX509);
298 return i_getTime(&m->X509.TbsCertificate.Validity.NotAfter, aValidityPeriodNotAfter);
299}
300
301HRESULT Certificate::getPublicKeyAlgorithmOID(com::Utf8Str &aPublicKeyAlgorithmOID)
302{
303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
304
305 Assert(m->fValidX509);
306 aPublicKeyAlgorithmOID = m->X509.TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm.szObjId;
307 return S_OK;
308}
309
310HRESULT Certificate::getPublicKeyAlgorithm(com::Utf8Str &aPublicKeyAlgorithm)
311{
312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
313
314 Assert(m->fValidX509);
315 return i_getAlgorithmName(&m->X509.TbsCertificate.SubjectPublicKeyInfo.Algorithm, aPublicKeyAlgorithm);
316}
317
318HRESULT Certificate::getSubjectPublicKey(std::vector<BYTE> &aSubjectPublicKey)
319{
320
321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Getting encoded ASN.1 bytes may make changes to X509. */
322 return i_getEncodedBytes(&m->X509.TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.Asn1Core, aSubjectPublicKey);
323}
324
325HRESULT Certificate::getIssuerUniqueIdentifier(com::Utf8Str &aIssuerUniqueIdentifier)
326{
327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
328
329 return i_getUniqueIdentifier(&m->X509.TbsCertificate.T1.IssuerUniqueId, aIssuerUniqueIdentifier);
330}
331
332HRESULT Certificate::getSubjectUniqueIdentifier(com::Utf8Str &aSubjectUniqueIdentifier)
333{
334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
335
336 return i_getUniqueIdentifier(&m->X509.TbsCertificate.T2.SubjectUniqueId, aSubjectUniqueIdentifier);
337}
338
339HRESULT Certificate::getCertificateAuthority(BOOL *aCertificateAuthority)
340{
341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
342
343 *aCertificateAuthority = m->X509.TbsCertificate.T3.pBasicConstraints
344 && m->X509.TbsCertificate.T3.pBasicConstraints->CA.fValue;
345
346 return S_OK;
347}
348
349HRESULT Certificate::getKeyUsage(ULONG *aKeyUsage)
350{
351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
352
353 *aKeyUsage = m->X509.TbsCertificate.T3.fKeyUsage;
354 return S_OK;
355}
356
357HRESULT Certificate::getExtendedKeyUsage(std::vector<com::Utf8Str> &aExtendedKeyUsage)
358{
359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
360 NOREF(aExtendedKeyUsage);
361 return E_NOTIMPL;
362}
363
364HRESULT Certificate::getRawCertData(std::vector<BYTE> &aRawCertData)
365{
366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Getting encoded ASN.1 bytes may make changes to X509. */
367 return i_getEncodedBytes(&m->X509.SeqCore.Asn1Core, aRawCertData);
368}
369
370HRESULT Certificate::getSelfSigned(BOOL *aSelfSigned)
371{
372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
373
374 Assert(m->fValidX509);
375 *aSelfSigned = RTCrX509Certificate_IsSelfSigned(&m->X509);
376
377 return S_OK;
378}
379
380HRESULT Certificate::getTrusted(BOOL *aTrusted)
381{
382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
383
384 Assert(m->fValidX509);
385 *aTrusted = m->fTrusted;
386
387 return S_OK;
388}
389
390HRESULT Certificate::getExpired(BOOL *aExpired)
391{
392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
393 Assert(m->fValidX509);
394 *aExpired = m->fExpired;
395 return S_OK;
396}
397
398/** @} */
399
400/** @name Wrapped ICertificate methods
401 * @{
402 */
403
404HRESULT Certificate::isCurrentlyExpired(BOOL *aResult)
405{
406 AssertReturnStmt(m->fValidX509, *aResult = TRUE, E_UNEXPECTED);
407 RTTIMESPEC Now;
408 *aResult = RTCrX509Validity_IsValidAtTimeSpec(&m->X509.TbsCertificate.Validity, RTTimeNow(&Now)) ? FALSE : TRUE;
409 return S_OK;
410}
411
412HRESULT Certificate::queryInfo(LONG aWhat, com::Utf8Str &aResult)
413{
414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
415 /* Insurance. */
416 NOREF(aResult);
417 return setError(E_FAIL, "Unknown item %u", aWhat);
418}
419
420/** @} */
421
422
423/** @name Methods extracting COM data from the certificate object
424 * @{
425 */
426
427/**
428 * Translates an algorithm OID into a human readable string, if possible.
429 *
430 * @returns S_OK.
431 * @param a_pAlgId The algorithm.
432 * @param a_rReturn The return string value.
433 * @throws std::bad_alloc
434 */
435HRESULT Certificate::i_getAlgorithmName(PCRTCRX509ALGORITHMIDENTIFIER a_pAlgId, com::Utf8Str &a_rReturn)
436{
437 const char *pszOid = a_pAlgId->Algorithm.szObjId;
438 const char *pszName;
439 if (!pszOid) pszName = "";
440 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_RSA)) pszName = "rsaEncryption";
441 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_MD2_WITH_RSA)) pszName = "md2WithRSAEncryption";
442 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_MD4_WITH_RSA)) pszName = "md4WithRSAEncryption";
443 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_MD5_WITH_RSA)) pszName = "md5WithRSAEncryption";
444 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA1_WITH_RSA)) pszName = "sha1WithRSAEncryption";
445 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA224_WITH_RSA)) pszName = "sha224WithRSAEncryption";
446 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA256_WITH_RSA)) pszName = "sha256WithRSAEncryption";
447 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA384_WITH_RSA)) pszName = "sha384WithRSAEncryption";
448 else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA512_WITH_RSA)) pszName = "sha512WithRSAEncryption";
449 else
450 pszName = pszOid;
451 a_rReturn = pszName;
452 return S_OK;
453}
454
455/**
456 * Formats a X.509 name into a string array.
457 *
458 * The name is prefix with a short hand of the relative distinguished name
459 * type followed by an equal sign.
460 *
461 * @returns S_OK.
462 * @param a_pName The X.509 name.
463 * @param a_rReturn The return string array.
464 * @throws std::bad_alloc
465 */
466HRESULT Certificate::i_getX509Name(PCRTCRX509NAME a_pName, std::vector<com::Utf8Str> &a_rReturn)
467{
468 if (RTCrX509Name_IsPresent(a_pName))
469 {
470 for (uint32_t i = 0; i < a_pName->cItems; i++)
471 {
472 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = a_pName->papItems[i];
473 for (uint32_t j = 0; j < pRdn->cItems; j++)
474 {
475 PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
476
477 AssertReturn(pComponent->Value.enmType == RTASN1TYPE_STRING,
478 setErrorVrc(VERR_CR_X509_NAME_NOT_STRING, "VERR_CR_X509_NAME_NOT_STRING"));
479
480 /* Get the prefix for this name component. */
481 const char *pszPrefix = RTCrX509Name_GetShortRdn(&pComponent->Type);
482 AssertStmt(pszPrefix, pszPrefix = pComponent->Type.szObjId);
483
484 /* Get the string. */
485 const char *pszUtf8;
486 int vrc = RTAsn1String_QueryUtf8(&pComponent->Value.u.String, &pszUtf8, NULL /*pcch*/);
487 AssertRCReturn(vrc, setErrorVrc(vrc, "RTAsn1String_QueryUtf8(%u/%u,,) -> %Rrc", i, j, vrc));
488
489 a_rReturn.push_back(Utf8StrFmt("%s=%s", pszPrefix, pszUtf8));
490 }
491 }
492 }
493 return S_OK;
494}
495
496/**
497 * Translates an ASN.1 timestamp into an ISO timestamp string.
498 *
499 * @returns S_OK.
500 * @param a_pTime The timestamp
501 * @param a_rReturn The return string value.
502 * @throws std::bad_alloc
503 */
504HRESULT Certificate::i_getTime(PCRTASN1TIME a_pTime, com::Utf8Str &a_rReturn)
505{
506 char szTmp[128];
507 if (RTTimeToString(&a_pTime->Time, szTmp, sizeof(szTmp)))
508 {
509 a_rReturn = szTmp;
510 return S_OK;
511 }
512 AssertFailed();
513 return E_FAIL;
514}
515
516/**
517 * Translates a X.509 unique identifier to a string.
518 *
519 * @returns S_OK.
520 * @param a_pUniqueId The unique identifier.
521 * @param a_rReturn The return string value.
522 * @throws std::bad_alloc
523 */
524HRESULT Certificate::i_getUniqueIdentifier(PCRTCRX509UNIQUEIDENTIFIER a_pUniqueId, com::Utf8Str &a_rReturn)
525{
526 /* The a_pUniqueId may not be present! */
527 if (RTCrX509UniqueIdentifier_IsPresent(a_pUniqueId))
528 {
529 void const *pvData = RTASN1BITSTRING_GET_BIT0_PTR(a_pUniqueId);
530 size_t const cbData = RTASN1BITSTRING_GET_BYTE_SIZE(a_pUniqueId);
531 size_t const cbFormatted = cbData * 3 - 1 + 1;
532 a_rReturn.reserve(cbFormatted); /* throws */
533 int vrc = RTStrPrintHexBytes(a_rReturn.mutableRaw(), cbFormatted, pvData, cbData, RTSTRPRINTHEXBYTES_F_SEP_COLON);
534 a_rReturn.jolt();
535 AssertRCReturn(vrc, Global::vboxStatusCodeToCOM(vrc));
536 }
537 else
538 Assert(a_rReturn.isEmpty());
539 return S_OK;
540}
541
542/**
543 * Translates any ASN.1 object into a (DER encoded) byte array.
544 *
545 * @returns S_OK.
546 * @param a_pAsn1Obj The ASN.1 object to get the DER encoded bytes for.
547 * @param a_rReturn The return byte vector.
548 * @throws std::bad_alloc
549 */
550HRESULT Certificate::i_getEncodedBytes(PRTASN1CORE a_pAsn1Obj, std::vector<BYTE> &a_rReturn)
551{
552 HRESULT hrc = S_OK;
553 Assert(a_rReturn.size() == 0);
554 if (RTAsn1Core_IsPresent(a_pAsn1Obj))
555 {
556 uint32_t cbEncoded;
557 int vrc = RTAsn1EncodePrepare(a_pAsn1Obj, 0, &cbEncoded, NULL);
558 if (RT_SUCCESS(vrc))
559 {
560 a_rReturn.resize(cbEncoded);
561 Assert(a_rReturn.size() == cbEncoded);
562 if (cbEncoded)
563 {
564 vrc = RTAsn1EncodeToBuffer(a_pAsn1Obj, 0, &a_rReturn.front(), a_rReturn.size(), NULL);
565 if (RT_FAILURE(vrc))
566 hrc = setErrorVrc(vrc, "RTAsn1EncodeToBuffer failed with %Rrc", vrc);
567 }
568 }
569 else
570 hrc = setErrorVrc(vrc, "RTAsn1EncodePrepare failed with %Rrc", vrc);
571 }
572 return hrc;
573}
574
575/** @} */
576
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use