1/*
2 * Copyright (C) 2003-2007 Justin Karneges <justin@affinix.com>
3 * Copyright (C) 2004-2006 Brad Hards <bradh@frogmouth.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 *
20 */
21
22#include "qca_cert.h"
23
24#include "qca_publickey.h"
25#include "qcaprovider.h"
26
27#include <QFile>
28#include <QRegExp>
29#include <QRegularExpression>
30#include <QTextStream>
31#include <QUrl>
32
33#include <cstdlib>
34
35namespace QCA {
36
37Provider::Context *getContext(const QString &type, const QString &provider);
38Provider::Context *getContext(const QString &type, Provider *p);
39
40// from qca_publickey.cpp
41bool stringToFile(const QString &fileName, const QString &content);
42bool stringFromFile(const QString &fileName, QString *s);
43bool arrayToFile(const QString &fileName, const QByteArray &content);
44bool arrayFromFile(const QString &fileName, QByteArray *a);
45bool ask_passphrase(const QString &fname, void *ptr, SecureArray *answer);
46ProviderList allProviders();
47Provider *providerForName(const QString &name);
48bool use_asker_fallback(ConvertResult r);
49
50// last 3 arguments must be valid, and chain must be empty
51static bool get_pkcs12_der(const QByteArray &der,
52 const QString &fileName,
53 void *ptr,
54 const SecureArray &passphrase,
55 ConvertResult *result,
56 const QString &provider,
57 QString *name,
58 CertificateChain *chain,
59 PrivateKey *key)
60{
61 QString _name;
62 QList<CertContext *> list;
63 PKeyContext *kc = nullptr;
64
65 PKCS12Context *pix = static_cast<PKCS12Context *>(getContext(QStringLiteral("pkcs12"), provider));
66 ConvertResult r = pix->fromPKCS12(in: der, passphrase, name: &_name, chain: &list, priv: &kc);
67
68 // error converting without passphrase? maybe a passphrase is needed
69 if (use_asker_fallback(r) && passphrase.isEmpty()) {
70 SecureArray pass;
71 if (ask_passphrase(fname: fileName, ptr, answer: &pass))
72 r = pix->fromPKCS12(in: der, passphrase: pass, name: &_name, chain: &list, priv: &kc);
73 }
74 delete pix;
75
76 if (result)
77 *result = r;
78
79 if (r == ConvertGood) {
80 *name = _name;
81 for (int n = 0; n < list.count(); ++n) {
82 Certificate cert;
83 cert.change(c: list[n]);
84 chain->append(t: cert);
85 }
86 key->change(c: kc);
87 return true;
88 }
89 return false;
90}
91
92static CertificateInfo orderedToMap(const CertificateInfoOrdered &info)
93{
94 CertificateInfo out;
95
96 // first, do all but EmailLegacy
97 for (int n = 0; n < info.count(); ++n) {
98 const CertificateInfoPair &i = info[n];
99 if (i.type().known() != EmailLegacy)
100 out.insert(key: i.type(), value: i.value());
101 }
102
103 // lastly, apply EmailLegacy
104 for (int n = 0; n < info.count(); ++n) {
105 const CertificateInfoPair &i = info[n];
106 if (i.type().known() == EmailLegacy) {
107 // de-dup
108 const QList<QString> emails = out.values(key: Email);
109 if (!emails.contains(str: i.value()))
110 out.insert(key: Email, value: i.value());
111 }
112 }
113
114 return out;
115}
116
117static void moveMapValues(CertificateInfo *from, CertificateInfoOrdered *to, const CertificateInfoType &type)
118{
119 const QList<QString> values = from->values(key: type);
120 from->remove(key: type);
121
122 // multimap values are stored in reverse. we'll insert backwards in
123 // order to right them.
124 for (int n = values.count() - 1; n >= 0; --n)
125 to->append(t: CertificateInfoPair(type, values[n]));
126}
127
128static CertificateInfoOrdered mapToOrdered(const CertificateInfo &info)
129{
130 CertificateInfo in = info;
131 CertificateInfoOrdered out;
132
133 // have a specific order for some types
134 moveMapValues(from: &in, to: &out, type: CommonName);
135 moveMapValues(from: &in, to: &out, type: Country);
136 moveMapValues(from: &in, to: &out, type: Locality);
137 moveMapValues(from: &in, to: &out, type: State);
138 moveMapValues(from: &in, to: &out, type: Organization);
139 moveMapValues(from: &in, to: &out, type: OrganizationalUnit);
140 moveMapValues(from: &in, to: &out, type: Email);
141 moveMapValues(from: &in, to: &out, type: URI);
142 moveMapValues(from: &in, to: &out, type: DNS);
143 moveMapValues(from: &in, to: &out, type: IPAddress);
144 moveMapValues(from: &in, to: &out, type: XMPP);
145
146 // get remaining types
147 const QList<CertificateInfoType> typesLeft = in.keys();
148
149 // dedup
150 QList<CertificateInfoType> types;
151 for (int n = 0; n < typesLeft.count(); ++n) {
152 if (!types.contains(t: typesLeft[n]))
153 types += typesLeft[n];
154 }
155
156 // insert the rest of the types in the order we got them (map order)
157 for (int n = 0; n < types.count(); ++n)
158 moveMapValues(from: &in, to: &out, type: types[n]);
159
160 Q_ASSERT(in.isEmpty());
161
162 return out;
163}
164
165//----------------------------------------------------------------------------
166// Global
167//----------------------------------------------------------------------------
168static const char CommonName_id[] = "2.5.4.3";
169static const char Email_id[] = "GeneralName.rfc822Name";
170static const char EmailLegacy_id[] = "1.2.840.113549.1.9.1";
171static const char Organization_id[] = "2.5.4.10";
172static const char OrganizationalUnit_id[] = "2.5.4.11";
173static const char Locality_id[] = "2.5.4.7";
174static const char IncorporationLocality_id[] = "1.3.6.1.4.1.311.60.2.1.1";
175static const char State_id[] = "2.5.4.8";
176static const char IncorporationState_id[] = "1.3.6.1.4.1.311.60.2.1.2";
177static const char Country_id[] = "2.5.4.6";
178static const char IncorporationCountry_id[] = "1.3.6.1.4.1.311.60.2.1.3";
179static const char URI_id[] = "GeneralName.uniformResourceIdentifier";
180static const char DNS_id[] = "GeneralName.dNSName";
181static const char IPAddress_id[] = "GeneralName.iPAddress";
182static const char XMPP_id[] = "1.3.6.1.5.5.7.8.5";
183
184static const char DigitalSignature_id[] = "KeyUsage.digitalSignature";
185static const char NonRepudiation_id[] = "KeyUsage.nonRepudiation";
186static const char KeyEncipherment_id[] = "KeyUsage.keyEncipherment";
187static const char DataEncipherment_id[] = "KeyUsage.dataEncipherment";
188static const char KeyAgreement_id[] = "KeyUsage.keyAgreement";
189static const char KeyCertificateSign_id[] = "KeyUsage.keyCertSign";
190static const char CRLSign_id[] = "KeyUsage.crlSign";
191static const char EncipherOnly_id[] = "KeyUsage.encipherOnly";
192static const char DecipherOnly_id[] = "KeyUsage.decipherOnly";
193static const char ServerAuth_id[] = "1.3.6.1.5.5.7.3.1";
194static const char ClientAuth_id[] = "1.3.6.1.5.5.7.3.2";
195static const char CodeSigning_id[] = "1.3.6.1.5.5.7.3.3";
196static const char EmailProtection_id[] = "1.3.6.1.5.5.7.3.4";
197static const char IPSecEndSystem_id[] = "1.3.6.1.5.5.7.3.5";
198static const char IPSecTunnel_id[] = "1.3.6.1.5.5.7.3.6";
199static const char IPSecUser_id[] = "1.3.6.1.5.5.7.3.7";
200static const char TimeStamping_id[] = "1.3.6.1.5.5.7.3.8";
201static const char OCSPSigning_id[] = "1.3.6.1.5.5.7.3.9";
202
203static QString knownToId(CertificateInfoTypeKnown k)
204{
205 const char *out = nullptr;
206 switch (k) {
207 case CommonName:
208 out = CommonName_id;
209 break;
210 case Email:
211 out = Email_id;
212 break;
213 case EmailLegacy:
214 out = EmailLegacy_id;
215 break;
216 case Organization:
217 out = Organization_id;
218 break;
219 case OrganizationalUnit:
220 out = OrganizationalUnit_id;
221 break;
222 case Locality:
223 out = Locality_id;
224 break;
225 case IncorporationLocality:
226 out = IncorporationLocality_id;
227 break;
228 case State:
229 out = State_id;
230 break;
231 case IncorporationState:
232 out = IncorporationState_id;
233 break;
234 case Country:
235 out = Country_id;
236 break;
237 case IncorporationCountry:
238 out = IncorporationCountry_id;
239 break;
240 case URI:
241 out = URI_id;
242 break;
243 case DNS:
244 out = DNS_id;
245 break;
246 case IPAddress:
247 out = IPAddress_id;
248 break;
249 case XMPP:
250 out = XMPP_id;
251 break;
252 }
253 Q_ASSERT(out);
254 if (!out)
255 abort();
256 return QString::fromLatin1(ba: out);
257}
258
259static int idToKnown(const QString &id)
260{
261 if (id == QLatin1String(CommonName_id))
262 return CommonName;
263 else if (id == QLatin1String(Email_id))
264 return Email;
265 else if (id == QLatin1String(EmailLegacy_id))
266 return EmailLegacy;
267 else if (id == QLatin1String(Organization_id))
268 return Organization;
269 else if (id == QLatin1String(OrganizationalUnit_id))
270 return OrganizationalUnit;
271 else if (id == QLatin1String(Locality_id))
272 return Locality;
273 else if (id == QLatin1String(IncorporationLocality_id))
274 return IncorporationLocality;
275 else if (id == QLatin1String(State_id))
276 return State;
277 else if (id == QLatin1String(IncorporationState_id))
278 return IncorporationState;
279 else if (id == QLatin1String(Country_id))
280 return Country;
281 else if (id == QLatin1String(IncorporationCountry_id))
282 return IncorporationCountry;
283 else if (id == QLatin1String(URI_id))
284 return URI;
285 else if (id == QLatin1String(DNS_id))
286 return DNS;
287 else if (id == QLatin1String(IPAddress_id))
288 return IPAddress;
289 else if (id == QLatin1String(XMPP_id))
290 return XMPP;
291 else
292 return -1;
293}
294
295static CertificateInfoType::Section knownToSection(CertificateInfoTypeKnown k)
296{
297 switch (k) {
298 case CommonName:
299 case EmailLegacy:
300 case Organization:
301 case OrganizationalUnit:
302 case Locality:
303 case IncorporationLocality:
304 case State:
305 case IncorporationState:
306 case Country:
307 case IncorporationCountry:
308 return CertificateInfoType::DN;
309 default:
310 break;
311 }
312 return CertificateInfoType::AlternativeName;
313}
314
315static const char *knownToShortName(CertificateInfoTypeKnown k)
316{
317 switch (k) {
318 case CommonName:
319 return "CN";
320 case Locality:
321 return "L";
322 case State:
323 return "ST";
324 case Organization:
325 return "O";
326 case OrganizationalUnit:
327 return "OU";
328 case Country:
329 return "C";
330 case EmailLegacy:
331 return "emailAddress";
332 default:
333 break;
334 }
335 return nullptr;
336}
337
338static QString constraintKnownToId(ConstraintTypeKnown k)
339{
340 const char *out = nullptr;
341 switch (k) {
342 case DigitalSignature:
343 out = DigitalSignature_id;
344 break;
345 case NonRepudiation:
346 out = NonRepudiation_id;
347 break;
348 case KeyEncipherment:
349 out = KeyEncipherment_id;
350 break;
351 case DataEncipherment:
352 out = DataEncipherment_id;
353 break;
354 case KeyAgreement:
355 out = KeyAgreement_id;
356 break;
357 case KeyCertificateSign:
358 out = KeyCertificateSign_id;
359 break;
360 case CRLSign:
361 out = CRLSign_id;
362 break;
363 case EncipherOnly:
364 out = EncipherOnly_id;
365 break;
366 case DecipherOnly:
367 out = DecipherOnly_id;
368 break;
369 case ServerAuth:
370 out = ServerAuth_id;
371 break;
372 case ClientAuth:
373 out = ClientAuth_id;
374 break;
375 case CodeSigning:
376 out = CodeSigning_id;
377 break;
378 case EmailProtection:
379 out = EmailProtection_id;
380 break;
381 case IPSecEndSystem:
382 out = IPSecEndSystem_id;
383 break;
384 case IPSecTunnel:
385 out = IPSecTunnel_id;
386 break;
387 case IPSecUser:
388 out = IPSecUser_id;
389 break;
390 case TimeStamping:
391 out = TimeStamping_id;
392 break;
393 case OCSPSigning:
394 out = OCSPSigning_id;
395 break;
396 }
397 Q_ASSERT(out);
398 if (!out)
399 abort();
400 return QString::fromLatin1(ba: out);
401}
402
403static int constraintIdToKnown(const QString &id)
404{
405 if (id == QLatin1String(DigitalSignature_id))
406 return DigitalSignature;
407 else if (id == QLatin1String(NonRepudiation_id))
408 return NonRepudiation;
409 else if (id == QLatin1String(KeyEncipherment_id))
410 return KeyEncipherment;
411 else if (id == QLatin1String(DataEncipherment_id))
412 return DataEncipherment;
413 else if (id == QLatin1String(KeyAgreement_id))
414 return KeyAgreement;
415 else if (id == QLatin1String(KeyCertificateSign_id))
416 return KeyCertificateSign;
417 else if (id == QLatin1String(CRLSign_id))
418 return CRLSign;
419 else if (id == QLatin1String(EncipherOnly_id))
420 return EncipherOnly;
421 else if (id == QLatin1String(DecipherOnly_id))
422 return DecipherOnly;
423 else if (id == QLatin1String(ServerAuth_id))
424 return ServerAuth;
425 else if (id == QLatin1String(ClientAuth_id))
426 return ClientAuth;
427 else if (id == QLatin1String(CodeSigning_id))
428 return CodeSigning;
429 else if (id == QLatin1String(EmailProtection_id))
430 return EmailProtection;
431 else if (id == QLatin1String(IPSecEndSystem_id))
432 return IPSecEndSystem;
433 else if (id == QLatin1String(IPSecTunnel_id))
434 return IPSecTunnel;
435 else if (id == QLatin1String(IPSecUser_id))
436 return IPSecUser;
437 else if (id == QLatin1String(TimeStamping_id))
438 return TimeStamping;
439 else if (id == QLatin1String(OCSPSigning_id))
440 return OCSPSigning;
441 else
442 return -1;
443}
444
445static ConstraintType::Section constraintKnownToSection(ConstraintTypeKnown k)
446{
447 switch (k) {
448 case DigitalSignature:
449 case NonRepudiation:
450 case KeyEncipherment:
451 case DataEncipherment:
452 case KeyAgreement:
453 case KeyCertificateSign:
454 case CRLSign:
455 case EncipherOnly:
456 case DecipherOnly:
457 return ConstraintType::KeyUsage;
458 default:
459 break;
460 }
461 return ConstraintType::ExtendedKeyUsage;
462}
463
464static QString dnLabel(const CertificateInfoType &type)
465{
466 const char *str = knownToShortName(k: type.known());
467 if (str)
468 return QString::fromLatin1(ba: str);
469
470 const QString id = type.id();
471 // is it an oid?
472 if (id[0].isDigit())
473 return QStringLiteral("OID.") + id;
474
475 return QStringLiteral("qca.") + id;
476}
477
478QString orderedToDNString(const CertificateInfoOrdered &in)
479{
480 QStringList parts;
481 foreach (const CertificateInfoPair &i, in) {
482 if (i.type().section() != CertificateInfoType::DN)
483 continue;
484
485 const QString name = dnLabel(type: i.type());
486 parts += name + QLatin1Char('=') + i.value();
487 }
488 return parts.join(QStringLiteral(", "));
489}
490
491CertificateInfoOrdered orderedDNOnly(const CertificateInfoOrdered &in)
492{
493 CertificateInfoOrdered out;
494 for (int n = 0; n < in.count(); ++n) {
495 if (in[n].type().section() == CertificateInfoType::DN)
496 out += in[n];
497 }
498 return out;
499}
500
501static QString baseCertName(const CertificateInfo &info)
502{
503 QString str = info.value(key: CommonName);
504 if (str.isEmpty()) {
505 str = info.value(key: Organization);
506 if (str.isEmpty())
507 str = QStringLiteral("Unnamed");
508 }
509 return str;
510}
511
512static QList<int> findSameName(const QString &name, const QStringList &list)
513{
514 QList<int> out;
515 for (int n = 0; n < list.count(); ++n) {
516 if (list[n] == name)
517 out += n;
518 }
519 return out;
520}
521
522static QString
523uniqueSubjectValue(const CertificateInfoType &type, const QList<int> &items, const QList<Certificate> &certs, int i)
524{
525 QStringList vals = certs[items[i]].subjectInfo().values(key: type);
526 if (!vals.isEmpty()) {
527 foreach (int n, items) {
528 if (n == items[i])
529 continue;
530
531 const QStringList other_vals = certs[n].subjectInfo().values(key: type);
532 for (int k = 0; k < vals.count(); ++k) {
533 if (other_vals.contains(str: vals[k])) {
534 vals.removeAt(i: k);
535 break;
536 }
537 }
538
539 if (vals.isEmpty())
540 break;
541 }
542
543 if (!vals.isEmpty())
544 return vals[0];
545 }
546
547 return QString();
548}
549
550static QString uniqueIssuerName(const QList<int> &items, const QList<Certificate> &certs, int i)
551{
552 const QString val = baseCertName(info: certs[items[i]].issuerInfo());
553
554 bool found = false;
555 foreach (int n, items) {
556 if (n == items[i])
557 continue;
558
559 const QString other_val = baseCertName(info: certs[n].issuerInfo());
560 if (other_val == val) {
561 found = true;
562 break;
563 }
564 }
565
566 if (!found)
567 return val;
568
569 return QString();
570}
571
572static const char *constraintToString(const ConstraintType &type)
573{
574 switch (type.known()) {
575 case DigitalSignature:
576 return "DigitalSignature";
577 case NonRepudiation:
578 return "NonRepudiation";
579 case KeyEncipherment:
580 return "KeyEncipherment";
581 case DataEncipherment:
582 return "DataEncipherment";
583 case KeyAgreement:
584 return "KeyAgreement";
585 case KeyCertificateSign:
586 return "KeyCertificateSign";
587 case CRLSign:
588 return "CRLSign";
589 case EncipherOnly:
590 return "EncipherOnly";
591 case DecipherOnly:
592 return "DecipherOnly";
593 case ServerAuth:
594 return "ServerAuth";
595 case ClientAuth:
596 return "ClientAuth";
597 case CodeSigning:
598 return "CodeSigning";
599 case EmailProtection:
600 return "EmailProtection";
601 case IPSecEndSystem:
602 return "IPSecEndSystem";
603 case IPSecTunnel:
604 return "IPSecTunnel";
605 case IPSecUser:
606 return "IPSecUser";
607 case TimeStamping:
608 return "TimeStamping";
609 case OCSPSigning:
610 return "OCSPSigning";
611 }
612 return nullptr;
613}
614
615static QString
616uniqueConstraintValue(const ConstraintType &type, const QList<int> &items, const QList<Certificate> &certs, int i)
617{
618 if (certs[items[i]].constraints().contains(t: type)) {
619 bool found = false;
620 foreach (int n, items) {
621 if (n == items[i])
622 continue;
623
624 Constraints other_vals = certs[n].constraints();
625 if (other_vals.contains(t: type)) {
626 found = true;
627 break;
628 }
629 }
630
631 if (!found)
632 return QString::fromLatin1(ba: constraintToString(type));
633 }
634
635 return QString();
636}
637
638static QString makeUniqueName(const QList<int> &items, const QStringList &list, const QList<Certificate> &certs, int i)
639{
640 QString str, name;
641
642 // different organization?
643 str = uniqueSubjectValue(type: Organization, items, certs, i);
644 if (!str.isEmpty()) {
645 name = list[items[i]] + QStringLiteral(" of ") + str;
646 goto end;
647 }
648
649 // different organizational unit?
650 str = uniqueSubjectValue(type: OrganizationalUnit, items, certs, i);
651 if (!str.isEmpty()) {
652 name = list[items[i]] + QStringLiteral(" of ") + str;
653 goto end;
654 }
655
656 // different email address?
657 str = uniqueSubjectValue(type: Email, items, certs, i);
658 if (!str.isEmpty()) {
659 name = list[items[i]] + QStringLiteral(" <") + str + QLatin1Char('>');
660 goto end;
661 }
662
663 // different xmpp addresses?
664 str = uniqueSubjectValue(type: XMPP, items, certs, i);
665 if (!str.isEmpty()) {
666 name = list[items[i]] + QStringLiteral(" <xmpp:") + str + QLatin1Char('>');
667 goto end;
668 }
669
670 // different issuers?
671 str = uniqueIssuerName(items, certs, i);
672 if (!str.isEmpty()) {
673 name = list[items[i]] + QStringLiteral(" by ") + str;
674 goto end;
675 }
676
677 // different usages?
678
679 // DigitalSignature
680 str = uniqueConstraintValue(type: DigitalSignature, items, certs, i);
681 if (!str.isEmpty()) {
682 name = list[items[i]] + QStringLiteral(" for ") + str;
683 goto end;
684 }
685
686 // ClientAuth
687 str = uniqueConstraintValue(type: ClientAuth, items, certs, i);
688 if (!str.isEmpty()) {
689 name = list[items[i]] + QStringLiteral(" for ") + str;
690 goto end;
691 }
692
693 // EmailProtection
694 str = uniqueConstraintValue(type: EmailProtection, items, certs, i);
695 if (!str.isEmpty()) {
696 name = list[items[i]] + QStringLiteral(" for ") + str;
697 goto end;
698 }
699
700 // DataEncipherment
701 str = uniqueConstraintValue(type: DataEncipherment, items, certs, i);
702 if (!str.isEmpty()) {
703 name = list[items[i]] + QStringLiteral(" for ") + str;
704 goto end;
705 }
706
707 // EncipherOnly
708 str = uniqueConstraintValue(type: EncipherOnly, items, certs, i);
709 if (!str.isEmpty()) {
710 name = list[items[i]] + QStringLiteral(" for ") + str;
711 goto end;
712 }
713
714 // DecipherOnly
715 str = uniqueConstraintValue(type: DecipherOnly, items, certs, i);
716 if (!str.isEmpty()) {
717 name = list[items[i]] + QStringLiteral(" for ") + str;
718 goto end;
719 }
720
721 // if there's nothing easily unique, then do a DN string
722 name = certs[items[i]].subjectInfoOrdered().toString();
723
724end:
725 return name;
726}
727
728QStringList makeFriendlyNames(const QList<Certificate> &list)
729{
730 QStringList names;
731
732 // give a base name to all certs first
733 foreach (const Certificate &cert, list)
734 names += baseCertName(info: cert.subjectInfo());
735
736 // come up with a collision list
737 QList<QList<int>> itemCollisions;
738 foreach (const QString &name, names) {
739 // anyone else using this name?
740 const QList<int> items = findSameName(name, list: names);
741 if (items.count() > 1) {
742 // don't save duplicate collisions
743 bool haveAlready = false;
744 foreach (const QList<int> &other, itemCollisions) {
745 foreach (int n, items) {
746 if (other.contains(t: n)) {
747 haveAlready = true;
748 break;
749 }
750 }
751
752 if (haveAlready)
753 break;
754 }
755
756 if (haveAlready)
757 continue;
758
759 itemCollisions += items;
760 }
761 }
762
763 // resolve collisions by providing extra details
764 foreach (const QList<int> &items, itemCollisions) {
765 // printf("%d items are using [%s]\n", items.count(), qPrintable(names[items[0]]));
766
767 for (int n = 0; n < items.count(); ++n) {
768 names[items[n]] = makeUniqueName(items, list: names, certs: list, i: n);
769 // printf(" %d: reassigning: [%s]\n", items[n], qPrintable(names[items[n]]));
770 }
771 }
772
773 return names;
774}
775
776//----------------------------------------------------------------------------
777// CertificateInfoType
778//----------------------------------------------------------------------------
779class CertificateInfoType::Private : public QSharedData
780{
781public:
782 CertificateInfoType::Section section;
783 int known;
784 QString id;
785
786 Private()
787 : section(CertificateInfoType::DN)
788 , known(-1)
789 {
790 }
791};
792
793CertificateInfoType::CertificateInfoType()
794 : d(new Private)
795{
796}
797
798CertificateInfoType::CertificateInfoType(CertificateInfoTypeKnown known)
799 : d(new Private)
800{
801 d->section = knownToSection(k: known);
802 d->known = known;
803 d->id = knownToId(k: known); // always valid
804}
805
806CertificateInfoType::CertificateInfoType(const QString &id, Section section)
807 : d(new Private)
808{
809 d->section = section;
810 d->known = idToKnown(id); // can be -1 for unknown
811 d->id = id;
812}
813
814CertificateInfoType::CertificateInfoType(const CertificateInfoType &from)
815 : d(from.d)
816{
817}
818
819CertificateInfoType::~CertificateInfoType()
820{
821}
822
823CertificateInfoType &CertificateInfoType::operator=(const CertificateInfoType &from)
824{
825 d = from.d;
826 return *this;
827}
828
829CertificateInfoType::Section CertificateInfoType::section() const
830{
831 return d->section;
832}
833
834CertificateInfoTypeKnown CertificateInfoType::known() const
835{
836 return (CertificateInfoTypeKnown)d->known;
837}
838
839QString CertificateInfoType::id() const
840{
841 return d->id;
842}
843
844bool CertificateInfoType::operator<(const CertificateInfoType &other) const
845{
846 // sort by knowns (in enum order), then by ids (in string order)
847 if (d->known != -1) {
848 if (other.d->known == -1)
849 return true;
850 else if (d->known < other.d->known)
851 return true;
852 else
853 return false;
854 } else {
855 if (other.d->known != -1)
856 return false;
857 else if (d->id < other.d->id)
858 return true;
859 else
860 return false;
861 }
862}
863
864bool CertificateInfoType::operator==(const CertificateInfoType &other) const
865{
866 // are both known types?
867 if (d->known != -1 && other.d->known != -1) {
868 // if so, compare the ints
869 if (d->known != other.d->known)
870 return false;
871 } else {
872 // otherwise, compare the string ids
873 if (d->id != other.d->id)
874 return false;
875 }
876
877 if (d->section != other.d->section)
878 return false;
879
880 return true;
881}
882
883//----------------------------------------------------------------------------
884// CertificateInfoPair
885//----------------------------------------------------------------------------
886class CertificateInfoPair::Private : public QSharedData
887{
888public:
889 CertificateInfoType type;
890 QString value;
891};
892
893CertificateInfoPair::CertificateInfoPair()
894 : d(new Private)
895{
896}
897
898CertificateInfoPair::CertificateInfoPair(const CertificateInfoType &type, const QString &value)
899 : d(new Private)
900{
901 d->type = type;
902 d->value = value;
903}
904
905CertificateInfoPair::CertificateInfoPair(const CertificateInfoPair &from)
906 : d(from.d)
907{
908}
909
910CertificateInfoPair::~CertificateInfoPair()
911{
912}
913
914CertificateInfoPair &CertificateInfoPair::operator=(const CertificateInfoPair &from)
915{
916 d = from.d;
917 return *this;
918}
919
920CertificateInfoType CertificateInfoPair::type() const
921{
922 return d->type;
923}
924
925QString CertificateInfoPair::value() const
926{
927 return d->value;
928}
929
930bool CertificateInfoPair::operator==(const CertificateInfoPair &other) const
931{
932 if (d->type == other.d->type && d->value == other.d->value)
933 return true;
934 return false;
935}
936
937//----------------------------------------------------------------------------
938// ConstraintType
939//----------------------------------------------------------------------------
940class ConstraintType::Private : public QSharedData
941{
942public:
943 ConstraintType::Section section;
944 int known;
945 QString id;
946
947 Private()
948 : section(ConstraintType::KeyUsage)
949 , known(-1)
950 {
951 }
952};
953
954ConstraintType::ConstraintType()
955 : d(new Private)
956{
957}
958
959ConstraintType::ConstraintType(ConstraintTypeKnown known)
960 : d(new Private)
961{
962 d->section = constraintKnownToSection(k: known);
963 d->known = known;
964 d->id = constraintKnownToId(k: known); // always valid
965}
966
967ConstraintType::ConstraintType(const QString &id, Section section)
968 : d(new Private)
969{
970 d->section = section;
971 d->known = constraintIdToKnown(id); // can be -1 for unknown
972 d->id = id;
973}
974
975ConstraintType::ConstraintType(const ConstraintType &from)
976 : d(from.d)
977{
978}
979
980ConstraintType::~ConstraintType()
981{
982}
983
984ConstraintType &ConstraintType::operator=(const ConstraintType &from)
985{
986 d = from.d;
987 return *this;
988}
989
990ConstraintType::Section ConstraintType::section() const
991{
992 return d->section;
993}
994
995ConstraintTypeKnown ConstraintType::known() const
996{
997 return (ConstraintTypeKnown)d->known;
998}
999
1000QString ConstraintType::id() const
1001{
1002 return d->id;
1003}
1004
1005bool ConstraintType::operator<(const ConstraintType &other) const
1006{
1007 // sort by knowns (in enum order), then by ids (in string order)
1008 if (d->known != -1) {
1009 if (other.d->known == -1)
1010 return true;
1011 else if (d->known < other.d->known)
1012 return true;
1013 else
1014 return false;
1015 } else {
1016 if (other.d->known != -1)
1017 return false;
1018 else if (d->id < other.d->id)
1019 return true;
1020 else
1021 return false;
1022 }
1023}
1024
1025bool ConstraintType::operator==(const ConstraintType &other) const
1026{
1027 // are both known types?
1028 if (d->known != -1 && other.d->known != -1) {
1029 // if so, compare the ints
1030 if (d->known != other.d->known)
1031 return false;
1032 } else {
1033 // otherwise, compare the string ids
1034 if (d->id != other.d->id)
1035 return false;
1036 }
1037
1038 if (d->section != other.d->section)
1039 return false;
1040
1041 return true;
1042}
1043
1044//----------------------------------------------------------------------------
1045// CertificateOptions
1046//----------------------------------------------------------------------------
1047class CertificateOptions::Private
1048{
1049public:
1050 CertificateRequestFormat format;
1051
1052 QString challenge;
1053 CertificateInfoOrdered info;
1054 CertificateInfo infoMap;
1055 Constraints constraints;
1056 QStringList policies;
1057 QStringList crlLocations, issuerLocations, ocspLocations;
1058 bool isCA;
1059 int pathLimit;
1060 BigInteger serial;
1061 QDateTime start, end;
1062
1063 Private()
1064 : isCA(false)
1065 , pathLimit(0)
1066 {
1067 }
1068};
1069
1070CertificateOptions::CertificateOptions(CertificateRequestFormat f)
1071{
1072 d = new Private;
1073 d->format = f;
1074}
1075
1076CertificateOptions::CertificateOptions(const CertificateOptions &from)
1077{
1078 d = new Private(*from.d);
1079}
1080
1081CertificateOptions::~CertificateOptions()
1082{
1083 delete d;
1084}
1085
1086CertificateOptions &CertificateOptions::operator=(const CertificateOptions &from)
1087{
1088 *d = *from.d;
1089 return *this;
1090}
1091
1092CertificateRequestFormat CertificateOptions::format() const
1093{
1094 return d->format;
1095}
1096
1097void CertificateOptions::setFormat(CertificateRequestFormat f)
1098{
1099 d->format = f;
1100}
1101
1102bool CertificateOptions::isValid() const
1103{
1104 // logic from Botan
1105 if (d->infoMap.value(key: CommonName).isEmpty() || d->infoMap.value(key: Country).isEmpty())
1106 return false;
1107 if (d->infoMap.value(key: Country).length() != 2)
1108 return false;
1109 if (d->start >= d->end)
1110 return false;
1111 return true;
1112}
1113
1114QString CertificateOptions::challenge() const
1115{
1116 return d->challenge;
1117}
1118
1119CertificateInfo CertificateOptions::info() const
1120{
1121 return d->infoMap;
1122}
1123
1124CertificateInfoOrdered CertificateOptions::infoOrdered() const
1125{
1126 return d->info;
1127}
1128
1129Constraints CertificateOptions::constraints() const
1130{
1131 return d->constraints;
1132}
1133
1134QStringList CertificateOptions::policies() const
1135{
1136 return d->policies;
1137}
1138
1139QStringList CertificateOptions::crlLocations() const
1140{
1141 return d->crlLocations;
1142}
1143
1144QStringList CertificateOptions::issuerLocations() const
1145{
1146 return d->issuerLocations;
1147}
1148
1149QStringList CertificateOptions::ocspLocations() const
1150{
1151 return d->ocspLocations;
1152}
1153
1154bool CertificateOptions::isCA() const
1155{
1156 return d->isCA;
1157}
1158
1159int CertificateOptions::pathLimit() const
1160{
1161 return d->pathLimit;
1162}
1163
1164BigInteger CertificateOptions::serialNumber() const
1165{
1166 return d->serial;
1167}
1168
1169QDateTime CertificateOptions::notValidBefore() const
1170{
1171 return d->start;
1172}
1173
1174QDateTime CertificateOptions::notValidAfter() const
1175{
1176 return d->end;
1177}
1178
1179void CertificateOptions::setChallenge(const QString &s)
1180{
1181 d->challenge = s;
1182}
1183
1184void CertificateOptions::setInfo(const CertificateInfo &info)
1185{
1186 d->info = mapToOrdered(info);
1187 d->infoMap = info;
1188}
1189
1190void CertificateOptions::setInfoOrdered(const CertificateInfoOrdered &info)
1191{
1192 d->info = info;
1193 d->infoMap = orderedToMap(info);
1194}
1195
1196void CertificateOptions::setConstraints(const Constraints &constraints)
1197{
1198 d->constraints = constraints;
1199}
1200
1201void CertificateOptions::setPolicies(const QStringList &policies)
1202{
1203 d->policies = policies;
1204}
1205
1206void CertificateOptions::setCRLLocations(const QStringList &locations)
1207{
1208 d->crlLocations = locations;
1209}
1210
1211void CertificateOptions::setIssuerLocations(const QStringList &locations)
1212{
1213 d->issuerLocations = locations;
1214}
1215
1216void CertificateOptions::setOCSPLocations(const QStringList &locations)
1217{
1218 d->ocspLocations = locations;
1219}
1220
1221void CertificateOptions::setAsCA(int pathLimit)
1222{
1223 d->isCA = true;
1224 d->pathLimit = pathLimit;
1225}
1226
1227void CertificateOptions::setAsUser()
1228{
1229 d->isCA = false;
1230 d->pathLimit = 0;
1231}
1232
1233void CertificateOptions::setSerialNumber(const BigInteger &i)
1234{
1235 d->serial = i;
1236}
1237
1238void CertificateOptions::setValidityPeriod(const QDateTime &start, const QDateTime &end)
1239{
1240 d->start = start;
1241 d->end = end;
1242}
1243
1244//----------------------------------------------------------------------------
1245// Certificate
1246//----------------------------------------------------------------------------
1247// ip address string to binary (msb), adapted from jdns (adapted from qt)
1248// return: size 4 = ipv4, size 16 = ipv6, size 0 = error
1249static QByteArray ipaddr_str2bin(const QString &str)
1250{
1251 // ipv6
1252 if (str.contains(c: QLatin1Char(':'))) {
1253 const QStringList parts = str.split(sep: QLatin1Char(':'), behavior: Qt::KeepEmptyParts);
1254 if (parts.count() < 3 || parts.count() > 8)
1255 return QByteArray();
1256
1257 QByteArray ipv6(16, 0);
1258 int at = 16;
1259 int fill = 9 - parts.count();
1260 for (int n = parts.count() - 1; n >= 0; --n) {
1261 if (at <= 0)
1262 return QByteArray();
1263
1264 if (parts[n].isEmpty()) {
1265 if (n == parts.count() - 1) {
1266 if (!parts[n - 1].isEmpty())
1267 return QByteArray();
1268 ipv6[--at] = 0;
1269 ipv6[--at] = 0;
1270 } else if (n == 0) {
1271 if (!parts[n + 1].isEmpty())
1272 return QByteArray();
1273 ipv6[--at] = 0;
1274 ipv6[--at] = 0;
1275 } else {
1276 for (int i = 0; i < fill; ++i) {
1277 if (at <= 0)
1278 return QByteArray();
1279 ipv6[--at] = 0;
1280 ipv6[--at] = 0;
1281 }
1282 }
1283 } else {
1284 if (parts[n].indexOf(c: QLatin1Char('.')) == -1) {
1285 bool ok;
1286 int x = parts[n].toInt(ok: &ok, base: 16);
1287 if (!ok || x < 0 || x > 0xffff)
1288 return QByteArray();
1289 ipv6[--at] = x & 0xff;
1290 ipv6[--at] = (x >> 8) & 0xff;
1291 } else {
1292 if (n != parts.count() - 1)
1293 return QByteArray();
1294
1295 const QByteArray buf = ipaddr_str2bin(str: parts[n]);
1296 if (buf.isEmpty())
1297 return QByteArray();
1298
1299 ipv6[--at] = buf[3];
1300 ipv6[--at] = buf[2];
1301 ipv6[--at] = buf[1];
1302 ipv6[--at] = buf[0];
1303 --fill;
1304 }
1305 }
1306 }
1307
1308 return ipv6;
1309 } else if (str.contains(c: QLatin1Char('.'))) {
1310 const QStringList parts = str.split(sep: QLatin1Char('.'), behavior: Qt::KeepEmptyParts);
1311 if (parts.count() != 4)
1312 return QByteArray();
1313
1314 QByteArray out(4, 0);
1315 for (int n = 0; n < 4; ++n) {
1316 bool ok;
1317 int x = parts[n].toInt(ok: &ok);
1318 if (!ok || x < 0 || x > 0xff)
1319 return QByteArray();
1320 out[n] = (unsigned char)x;
1321 }
1322 return out;
1323 } else
1324 return QByteArray();
1325}
1326
1327// acedomain must be all lowercase, with no trailing dot or wildcards
1328static bool cert_match_domain(const QString &certname, const QString &acedomain)
1329{
1330 // KSSL strips start/end whitespace, even though such whitespace is
1331 // probably not legal anyway. (compat)
1332 QString name = certname.trimmed();
1333
1334 // KSSL strips trailing dot, even though the dot is probably not
1335 // legal anyway. (compat)
1336 if (name.length() > 0 && name[name.length() - 1] == QLatin1Char('.'))
1337 name.truncate(pos: name.length() - 1);
1338
1339 // after our compatibility modifications, make sure the name isn't
1340 // empty.
1341 if (name.isEmpty())
1342 return false;
1343
1344 // lowercase, for later performing case insensitive matching
1345 name = name.toLower();
1346
1347 // ensure the cert field contains valid characters only
1348 if (QRegularExpression(QStringLiteral("[^a-z0-9\\.\\*\\-]")).match(subject: name).hasMatch())
1349 return false;
1350
1351 // hack into parts, and require at least 1 part
1352 const QStringList parts_name = name.split(sep: QLatin1Char('.'), behavior: Qt::KeepEmptyParts);
1353 if (parts_name.isEmpty())
1354 return false;
1355
1356 // KSSL checks to make sure the last two parts don't contain
1357 // wildcards. I don't know where it is written that this
1358 // should be done, but for compat sake we'll do it.
1359 if (parts_name[parts_name.count() - 1].contains(c: QLatin1Char('*')))
1360 return false;
1361 if (parts_name.count() >= 2 && parts_name[parts_name.count() - 2].contains(c: QLatin1Char('*')))
1362 return false;
1363
1364 const QStringList parts_compare = acedomain.split(sep: QLatin1Char('.'), behavior: Qt::KeepEmptyParts);
1365 if (parts_compare.isEmpty())
1366 return false;
1367
1368 // don't allow empty parts
1369 foreach (const QString &s, parts_name) {
1370 if (s.isEmpty())
1371 return false;
1372 }
1373 foreach (const QString &s, parts_compare) {
1374 if (s.isEmpty())
1375 return false;
1376 }
1377
1378 // RFC2818: "Names may contain the wildcard character * which is
1379 // considered to match any single domain name component or
1380 // component fragment. E.g., *.a.com matches foo.a.com but not
1381 // bar.foo.a.com. f*.com matches foo.com but not bar.com."
1382 //
1383 // This means that for the domain to match it must have the
1384 // same number of components, wildcards or not. If there are
1385 // wildcards, their scope must only be within the component
1386 // they reside in.
1387 //
1388 // First, make sure the number of parts is equal.
1389 if (parts_name.count() != parts_compare.count())
1390 return false;
1391
1392 // Now compare each part
1393 for (int n = 0; n < parts_name.count(); ++n) {
1394 const QString &p1 = parts_name[n];
1395 const QString &p2 = parts_compare[n];
1396
1397 if (!QRegExp(p1, Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(str: p2))
1398 return false;
1399 }
1400
1401 return true;
1402}
1403
1404// ipaddress must be an ipv4 or ipv6 address in binary format
1405static bool cert_match_ipaddress(const QString &certname, const QByteArray &ipaddress)
1406{
1407 // KSSL strips start/end whitespace, even though such whitespace is
1408 // probably not legal anyway. (compat)
1409 QString name = certname.trimmed();
1410
1411 // KSSL accepts IPv6 in brackets, which is usually done for URIs, but
1412 // IMO sounds very strange for a certificate. We'll follow this
1413 // behavior anyway. (compat)
1414 if (name.length() >= 2 && name[0] == QLatin1Char('[') && name[name.length() - 1] == QLatin1Char(']'))
1415 name = name.mid(position: 1, n: name.length() - 2); // chop off brackets
1416
1417 // after our compatibility modifications, make sure the name isn't
1418 // empty.
1419 if (name.isEmpty())
1420 return false;
1421
1422 // convert to binary form
1423 const QByteArray addr = ipaddr_str2bin(str: name);
1424 if (addr.isEmpty())
1425 return false;
1426
1427 // not the same?
1428 if (addr != ipaddress)
1429 return false;
1430
1431 return true;
1432}
1433
1434class Certificate::Private : public QSharedData
1435{
1436public:
1437 CertificateInfo subjectInfoMap, issuerInfoMap;
1438
1439 void update(CertContext *c)
1440 {
1441 if (c) {
1442 subjectInfoMap = orderedToMap(info: c->props()->subject);
1443 issuerInfoMap = orderedToMap(info: c->props()->issuer);
1444 } else {
1445 subjectInfoMap = CertificateInfo();
1446 issuerInfoMap = CertificateInfo();
1447 }
1448 }
1449};
1450
1451Certificate::Certificate()
1452 : d(new Private)
1453{
1454}
1455
1456Certificate::Certificate(const QString &fileName)
1457 : d(new Private)
1458{
1459 *this = fromPEMFile(fileName, result: nullptr, provider: QString());
1460}
1461
1462Certificate::Certificate(const CertificateOptions &opts, const PrivateKey &key, const QString &provider)
1463 : d(new Private)
1464{
1465 CertContext *c = static_cast<CertContext *>(getContext(QStringLiteral("cert"), provider));
1466 if (c->createSelfSigned(opts, priv: *(static_cast<const PKeyContext *>(key.context()))))
1467 change(c);
1468 else
1469 delete c;
1470}
1471
1472Certificate::Certificate(const Certificate &from)
1473 : Algorithm(from)
1474 , d(from.d)
1475{
1476}
1477
1478Certificate::~Certificate()
1479{
1480}
1481
1482Certificate &Certificate::operator=(const Certificate &from)
1483{
1484 Algorithm::operator=(from);
1485 d = from.d;
1486 return *this;
1487}
1488
1489bool Certificate::isNull() const
1490{
1491 return (!context() ? true : false);
1492}
1493
1494QDateTime Certificate::notValidBefore() const
1495{
1496 return static_cast<const CertContext *>(context())->props()->start;
1497}
1498
1499QDateTime Certificate::notValidAfter() const
1500{
1501 return static_cast<const CertContext *>(context())->props()->end;
1502}
1503
1504CertificateInfo Certificate::subjectInfo() const
1505{
1506 return d->subjectInfoMap;
1507}
1508
1509CertificateInfoOrdered Certificate::subjectInfoOrdered() const
1510{
1511 return static_cast<const CertContext *>(context())->props()->subject;
1512}
1513
1514CertificateInfo Certificate::issuerInfo() const
1515{
1516 return d->issuerInfoMap;
1517}
1518
1519CertificateInfoOrdered Certificate::issuerInfoOrdered() const
1520{
1521 return static_cast<const CertContext *>(context())->props()->issuer;
1522}
1523
1524Constraints Certificate::constraints() const
1525{
1526 return static_cast<const CertContext *>(context())->props()->constraints;
1527}
1528
1529QStringList Certificate::policies() const
1530{
1531 return static_cast<const CertContext *>(context())->props()->policies;
1532}
1533
1534QStringList Certificate::crlLocations() const
1535{
1536 return static_cast<const CertContext *>(context())->props()->crlLocations;
1537}
1538
1539QStringList Certificate::issuerLocations() const
1540{
1541 return static_cast<const CertContext *>(context())->props()->issuerLocations;
1542}
1543
1544QStringList Certificate::ocspLocations() const
1545{
1546 return static_cast<const CertContext *>(context())->props()->ocspLocations;
1547}
1548
1549QString Certificate::commonName() const
1550{
1551 return d->subjectInfoMap.value(key: CommonName);
1552}
1553
1554BigInteger Certificate::serialNumber() const
1555{
1556 return static_cast<const CertContext *>(context())->props()->serial;
1557}
1558
1559PublicKey Certificate::subjectPublicKey() const
1560{
1561 PKeyContext *c = static_cast<const CertContext *>(context())->subjectPublicKey();
1562 PublicKey key;
1563 key.change(c);
1564 return key;
1565}
1566
1567bool Certificate::isCA() const
1568{
1569 return static_cast<const CertContext *>(context())->props()->isCA;
1570}
1571
1572bool Certificate::isSelfSigned() const
1573{
1574 return static_cast<const CertContext *>(context())->props()->isSelfSigned;
1575}
1576
1577bool Certificate::isIssuerOf(const Certificate &other) const
1578{
1579 const CertContext *cc = static_cast<const CertContext *>(other.context());
1580 return static_cast<const CertContext *>(context())->isIssuerOf(other: cc);
1581}
1582
1583int Certificate::pathLimit() const
1584{
1585 return static_cast<const CertContext *>(context())->props()->pathLimit;
1586}
1587
1588SignatureAlgorithm Certificate::signatureAlgorithm() const
1589{
1590 return static_cast<const CertContext *>(context())->props()->sigalgo;
1591}
1592
1593QByteArray Certificate::subjectKeyId() const
1594{
1595 return static_cast<const CertContext *>(context())->props()->subjectId;
1596}
1597
1598QByteArray Certificate::issuerKeyId() const
1599{
1600 return static_cast<const CertContext *>(context())->props()->issuerId;
1601}
1602
1603Validity Certificate::validate(const CertificateCollection &trusted,
1604 const CertificateCollection &untrusted,
1605 UsageMode u,
1606 ValidateFlags vf) const
1607{
1608 const QList<Certificate> issuers = trusted.certificates() + untrusted.certificates();
1609 CertificateChain chain;
1610 chain += *this;
1611 Validity result;
1612 chain = chain.complete(issuers, result: &result);
1613 if (result != ValidityGood)
1614 return result;
1615 return chain.validate(trusted, untrusted_crls: untrusted.crls(), u, vf);
1616}
1617
1618QByteArray Certificate::toDER() const
1619{
1620 return static_cast<const CertContext *>(context())->toDER();
1621}
1622
1623QString Certificate::toPEM() const
1624{
1625 return static_cast<const CertContext *>(context())->toPEM();
1626}
1627
1628bool Certificate::toPEMFile(const QString &fileName) const
1629{
1630 return stringToFile(fileName, content: toPEM());
1631}
1632
1633Certificate Certificate::fromDER(const QByteArray &a, ConvertResult *result, const QString &provider)
1634{
1635 Certificate c;
1636 CertContext *cc = static_cast<CertContext *>(getContext(QStringLiteral("cert"), provider));
1637 ConvertResult r = cc->fromDER(a);
1638 if (result)
1639 *result = r;
1640 if (r == ConvertGood)
1641 c.change(c: cc);
1642 else
1643 delete cc;
1644 return c;
1645}
1646
1647Certificate Certificate::fromPEM(const QString &s, ConvertResult *result, const QString &provider)
1648{
1649 Certificate c;
1650 CertContext *cc = static_cast<CertContext *>(getContext(QStringLiteral("cert"), provider));
1651 ConvertResult r = cc->fromPEM(s);
1652 if (result)
1653 *result = r;
1654 if (r == ConvertGood)
1655 c.change(c: cc);
1656 else
1657 delete cc;
1658 return c;
1659}
1660
1661Certificate Certificate::fromPEMFile(const QString &fileName, ConvertResult *result, const QString &provider)
1662{
1663 QString pem;
1664 if (!stringFromFile(fileName, s: &pem)) {
1665 if (result)
1666 *result = ErrorFile;
1667 return Certificate();
1668 }
1669 return fromPEM(s: pem, result, provider);
1670}
1671
1672// check for ip addresses in iPAddress, dNSName, then commonName
1673// for all else, check in dNSName, then commonName
1674bool Certificate::matchesHostName(const QString &host) const
1675{
1676 const QByteArray ipaddr = ipaddr_str2bin(str: host);
1677 if (!ipaddr.isEmpty()) // ip address
1678 {
1679 // check iPAddress, dNSName, commonName
1680 const CertificateInfoOrdered subjectInfo = subjectInfoOrdered();
1681 for (const CertificateInfoPair &p : subjectInfo) {
1682 const CertificateInfoType type = p.type();
1683 if (type == IPAddress || type == DNS || type == CommonName) {
1684 if (cert_match_ipaddress(certname: p.value(), ipaddress: ipaddr))
1685 return true;
1686 }
1687 }
1688 } else // domain
1689 {
1690 // lowercase
1691 QString name = host.toLower();
1692
1693 // ACE
1694 name = QString::fromLatin1(ba: QUrl::toAce(domain: name));
1695
1696 // don't allow wildcards in the comparison host
1697 if (name.contains(c: QLatin1Char('*')))
1698 return false;
1699
1700 // strip out trailing dot
1701 if (name.length() > 0 && name[name.length() - 1] == QLatin1Char('.'))
1702 name.truncate(pos: name.length() - 1);
1703
1704 // make sure the name is not empty after our modifications
1705 if (name.isEmpty())
1706 return false;
1707
1708 // check dNSName, commonName
1709 const CertificateInfoOrdered subjectInfo = subjectInfoOrdered();
1710 for (const CertificateInfoPair &p : subjectInfo) {
1711 const CertificateInfoType type = p.type();
1712 if (type == DNS || type == CommonName) {
1713 if (cert_match_domain(certname: p.value(), acedomain: name))
1714 return true;
1715 }
1716 }
1717 }
1718
1719 return false;
1720}
1721
1722bool Certificate::operator==(const Certificate &otherCert) const
1723{
1724 if (isNull()) {
1725 if (otherCert.isNull())
1726 return true;
1727 else
1728 return false;
1729 } else if (otherCert.isNull())
1730 return false;
1731
1732 const CertContext *other = static_cast<const CertContext *>(otherCert.context());
1733 return static_cast<const CertContext *>(context())->compare(other);
1734}
1735
1736void Certificate::change(CertContext *c)
1737{
1738 Algorithm::change(c);
1739 d->update(c: static_cast<CertContext *>(context()));
1740}
1741
1742Validity Certificate::chain_validate(const CertificateChain &chain,
1743 const CertificateCollection &trusted,
1744 const QList<CRL> &untrusted_crls,
1745 UsageMode u,
1746 ValidateFlags vf) const
1747{
1748 QList<CertContext *> chain_list;
1749 QList<CertContext *> trusted_list;
1750 QList<CRLContext *> crl_list;
1751
1752 QList<Certificate> chain_certs = chain;
1753 QList<Certificate> trusted_certs = trusted.certificates();
1754 QList<CRL> crls = trusted.crls() + untrusted_crls;
1755
1756 for (int n = 0; n < chain_certs.count(); ++n) {
1757 CertContext *c = static_cast<CertContext *>(chain_certs[n].context());
1758 chain_list += c;
1759 }
1760 for (int n = 0; n < trusted_certs.count(); ++n) {
1761 CertContext *c = static_cast<CertContext *>(trusted_certs[n].context());
1762 trusted_list += c;
1763 }
1764 for (int n = 0; n < crls.count(); ++n) {
1765 CRLContext *c = static_cast<CRLContext *>(crls[n].context());
1766 crl_list += c;
1767 }
1768
1769 return static_cast<const CertContext *>(context())->validate_chain(chain: chain_list, trusted: trusted_list, crls: crl_list, u, vf);
1770}
1771
1772CertificateChain
1773Certificate::chain_complete(const CertificateChain &chain, const QList<Certificate> &issuers, Validity *result) const
1774{
1775 CertificateChain out;
1776 QList<Certificate> pool = issuers + chain.mid(pos: 1);
1777 out += chain.first();
1778 if (result)
1779 *result = ValidityGood;
1780 while (!out.last().isSelfSigned()) {
1781 // try to get next in chain
1782 int at = -1;
1783 for (int n = 0; n < pool.count(); ++n) {
1784 // QString str = QString("[%1] issued by [%2] ? ").arg(out.last().commonName()).arg(pool[n].commonName());
1785 if (pool[n].isIssuerOf(other: out.last())) {
1786 // printf("%s yes\n", qPrintable(str));
1787 at = n;
1788 break;
1789 }
1790 // printf("%s no\n", qPrintable(str));
1791 }
1792 if (at == -1) {
1793 if (result)
1794 *result = ErrorInvalidCA;
1795 break;
1796 }
1797
1798 // take it out of the pool
1799 Certificate next = pool.takeAt(i: at);
1800
1801 // make sure it isn't in the chain already (avoid loops)
1802 if (out.contains(t: next))
1803 break;
1804
1805 // append to the chain
1806 out += next;
1807 }
1808 return out;
1809}
1810
1811//----------------------------------------------------------------------------
1812// CertificateRequest
1813//----------------------------------------------------------------------------
1814class CertificateRequest::Private : public QSharedData
1815{
1816public:
1817 CertificateInfo subjectInfoMap;
1818
1819 void update(CSRContext *c)
1820 {
1821 if (c)
1822 subjectInfoMap = orderedToMap(info: c->props()->subject);
1823 else
1824 subjectInfoMap = CertificateInfo();
1825 }
1826};
1827
1828CertificateRequest::CertificateRequest()
1829 : d(new Private)
1830{
1831}
1832
1833CertificateRequest::CertificateRequest(const QString &fileName)
1834 : d(new Private)
1835{
1836 *this = fromPEMFile(fileName, result: nullptr, provider: QString());
1837}
1838
1839CertificateRequest::CertificateRequest(const CertificateOptions &opts, const PrivateKey &key, const QString &provider)
1840 : d(new Private)
1841{
1842 CSRContext *c = static_cast<CSRContext *>(getContext(QStringLiteral("csr"), provider));
1843 if (c->createRequest(opts, priv: *(static_cast<const PKeyContext *>(key.context()))))
1844 change(c);
1845 else
1846 delete c;
1847}
1848
1849CertificateRequest::CertificateRequest(const CertificateRequest &from)
1850 : Algorithm(from)
1851 , d(from.d)
1852{
1853}
1854
1855CertificateRequest::~CertificateRequest()
1856{
1857}
1858
1859CertificateRequest &CertificateRequest::operator=(const CertificateRequest &from)
1860{
1861 Algorithm::operator=(from);
1862 d = from.d;
1863 return *this;
1864}
1865
1866bool CertificateRequest::isNull() const
1867{
1868 return (!context() ? true : false);
1869}
1870
1871bool CertificateRequest::canUseFormat(CertificateRequestFormat f, const QString &provider)
1872{
1873 CSRContext *c = static_cast<CSRContext *>(getContext(QStringLiteral("csr"), provider));
1874 bool ok = c->canUseFormat(f);
1875 delete c;
1876 return ok;
1877}
1878
1879CertificateRequestFormat CertificateRequest::format() const
1880{
1881 if (isNull())
1882 return PKCS10; // some default so we don't explode
1883 return static_cast<const CSRContext *>(context())->props()->format;
1884}
1885
1886CertificateInfo CertificateRequest::subjectInfo() const
1887{
1888 return d->subjectInfoMap;
1889}
1890
1891CertificateInfoOrdered CertificateRequest::subjectInfoOrdered() const
1892{
1893 return static_cast<const CSRContext *>(context())->props()->subject;
1894}
1895
1896Constraints CertificateRequest::constraints() const
1897{
1898 return static_cast<const CSRContext *>(context())->props()->constraints;
1899}
1900
1901QStringList CertificateRequest::policies() const
1902{
1903 return static_cast<const CSRContext *>(context())->props()->policies;
1904}
1905
1906PublicKey CertificateRequest::subjectPublicKey() const
1907{
1908 PKeyContext *c = static_cast<const CSRContext *>(context())->subjectPublicKey();
1909 PublicKey key;
1910 key.change(c);
1911 return key;
1912}
1913
1914bool CertificateRequest::isCA() const
1915{
1916 return static_cast<const CSRContext *>(context())->props()->isCA;
1917}
1918
1919int CertificateRequest::pathLimit() const
1920{
1921 return static_cast<const CSRContext *>(context())->props()->pathLimit;
1922}
1923
1924QString CertificateRequest::challenge() const
1925{
1926 return static_cast<const CSRContext *>(context())->props()->challenge;
1927}
1928
1929SignatureAlgorithm CertificateRequest::signatureAlgorithm() const
1930{
1931 return static_cast<const CSRContext *>(context())->props()->sigalgo;
1932}
1933
1934bool CertificateRequest::operator==(const CertificateRequest &otherCsr) const
1935{
1936 if (isNull()) {
1937 if (otherCsr.isNull())
1938 return true;
1939 else
1940 return false;
1941 } else if (otherCsr.isNull())
1942 return false;
1943
1944 const CSRContext *other = static_cast<const CSRContext *>(otherCsr.context());
1945 return static_cast<const CSRContext *>(context())->compare(other);
1946}
1947
1948QByteArray CertificateRequest::toDER() const
1949{
1950 return static_cast<const CSRContext *>(context())->toDER();
1951}
1952
1953QString CertificateRequest::toPEM() const
1954{
1955 return static_cast<const CSRContext *>(context())->toPEM();
1956}
1957
1958bool CertificateRequest::toPEMFile(const QString &fileName) const
1959{
1960 return stringToFile(fileName, content: toPEM());
1961}
1962
1963CertificateRequest CertificateRequest::fromDER(const QByteArray &a, ConvertResult *result, const QString &provider)
1964{
1965 CertificateRequest c;
1966 CSRContext *csr = static_cast<CSRContext *>(getContext(QStringLiteral("csr"), provider));
1967 ConvertResult r = csr->fromDER(a);
1968 if (result)
1969 *result = r;
1970 if (r == ConvertGood)
1971 c.change(c: csr);
1972 else
1973 delete csr;
1974 return c;
1975}
1976
1977CertificateRequest CertificateRequest::fromPEM(const QString &s, ConvertResult *result, const QString &provider)
1978{
1979 CertificateRequest c;
1980 CSRContext *csr = static_cast<CSRContext *>(getContext(QStringLiteral("csr"), provider));
1981 ConvertResult r = csr->fromPEM(s);
1982 if (result)
1983 *result = r;
1984 if (r == ConvertGood)
1985 c.change(c: csr);
1986 else
1987 delete csr;
1988 return c;
1989}
1990
1991CertificateRequest
1992CertificateRequest::fromPEMFile(const QString &fileName, ConvertResult *result, const QString &provider)
1993{
1994 QString pem;
1995 if (!stringFromFile(fileName, s: &pem)) {
1996 if (result)
1997 *result = ErrorFile;
1998 return CertificateRequest();
1999 }
2000 return fromPEM(s: pem, result, provider);
2001}
2002
2003QString CertificateRequest::toString() const
2004{
2005 return static_cast<const CSRContext *>(context())->toSPKAC();
2006}
2007
2008CertificateRequest CertificateRequest::fromString(const QString &s, ConvertResult *result, const QString &provider)
2009{
2010 CertificateRequest c;
2011 CSRContext *csr = static_cast<CSRContext *>(getContext(QStringLiteral("csr"), provider));
2012 ConvertResult r = csr->fromSPKAC(s);
2013 if (result)
2014 *result = r;
2015 if (r == ConvertGood)
2016 c.change(c: csr);
2017 else
2018 delete csr;
2019 return c;
2020}
2021
2022void CertificateRequest::change(CSRContext *c)
2023{
2024 Algorithm::change(c);
2025 d->update(c: static_cast<CSRContext *>(context()));
2026}
2027
2028//----------------------------------------------------------------------------
2029// CRLEntry
2030//----------------------------------------------------------------------------
2031CRLEntry::CRLEntry()
2032{
2033 _reason = Unspecified;
2034}
2035
2036CRLEntry::CRLEntry(const Certificate &c, Reason r)
2037{
2038 _serial = c.serialNumber();
2039 _time = QDateTime::currentDateTime();
2040 _reason = r;
2041}
2042
2043// TODO make serial const & when we break ABI
2044CRLEntry::CRLEntry(
2045 const BigInteger serial, // clazy:exclude=function-args-by-ref NOLINT(performance-unnecessary-value-param)
2046 const QDateTime &time,
2047 Reason r)
2048{
2049 _serial = serial;
2050 _time = time;
2051 _reason = r;
2052}
2053
2054CRLEntry::CRLEntry(const CRLEntry &from)
2055 : _serial(from._serial)
2056 , _time(from._time)
2057 , _reason(from._reason)
2058{
2059}
2060
2061CRLEntry::~CRLEntry()
2062{
2063}
2064
2065CRLEntry &CRLEntry::operator=(const CRLEntry &from)
2066{
2067 _serial = from._serial;
2068 _time = from._time;
2069 _reason = from._reason;
2070 return *this;
2071}
2072
2073bool CRLEntry::isNull() const
2074{
2075 return (_time.isNull());
2076}
2077
2078BigInteger CRLEntry::serialNumber() const
2079{
2080 return _serial;
2081}
2082
2083QDateTime CRLEntry::time() const
2084{
2085 return _time;
2086}
2087
2088CRLEntry::Reason CRLEntry::reason() const
2089{
2090 return _reason;
2091}
2092
2093bool CRLEntry::operator==(const CRLEntry &otherEntry) const
2094{
2095 if (isNull()) {
2096 if (otherEntry.isNull())
2097 return true;
2098 else
2099 return false;
2100 } else if (otherEntry.isNull())
2101 return false;
2102
2103 if ((_serial != otherEntry._serial) || (_time != otherEntry._time) || (_reason != otherEntry._reason)) {
2104 return false;
2105 }
2106 return true;
2107}
2108
2109bool CRLEntry::operator<(const CRLEntry &otherEntry) const
2110{
2111 if (isNull() || otherEntry.isNull())
2112 return false;
2113
2114 if (_serial < otherEntry._serial)
2115 return true;
2116
2117 return false;
2118}
2119
2120//----------------------------------------------------------------------------
2121// CRL
2122//----------------------------------------------------------------------------
2123class CRL::Private : public QSharedData
2124{
2125public:
2126 CertificateInfo issuerInfoMap;
2127
2128 void update(CRLContext *c)
2129 {
2130 if (c)
2131 issuerInfoMap = orderedToMap(info: c->props()->issuer);
2132 else
2133 issuerInfoMap = CertificateInfo();
2134 }
2135};
2136
2137CRL::CRL()
2138 : d(new Private)
2139{
2140}
2141
2142CRL::CRL(const CRL &from)
2143 : Algorithm(from)
2144 , d(from.d)
2145{
2146}
2147
2148CRL::~CRL()
2149{
2150}
2151
2152CRL &CRL::operator=(const CRL &from)
2153{
2154 Algorithm::operator=(from);
2155 d = from.d;
2156 return *this;
2157}
2158
2159bool CRL::isNull() const
2160{
2161 return (!context() ? true : false);
2162}
2163
2164CertificateInfo CRL::issuerInfo() const
2165{
2166 return d->issuerInfoMap;
2167}
2168
2169CertificateInfoOrdered CRL::issuerInfoOrdered() const
2170{
2171 return static_cast<const CRLContext *>(context())->props()->issuer;
2172}
2173
2174int CRL::number() const
2175{
2176 return static_cast<const CRLContext *>(context())->props()->number;
2177}
2178
2179QDateTime CRL::thisUpdate() const
2180{
2181 return static_cast<const CRLContext *>(context())->props()->thisUpdate;
2182}
2183
2184QDateTime CRL::nextUpdate() const
2185{
2186 return static_cast<const CRLContext *>(context())->props()->nextUpdate;
2187}
2188
2189QList<CRLEntry> CRL::revoked() const
2190{
2191 return static_cast<const CRLContext *>(context())->props()->revoked;
2192}
2193
2194SignatureAlgorithm CRL::signatureAlgorithm() const
2195{
2196 return static_cast<const CRLContext *>(context())->props()->sigalgo;
2197}
2198
2199QByteArray CRL::issuerKeyId() const
2200{
2201 return static_cast<const CRLContext *>(context())->props()->issuerId;
2202}
2203
2204QByteArray CRL::toDER() const
2205{
2206 return static_cast<const CRLContext *>(context())->toDER();
2207}
2208
2209QString CRL::toPEM() const
2210{
2211 return static_cast<const CRLContext *>(context())->toPEM();
2212}
2213
2214bool CRL::operator==(const CRL &otherCrl) const
2215{
2216 if (isNull()) {
2217 if (otherCrl.isNull())
2218 return true;
2219 else
2220 return false;
2221 } else if (otherCrl.isNull())
2222 return false;
2223
2224 const CRLContext *other = static_cast<const CRLContext *>(otherCrl.context());
2225 return static_cast<const CRLContext *>(context())->compare(other);
2226}
2227
2228CRL CRL::fromDER(const QByteArray &a, ConvertResult *result, const QString &provider)
2229{
2230 CRL c;
2231 CRLContext *cc = static_cast<CRLContext *>(getContext(QStringLiteral("crl"), provider));
2232 ConvertResult r = cc->fromDER(a);
2233 if (result)
2234 *result = r;
2235 if (r == ConvertGood)
2236 c.change(c: cc);
2237 else
2238 delete cc;
2239 return c;
2240}
2241
2242CRL CRL::fromPEM(const QString &s, ConvertResult *result, const QString &provider)
2243{
2244 CRL c;
2245 CRLContext *cc = static_cast<CRLContext *>(getContext(QStringLiteral("crl"), provider));
2246 ConvertResult r = cc->fromPEM(s);
2247 if (result)
2248 *result = r;
2249 if (r == ConvertGood)
2250 c.change(c: cc);
2251 else
2252 delete cc;
2253 return c;
2254}
2255
2256CRL CRL::fromPEMFile(const QString &fileName, ConvertResult *result, const QString &provider)
2257{
2258 QString pem;
2259 if (!stringFromFile(fileName, s: &pem)) {
2260 if (result)
2261 *result = ErrorFile;
2262 return CRL();
2263 }
2264 return fromPEM(s: pem, result, provider);
2265}
2266
2267bool CRL::toPEMFile(const QString &fileName) const
2268{
2269 return stringToFile(fileName, content: toPEM());
2270}
2271
2272void CRL::change(CRLContext *c)
2273{
2274 Algorithm::change(c);
2275 d->update(c: static_cast<CRLContext *>(context()));
2276}
2277
2278//----------------------------------------------------------------------------
2279// Store
2280//----------------------------------------------------------------------------
2281// CRL / X509 CRL
2282// CERTIFICATE / X509 CERTIFICATE
2283static QString readNextPem(QTextStream *ts, bool *isCRL)
2284{
2285 QString pem;
2286 bool crl = false;
2287 bool found = false;
2288 bool done = false;
2289 while (!ts->atEnd()) {
2290 const QString line = ts->readLine();
2291 if (!found) {
2292 if (line.startsWith(s: QLatin1String("-----BEGIN "))) {
2293 if (line.contains(s: QLatin1String("CERTIFICATE"))) {
2294 found = true;
2295 pem += line + QLatin1Char('\n');
2296 crl = false;
2297 } else if (line.contains(s: QLatin1String("CRL"))) {
2298 found = true;
2299 pem += line + QLatin1Char('\n');
2300 crl = true;
2301 }
2302 }
2303 } else {
2304 pem += line + QLatin1Char('\n');
2305 if (line.startsWith(s: QLatin1String("-----END "))) {
2306 done = true;
2307 break;
2308 }
2309 }
2310 }
2311 if (!done)
2312 return QString();
2313 if (isCRL)
2314 *isCRL = crl;
2315 return pem;
2316}
2317
2318class CertificateCollection::Private : public QSharedData
2319{
2320public:
2321 QList<Certificate> certs;
2322 QList<CRL> crls;
2323};
2324
2325CertificateCollection::CertificateCollection()
2326 : d(new Private)
2327{
2328}
2329
2330CertificateCollection::CertificateCollection(const CertificateCollection &from)
2331 : d(from.d)
2332{
2333}
2334
2335CertificateCollection::~CertificateCollection()
2336{
2337}
2338
2339CertificateCollection &CertificateCollection::operator=(const CertificateCollection &from)
2340{
2341 d = from.d;
2342 return *this;
2343}
2344
2345void CertificateCollection::addCertificate(const Certificate &cert)
2346{
2347 d->certs.append(t: cert);
2348}
2349
2350void CertificateCollection::addCRL(const CRL &crl)
2351{
2352 d->crls.append(t: crl);
2353}
2354
2355QList<Certificate> CertificateCollection::certificates() const
2356{
2357 return d->certs;
2358}
2359
2360QList<CRL> CertificateCollection::crls() const
2361{
2362 return d->crls;
2363}
2364
2365void CertificateCollection::append(const CertificateCollection &other)
2366{
2367 d->certs += other.d->certs;
2368 d->crls += other.d->crls;
2369}
2370
2371CertificateCollection CertificateCollection::operator+(const CertificateCollection &other) const
2372{
2373 CertificateCollection c = *this;
2374 c.append(other);
2375 return c;
2376}
2377
2378CertificateCollection &CertificateCollection::operator+=(const CertificateCollection &other)
2379{
2380 append(other);
2381 return *this;
2382}
2383
2384bool CertificateCollection::canUsePKCS7(const QString &provider)
2385{
2386 return isSupported(features: "certcollection", provider);
2387}
2388
2389bool CertificateCollection::toFlatTextFile(const QString &fileName)
2390{
2391 QFile f(fileName);
2392 if (!f.open(flags: QFile::WriteOnly))
2393 return false;
2394
2395 QTextStream ts(&f);
2396 int n;
2397 for (n = 0; n < d->certs.count(); ++n)
2398 ts << d->certs[n].toPEM();
2399 for (n = 0; n < d->crls.count(); ++n)
2400 ts << d->crls[n].toPEM();
2401 return true;
2402}
2403
2404bool CertificateCollection::toPKCS7File(const QString &fileName, const QString &provider)
2405{
2406 CertCollectionContext *col =
2407 static_cast<CertCollectionContext *>(getContext(QStringLiteral("certcollection"), provider));
2408
2409 QList<CertContext *> cert_list;
2410 QList<CRLContext *> crl_list;
2411 int n;
2412 for (n = 0; n < d->certs.count(); ++n) {
2413 CertContext *c = static_cast<CertContext *>(d->certs[n].context());
2414 cert_list += c;
2415 }
2416 for (n = 0; n < d->crls.count(); ++n) {
2417 CRLContext *c = static_cast<CRLContext *>(d->crls[n].context());
2418 crl_list += c;
2419 }
2420
2421 const QByteArray result = col->toPKCS7(certs: cert_list, crls: crl_list);
2422 delete col;
2423
2424 return arrayToFile(fileName, content: result);
2425}
2426
2427CertificateCollection
2428CertificateCollection::fromFlatTextFile(const QString &fileName, ConvertResult *result, const QString &provider)
2429{
2430 QFile f(fileName);
2431 if (!f.open(flags: QFile::ReadOnly)) {
2432 if (result)
2433 *result = ErrorFile;
2434 return CertificateCollection();
2435 }
2436
2437 CertificateCollection certs;
2438 QTextStream ts(&f);
2439 while (true) {
2440 bool isCRL = false;
2441 QString pem = readNextPem(ts: &ts, isCRL: &isCRL);
2442 if (pem.isNull())
2443 break;
2444 if (isCRL) {
2445 CRL c = CRL::fromPEM(s: pem, result: nullptr, provider);
2446 if (!c.isNull())
2447 certs.addCRL(crl: c);
2448 } else {
2449 Certificate c = Certificate::fromPEM(s: pem, result: nullptr, provider);
2450 if (!c.isNull())
2451 certs.addCertificate(cert: c);
2452 }
2453 }
2454
2455 if (result)
2456 *result = ConvertGood;
2457
2458 return certs;
2459}
2460
2461CertificateCollection
2462CertificateCollection::fromPKCS7File(const QString &fileName, ConvertResult *result, const QString &provider)
2463{
2464 QByteArray der;
2465 if (!arrayFromFile(fileName, a: &der)) {
2466 if (result)
2467 *result = ErrorFile;
2468 return CertificateCollection();
2469 }
2470
2471 CertificateCollection certs;
2472
2473 QList<CertContext *> cert_list;
2474 QList<CRLContext *> crl_list;
2475 CertCollectionContext *col =
2476 static_cast<CertCollectionContext *>(getContext(QStringLiteral("certcollection"), provider));
2477 ConvertResult r = col->fromPKCS7(a: der, certs: &cert_list, crls: &crl_list);
2478 delete col;
2479
2480 if (result)
2481 *result = r;
2482 if (r == ConvertGood) {
2483 int n;
2484 for (n = 0; n < cert_list.count(); ++n) {
2485 Certificate c;
2486 c.change(c: cert_list[n]);
2487 certs.addCertificate(cert: c);
2488 }
2489 for (n = 0; n < crl_list.count(); ++n) {
2490 CRL c;
2491 c.change(c: crl_list[n]);
2492 certs.addCRL(crl: c);
2493 }
2494 }
2495 return certs;
2496}
2497
2498//----------------------------------------------------------------------------
2499// CertificateAuthority
2500//----------------------------------------------------------------------------
2501CertificateAuthority::CertificateAuthority(const Certificate &cert, const PrivateKey &key, const QString &provider)
2502 : Algorithm(QStringLiteral("ca"), provider)
2503{
2504 static_cast<CAContext *>(context())->setup(cert: *(static_cast<const CertContext *>(cert.context())),
2505 priv: *(static_cast<const PKeyContext *>(key.context())));
2506}
2507
2508CertificateAuthority::CertificateAuthority(const CertificateAuthority &from)
2509 : Algorithm(from)
2510{
2511}
2512
2513CertificateAuthority::~CertificateAuthority()
2514{
2515}
2516
2517CertificateAuthority &CertificateAuthority::operator=(const CertificateAuthority &from)
2518{
2519 Algorithm::operator=(from);
2520 return *this;
2521}
2522
2523Certificate CertificateAuthority::certificate() const
2524{
2525 Certificate c;
2526 c.change(c: static_cast<const CAContext *>(context())->certificate());
2527 return c;
2528}
2529
2530Certificate CertificateAuthority::signRequest(const CertificateRequest &req, const QDateTime &notValidAfter) const
2531{
2532 Certificate c;
2533 CertContext *cc = static_cast<const CAContext *>(context())->signRequest(
2534 req: *(static_cast<const CSRContext *>(req.context())), notValidAfter);
2535 if (cc)
2536 c.change(c: cc);
2537 return c;
2538}
2539
2540CRL CertificateAuthority::createCRL(const QDateTime &nextUpdate) const
2541{
2542 CRL crl;
2543 CRLContext *cc = static_cast<const CAContext *>(context())->createCRL(nextUpdate);
2544 if (cc)
2545 crl.change(c: cc);
2546 return crl;
2547}
2548
2549CRL CertificateAuthority::updateCRL(const CRL &crl, const QList<CRLEntry> &entries, const QDateTime &nextUpdate) const
2550{
2551 CRL new_crl;
2552 CRLContext *cc = static_cast<const CAContext *>(context())->updateCRL(
2553 crl: *(static_cast<const CRLContext *>(crl.context())), entries, nextUpdate);
2554 if (cc)
2555 new_crl.change(c: cc);
2556 return new_crl;
2557}
2558
2559//----------------------------------------------------------------------------
2560// KeyBundle
2561//----------------------------------------------------------------------------
2562class KeyBundle::Private : public QSharedData
2563{
2564public:
2565 QString name;
2566 CertificateChain chain;
2567 PrivateKey key;
2568};
2569
2570KeyBundle::KeyBundle()
2571 : d(new Private)
2572{
2573}
2574
2575KeyBundle::KeyBundle(const QString &fileName, const SecureArray &passphrase)
2576 : d(new Private)
2577{
2578 *this = fromFile(fileName, passphrase, result: nullptr, provider: QString());
2579}
2580
2581KeyBundle::KeyBundle(const KeyBundle &from)
2582 : d(from.d)
2583{
2584}
2585
2586KeyBundle::~KeyBundle()
2587{
2588}
2589
2590KeyBundle &KeyBundle::operator=(const KeyBundle &from)
2591{
2592 d = from.d;
2593 return *this;
2594}
2595
2596bool KeyBundle::isNull() const
2597{
2598 return d->chain.isEmpty();
2599}
2600
2601QString KeyBundle::name() const
2602{
2603 return d->name;
2604}
2605
2606CertificateChain KeyBundle::certificateChain() const
2607{
2608 return d->chain;
2609}
2610
2611PrivateKey KeyBundle::privateKey() const
2612{
2613 return d->key;
2614}
2615
2616void KeyBundle::setName(const QString &s)
2617{
2618 d->name = s;
2619}
2620
2621void KeyBundle::setCertificateChainAndKey(const CertificateChain &c, const PrivateKey &key)
2622{
2623 d->chain = c;
2624 d->key = key;
2625}
2626
2627QByteArray KeyBundle::toArray(const SecureArray &passphrase, const QString &provider) const
2628{
2629 PKCS12Context *pix = static_cast<PKCS12Context *>(getContext(QStringLiteral("pkcs12"), provider));
2630
2631 QList<const CertContext *> list;
2632 for (int n = 0; n < d->chain.count(); ++n)
2633 list.append(t: static_cast<const CertContext *>(d->chain[n].context()));
2634 const QByteArray buf =
2635 pix->toPKCS12(name: d->name, chain: list, priv: *(static_cast<const PKeyContext *>(d->key.context())), passphrase);
2636 delete pix;
2637
2638 return buf;
2639}
2640
2641bool KeyBundle::toFile(const QString &fileName, const SecureArray &passphrase, const QString &provider) const
2642{
2643 return arrayToFile(fileName, content: toArray(passphrase, provider));
2644}
2645
2646KeyBundle
2647KeyBundle::fromArray(const QByteArray &a, const SecureArray &passphrase, ConvertResult *result, const QString &provider)
2648{
2649 KeyBundle bundle;
2650 get_pkcs12_der(
2651 der: a, fileName: QString(), ptr: (void *)&a, passphrase, result, provider, name: &bundle.d->name, chain: &bundle.d->chain, key: &bundle.d->key);
2652 return bundle;
2653}
2654
2655KeyBundle KeyBundle::fromFile(const QString &fileName,
2656 const SecureArray &passphrase,
2657 ConvertResult *result,
2658 const QString &provider)
2659{
2660 QByteArray der;
2661 if (!arrayFromFile(fileName, a: &der)) {
2662 if (result)
2663 *result = ErrorFile;
2664 return KeyBundle();
2665 }
2666
2667 KeyBundle bundle;
2668 get_pkcs12_der(
2669 der, fileName, ptr: nullptr, passphrase, result, provider, name: &bundle.d->name, chain: &bundle.d->chain, key: &bundle.d->key);
2670 return bundle;
2671}
2672
2673//----------------------------------------------------------------------------
2674// PGPKey
2675//----------------------------------------------------------------------------
2676PGPKey::PGPKey()
2677{
2678}
2679
2680PGPKey::PGPKey(const QString &fileName)
2681{
2682 *this = fromFile(fileName, result: nullptr, provider: QString());
2683}
2684
2685PGPKey::PGPKey(const PGPKey &from)
2686 : Algorithm(from)
2687{
2688}
2689
2690PGPKey::~PGPKey()
2691{
2692}
2693
2694PGPKey &PGPKey::operator=(const PGPKey &from)
2695{
2696 Algorithm::operator=(from);
2697 return *this;
2698}
2699
2700bool PGPKey::isNull() const
2701{
2702 return (!context() ? true : false);
2703}
2704
2705QString PGPKey::keyId() const
2706{
2707 return static_cast<const PGPKeyContext *>(context())->props()->keyId;
2708}
2709
2710QString PGPKey::primaryUserId() const
2711{
2712 return static_cast<const PGPKeyContext *>(context())->props()->userIds.first();
2713}
2714
2715QStringList PGPKey::userIds() const
2716{
2717 return static_cast<const PGPKeyContext *>(context())->props()->userIds;
2718}
2719
2720bool PGPKey::isSecret() const
2721{
2722 return static_cast<const PGPKeyContext *>(context())->props()->isSecret;
2723}
2724
2725QDateTime PGPKey::creationDate() const
2726{
2727 return static_cast<const PGPKeyContext *>(context())->props()->creationDate;
2728}
2729
2730QDateTime PGPKey::expirationDate() const
2731{
2732 return static_cast<const PGPKeyContext *>(context())->props()->expirationDate;
2733}
2734
2735QString PGPKey::fingerprint() const
2736{
2737 return static_cast<const PGPKeyContext *>(context())->props()->fingerprint;
2738}
2739
2740bool PGPKey::inKeyring() const
2741{
2742 return static_cast<const PGPKeyContext *>(context())->props()->inKeyring;
2743}
2744
2745bool PGPKey::isTrusted() const
2746{
2747 return static_cast<const PGPKeyContext *>(context())->props()->isTrusted;
2748}
2749
2750QByteArray PGPKey::toArray() const
2751{
2752 return static_cast<const PGPKeyContext *>(context())->toBinary();
2753}
2754
2755QString PGPKey::toString() const
2756{
2757 return static_cast<const PGPKeyContext *>(context())->toAscii();
2758}
2759
2760bool PGPKey::toFile(const QString &fileName) const
2761{
2762 return stringToFile(fileName, content: toString());
2763}
2764
2765PGPKey PGPKey::fromArray(const QByteArray &a, ConvertResult *result, const QString &provider)
2766{
2767 PGPKey k;
2768 PGPKeyContext *kc = static_cast<PGPKeyContext *>(getContext(QStringLiteral("pgpkey"), provider));
2769 ConvertResult r = kc->fromBinary(a);
2770 if (result)
2771 *result = r;
2772 if (r == ConvertGood)
2773 k.change(c: kc);
2774 else
2775 delete kc;
2776 return k;
2777}
2778
2779PGPKey PGPKey::fromString(const QString &s, ConvertResult *result, const QString &provider)
2780{
2781 PGPKey k;
2782 PGPKeyContext *kc = static_cast<PGPKeyContext *>(getContext(QStringLiteral("pgpkey"), provider));
2783 ConvertResult r = kc->fromAscii(s);
2784 if (result)
2785 *result = r;
2786 if (r == ConvertGood)
2787 k.change(c: kc);
2788 else
2789 delete kc;
2790 return k;
2791}
2792
2793PGPKey PGPKey::fromFile(const QString &fileName, ConvertResult *result, const QString &provider)
2794{
2795 QString str;
2796 if (!stringFromFile(fileName, s: &str)) {
2797 if (result)
2798 *result = ErrorFile;
2799 return PGPKey();
2800 }
2801 return fromString(s: str, result, provider);
2802}
2803
2804//----------------------------------------------------------------------------
2805// KeyLoader
2806//----------------------------------------------------------------------------
2807class KeyLoaderThread : public QThread
2808{
2809 Q_OBJECT
2810public:
2811 enum Type
2812 {
2813 PKPEMFile,
2814 PKPEM,
2815 PKDER,
2816 KBDERFile,
2817 KBDER
2818 };
2819
2820 class In
2821 {
2822 public:
2823 Type type;
2824 QString fileName, pem;
2825 SecureArray der;
2826 QByteArray kbder;
2827 };
2828
2829 class Out
2830 {
2831 public:
2832 ConvertResult convertResult;
2833 PrivateKey privateKey;
2834 KeyBundle keyBundle;
2835 };
2836
2837 In in;
2838 Out out;
2839
2840 KeyLoaderThread(QObject *parent = nullptr)
2841 : QThread(parent)
2842 {
2843 }
2844
2845protected:
2846 void run() override
2847 {
2848 if (in.type == PKPEMFile)
2849 out.privateKey = PrivateKey::fromPEMFile(fileName: in.fileName, passphrase: SecureArray(), result: &out.convertResult);
2850 else if (in.type == PKPEM)
2851 out.privateKey = PrivateKey::fromPEM(s: in.pem, passphrase: SecureArray(), result: &out.convertResult);
2852 else if (in.type == PKDER)
2853 out.privateKey = PrivateKey::fromDER(a: in.der, passphrase: SecureArray(), result: &out.convertResult);
2854 else if (in.type == KBDERFile)
2855 out.keyBundle = KeyBundle::fromFile(fileName: in.fileName, passphrase: SecureArray(), result: &out.convertResult);
2856 else if (in.type == KBDER)
2857 out.keyBundle = KeyBundle::fromArray(a: in.kbder, passphrase: SecureArray(), result: &out.convertResult);
2858 }
2859};
2860
2861class KeyLoader::Private : public QObject
2862{
2863 Q_OBJECT
2864public:
2865 KeyLoader *q;
2866
2867 bool active;
2868 KeyLoaderThread *thread;
2869 KeyLoaderThread::In in;
2870 KeyLoaderThread::Out out;
2871
2872 Private(KeyLoader *_q)
2873 : QObject(_q)
2874 , q(_q)
2875 {
2876 active = false;
2877 }
2878
2879 void reset()
2880 {
2881 in = KeyLoaderThread::In();
2882 out = KeyLoaderThread::Out();
2883 }
2884
2885 void start()
2886 {
2887 active = true;
2888 thread = new KeyLoaderThread(this);
2889 // used queued for signal-safety
2890 connect(sender: thread, signal: &KeyLoaderThread::finished, context: this, slot: &KeyLoader::Private::thread_finished, type: Qt::QueuedConnection);
2891 thread->in = in;
2892 thread->start();
2893 }
2894
2895private Q_SLOTS:
2896 void thread_finished()
2897 {
2898 out = thread->out;
2899 delete thread;
2900 thread = nullptr;
2901 active = false;
2902
2903 emit q->finished();
2904 }
2905};
2906
2907KeyLoader::KeyLoader(QObject *parent)
2908 : QObject(parent)
2909{
2910 d = new Private(this);
2911}
2912
2913KeyLoader::~KeyLoader()
2914{
2915 delete d;
2916}
2917
2918void KeyLoader::loadPrivateKeyFromPEMFile(const QString &fileName)
2919{
2920 Q_ASSERT(!d->active);
2921 if (d->active)
2922 return;
2923
2924 d->reset();
2925 d->in.type = KeyLoaderThread::PKPEMFile;
2926 d->in.fileName = fileName;
2927 d->start();
2928}
2929
2930void KeyLoader::loadPrivateKeyFromPEM(const QString &s)
2931{
2932 Q_ASSERT(!d->active);
2933 if (d->active)
2934 return;
2935
2936 d->reset();
2937 d->in.type = KeyLoaderThread::PKPEM;
2938 d->in.pem = s;
2939 d->start();
2940}
2941
2942void KeyLoader::loadPrivateKeyFromDER(const SecureArray &a)
2943{
2944 Q_ASSERT(!d->active);
2945 if (d->active)
2946 return;
2947
2948 d->reset();
2949 d->in.type = KeyLoaderThread::PKDER;
2950 d->in.der = a;
2951 d->start();
2952}
2953
2954void KeyLoader::loadKeyBundleFromFile(const QString &fileName)
2955{
2956 Q_ASSERT(!d->active);
2957 if (d->active)
2958 return;
2959
2960 d->reset();
2961 d->in.type = KeyLoaderThread::KBDERFile;
2962 d->in.fileName = fileName;
2963 d->start();
2964}
2965
2966void KeyLoader::loadKeyBundleFromArray(const QByteArray &a)
2967{
2968 Q_ASSERT(!d->active);
2969 if (d->active)
2970 return;
2971
2972 d->reset();
2973 d->in.type = KeyLoaderThread::KBDERFile;
2974 d->in.kbder = a;
2975 d->start();
2976}
2977
2978ConvertResult KeyLoader::convertResult() const
2979{
2980 return d->out.convertResult;
2981}
2982
2983PrivateKey KeyLoader::privateKey() const
2984{
2985 return d->out.privateKey;
2986}
2987
2988KeyBundle KeyLoader::keyBundle() const
2989{
2990 return d->out.keyBundle;
2991}
2992
2993}
2994
2995#include "qca_cert.moc"
2996

source code of qca/src/qca_cert.cpp