2 * Copyright (C) 2005 Jan Hutter, Martin Willi
3 * Copyright (C) 2009-2017 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #include "x509_pkcs10.h"
20 #include <utils/debug.h>
22 #include <asn1/asn1.h>
23 #include <asn1/asn1_parser.h>
24 #include <credentials/keys/private_key.h>
25 #include <collections/linked_list.h>
26 #include <utils/identification.h>
28 typedef struct private_x509_pkcs10_t private_x509_pkcs10_t
;
31 * Private data of a x509_pkcs10_t object.
33 struct private_x509_pkcs10_t
{
35 * Public interface for this certificate.
40 * PKCS#10 certificate request encoding in ASN.1 DER format
45 * PKCS#10 request body over which signature is computed
47 chunk_t certificationRequestInfo
;
50 * Version of the PKCS#10 certificate request
55 * ID representing the certificate subject
57 identification_t
*subject
;
60 * List of subjectAltNames as identification_t
62 linked_list_t
*subjectAltNames
;
65 * certificate's embedded public key
67 public_key_t
*public_key
;
72 chunk_t challengePassword
;
85 * Is the certificate request self-signed?
90 * Certificate request parsed from blob/file?
101 * Imported from x509_cert.c
103 extern bool x509_parse_generalNames(chunk_t blob
, int level0
, bool implicit
,
104 linked_list_t
*list
);
105 extern chunk_t
x509_build_subjectAltNames(linked_list_t
*list
);
107 METHOD(certificate_t
, get_type
, certificate_type_t
,
108 private_x509_pkcs10_t
*this)
110 return CERT_PKCS10_REQUEST
;
113 METHOD(certificate_t
, get_subject
, identification_t
*,
114 private_x509_pkcs10_t
*this)
116 return this->subject
;
119 METHOD(certificate_t
, has_subject
, id_match_t
,
120 private_x509_pkcs10_t
*this, identification_t
*subject
)
122 return this->subject
->matches(this->subject
, subject
);
125 METHOD(certificate_t
, issued_by
, bool,
126 private_x509_pkcs10_t
*this, certificate_t
*issuer
,
127 signature_scheme_t
*schemep
)
130 signature_scheme_t scheme
;
133 if (&this->public.interface
.interface
!= issuer
)
137 if (this->self_signed
)
142 /* determine signature scheme */
143 scheme
= signature_scheme_from_oid(this->algorithm
);
144 if (scheme
== SIGN_UNKNOWN
)
149 /* get the public key contained in the certificate request */
150 key
= this->public_key
;
155 valid
= key
->verify(key
, scheme
, this->certificationRequestInfo
,
157 if (valid
&& schemep
)
164 METHOD(certificate_t
, get_public_key
, public_key_t
*,
165 private_x509_pkcs10_t
*this)
167 this->public_key
->get_ref(this->public_key
);
168 return this->public_key
;
171 METHOD(certificate_t
, get_validity
, bool,
172 private_x509_pkcs10_t
*this, time_t *when
, time_t *not_before
,
186 METHOD(certificate_t
, get_encoding
, bool,
187 private_x509_pkcs10_t
*this, cred_encoding_type_t type
, chunk_t
*encoding
)
189 if (type
== CERT_ASN1_DER
)
191 *encoding
= chunk_clone(this->encoding
);
194 return lib
->encoding
->encode(lib
->encoding
, type
, NULL
, encoding
,
195 CRED_PART_PKCS10_ASN1_DER
, this->encoding
, CRED_PART_END
);
198 METHOD(certificate_t
, equals
, bool,
199 private_x509_pkcs10_t
*this, certificate_t
*other
)
204 if (this == (private_x509_pkcs10_t
*)other
)
208 if (other
->get_type(other
) != CERT_PKCS10_REQUEST
)
212 if (other
->equals
== (void*)equals
)
213 { /* skip allocation if we have the same implementation */
214 return chunk_equals(this->encoding
, ((private_x509_pkcs10_t
*)other
)->encoding
);
216 if (!other
->get_encoding(other
, CERT_ASN1_DER
, &encoding
))
220 equal
= chunk_equals(this->encoding
, encoding
);
225 METHOD(certificate_t
, get_ref
, certificate_t
*,
226 private_x509_pkcs10_t
*this)
229 return &this->public.interface
.interface
;
232 METHOD(pkcs10_t
, get_challengePassword
, chunk_t
,
233 private_x509_pkcs10_t
*this)
235 return this->challengePassword
;
238 METHOD(pkcs10_t
, create_subjectAltName_enumerator
, enumerator_t
*,
239 private_x509_pkcs10_t
*this)
241 return this->subjectAltNames
->create_enumerator(this->subjectAltNames
);
245 * ASN.1 definition of a PKCS#10 extension request
247 static const asn1Object_t extensionRequestObjects
[] = {
248 { 0, "extensions", ASN1_SEQUENCE
, ASN1_LOOP
}, /* 0 */
249 { 1, "extension", ASN1_SEQUENCE
, ASN1_NONE
}, /* 1 */
250 { 2, "extnID", ASN1_OID
, ASN1_BODY
}, /* 2 */
251 { 2, "critical", ASN1_BOOLEAN
, ASN1_DEF
|ASN1_BODY
}, /* 3 */
252 { 2, "extnValue", ASN1_OCTET_STRING
, ASN1_BODY
}, /* 4 */
253 { 1, "end loop", ASN1_EOC
, ASN1_END
}, /* 5 */
254 { 0, "exit", ASN1_EOC
, ASN1_EXIT
}
256 #define PKCS10_EXTN_ID 2
257 #define PKCS10_EXTN_CRITICAL 3
258 #define PKCS10_EXTN_VALUE 4
261 * Parses a PKCS#10 extension request
263 static bool parse_extension_request(private_x509_pkcs10_t
*this, chunk_t blob
, int level0
)
265 asn1_parser_t
*parser
;
268 int extn_oid
= OID_UNKNOWN
;
269 bool success
= FALSE
;
272 parser
= asn1_parser_create(extensionRequestObjects
, blob
);
273 parser
->set_top_level(parser
, level0
);
275 while (parser
->iterate(parser
, &objectID
, &object
))
277 u_int level
= parser
->get_level(parser
)+1;
282 extn_oid
= asn1_known_oid(object
);
284 case PKCS10_EXTN_CRITICAL
:
285 critical
= object
.len
&& *object
.ptr
;
286 DBG2(DBG_ASN
, " %s", critical ?
"TRUE" : "FALSE");
288 case PKCS10_EXTN_VALUE
:
292 case OID_SUBJECT_ALT_NAME
:
293 if (!x509_parse_generalNames(object
, level
, FALSE
,
294 this->subjectAltNames
))
308 success
= parser
->success(parser
);
311 parser
->destroy(parser
);
317 * Parses a PKCS#10 challenge password
319 static bool parse_challengePassword(private_x509_pkcs10_t
*this, chunk_t blob
, int level
)
325 DBG1(DBG_ASN
, "L%d - challengePassword: ASN.1 object smaller "
326 "than 2 octets", level
);
330 if (tag
< ASN1_UTF8STRING
|| tag
> ASN1_IA5STRING
)
332 DBG1(DBG_ASN
, "L%d - challengePassword: ASN.1 object is not "
333 "a character string", level
);
336 if (asn1_length(&blob
) == ASN1_INVALID_LENGTH
)
338 DBG1(DBG_ASN
, "L%d - challengePassword: ASN.1 object has an "
339 "invalid length", level
);
342 DBG2(DBG_ASN
, "L%d - challengePassword:", level
);
343 DBG4(DBG_ASN
, " '%.*s'", (int)blob
.len
, blob
.ptr
);
348 * ASN.1 definition of a PKCS#10 certificate request
350 static const asn1Object_t certificationRequestObjects
[] = {
351 { 0, "certificationRequest", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 0 */
352 { 1, "certificationRequestInfo", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 1 */
353 { 2, "version", ASN1_INTEGER
, ASN1_BODY
}, /* 2 */
354 { 2, "subject", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 3 */
355 { 2, "subjectPublicKeyInfo", ASN1_SEQUENCE
, ASN1_RAW
}, /* 4 */
356 { 2, "attributes", ASN1_CONTEXT_C_0
, ASN1_LOOP
}, /* 5 */
357 { 3, "attribute", ASN1_SEQUENCE
, ASN1_NONE
}, /* 6 */
358 { 4, "type", ASN1_OID
, ASN1_BODY
}, /* 7 */
359 { 4, "values", ASN1_SET
, ASN1_LOOP
}, /* 8 */
360 { 5, "value", ASN1_EOC
, ASN1_RAW
}, /* 9 */
361 { 4, "end loop", ASN1_EOC
, ASN1_END
}, /* 10 */
362 { 2, "end loop", ASN1_EOC
, ASN1_END
}, /* 11 */
363 { 1, "signatureAlgorithm", ASN1_EOC
, ASN1_RAW
}, /* 12 */
364 { 1, "signature", ASN1_BIT_STRING
, ASN1_BODY
}, /* 13 */
365 { 0, "exit", ASN1_EOC
, ASN1_EXIT
}
367 #define PKCS10_CERT_REQUEST_INFO 1
368 #define PKCS10_VERSION 2
369 #define PKCS10_SUBJECT 3
370 #define PKCS10_SUBJECT_PUBLIC_KEY_INFO 4
371 #define PKCS10_ATTR_TYPE 7
372 #define PKCS10_ATTR_VALUE 9
373 #define PKCS10_ALGORITHM 12
374 #define PKCS10_SIGNATURE 13
377 * Parses a PKCS#10 certificate request
379 static bool parse_certificate_request(private_x509_pkcs10_t
*this)
381 asn1_parser_t
*parser
;
384 int attr_oid
= OID_UNKNOWN
;
385 bool success
= FALSE
;
387 parser
= asn1_parser_create(certificationRequestObjects
, this->encoding
);
389 while (parser
->iterate(parser
, &objectID
, &object
))
391 u_int level
= parser
->get_level(parser
)+1;
395 case PKCS10_CERT_REQUEST_INFO
:
396 this->certificationRequestInfo
= object
;
399 if (object
.len
> 0 && *object
.ptr
!= 0)
401 DBG1(DBG_ASN
, "PKCS#10 certificate request format is "
407 this->subject
= identification_create_from_encoding(ID_DER_ASN1_DN
, object
);
408 DBG2(DBG_ASN
, " '%Y'", this->subject
);
410 case PKCS10_SUBJECT_PUBLIC_KEY_INFO
:
411 this->public_key
= lib
->creds
->create(lib
->creds
, CRED_PUBLIC_KEY
,
412 KEY_ANY
, BUILD_BLOB_ASN1_DER
, object
, BUILD_END
);
413 if (this->public_key
== NULL
)
418 case PKCS10_ATTR_TYPE
:
419 attr_oid
= asn1_known_oid(object
);
421 case PKCS10_ATTR_VALUE
:
424 case OID_EXTENSION_REQUEST
:
425 if (!parse_extension_request(this, object
, level
))
430 case OID_CHALLENGE_PASSWORD
:
431 if (!parse_challengePassword(this, object
, level
))
440 case PKCS10_ALGORITHM
:
441 this->algorithm
= asn1_parse_algorithmIdentifier(object
, level
, NULL
);
443 case PKCS10_SIGNATURE
:
444 this->signature
= chunk_skip(object
, 1);
450 success
= parser
->success(parser
);
453 parser
->destroy(parser
);
456 /* check if the certificate request is self-signed */
457 if (issued_by(this, &this->public.interface
.interface
, NULL
))
459 this->self_signed
= TRUE
;
463 DBG1(DBG_LIB
, "certificate request is not self-signed");
470 METHOD(certificate_t
, destroy
, void,
471 private_x509_pkcs10_t
*this)
473 if (ref_put(&this->ref
))
475 this->subjectAltNames
->destroy_offset(this->subjectAltNames
,
476 offsetof(identification_t
, destroy
));
477 DESTROY_IF(this->subject
);
478 DESTROY_IF(this->public_key
);
479 chunk_free(&this->encoding
);
481 { /* only parsed certificate requests point these fields to "encoded" */
482 chunk_free(&this->certificationRequestInfo
);
483 chunk_free(&this->challengePassword
);
484 chunk_free(&this->signature
);
491 * create an empty but initialized PKCS#10 certificate request
493 static private_x509_pkcs10_t
* create_empty(void)
495 private_x509_pkcs10_t
*this;
501 .get_type
= _get_type
,
502 .get_subject
= _get_subject
,
503 .get_issuer
= _get_subject
,
504 .has_subject
= _has_subject
,
505 .has_issuer
= _has_subject
,
506 .issued_by
= _issued_by
,
507 .get_public_key
= _get_public_key
,
508 .get_validity
= _get_validity
,
509 .get_encoding
= _get_encoding
,
514 .get_challengePassword
= _get_challengePassword
,
515 .create_subjectAltName_enumerator
= _create_subjectAltName_enumerator
,
518 .subjectAltNames
= linked_list_create(),
526 * Generate and sign a new certificate request
528 static bool generate(private_x509_pkcs10_t
*cert
, private_key_t
*sign_key
,
531 chunk_t key_info
, subjectAltNames
, attributes
;
532 chunk_t extensionRequest
= chunk_empty
;
533 chunk_t challengePassword
= chunk_empty
;
534 signature_scheme_t scheme
;
535 identification_t
*subject
;
537 subject
= cert
->subject
;
538 cert
->public_key
= sign_key
->get_public_key(sign_key
);
540 /* select signature scheme */
541 cert
->algorithm
= hasher_signature_algorithm_to_oid(digest_alg
,
542 sign_key
->get_type(sign_key
));
543 if (cert
->algorithm
== OID_UNKNOWN
)
547 scheme
= signature_scheme_from_oid(cert
->algorithm
);
549 if (!cert
->public_key
->get_encoding(cert
->public_key
,
550 PUBKEY_SPKI_ASN1_DER
, &key_info
))
555 /* encode subjectAltNames */
556 subjectAltNames
= x509_build_subjectAltNames(cert
->subjectAltNames
);
558 if (subjectAltNames
.ptr
)
560 extensionRequest
= asn1_wrap(ASN1_SEQUENCE
, "mm",
561 asn1_build_known_oid(OID_EXTENSION_REQUEST
),
562 asn1_wrap(ASN1_SET
, "m",
563 asn1_wrap(ASN1_SEQUENCE
, "m", subjectAltNames
)
566 if (cert
->challengePassword
.len
> 0)
568 asn1_t type
= asn1_is_printablestring(cert
->challengePassword
) ?
569 ASN1_PRINTABLESTRING
: ASN1_T61STRING
;
571 challengePassword
= asn1_wrap(ASN1_SEQUENCE
, "mm",
572 asn1_build_known_oid(OID_CHALLENGE_PASSWORD
),
573 asn1_wrap(ASN1_SET
, "m",
574 asn1_simple_object(type
, cert
->challengePassword
)
578 attributes
= asn1_wrap(ASN1_CONTEXT_C_0
, "mm", extensionRequest
,
581 cert
->certificationRequestInfo
= asn1_wrap(ASN1_SEQUENCE
, "ccmm",
583 subject
->get_encoding(subject
),
587 if (!sign_key
->sign(sign_key
, scheme
, cert
->certificationRequestInfo
,
593 cert
->encoding
= asn1_wrap(ASN1_SEQUENCE
, "cmm",
594 cert
->certificationRequestInfo
,
595 asn1_algorithmIdentifier(cert
->algorithm
),
596 asn1_bitstring("c", cert
->signature
));
603 x509_pkcs10_t
*x509_pkcs10_load(certificate_type_t type
, va_list args
)
605 chunk_t blob
= chunk_empty
;
609 switch (va_arg(args
, builder_part_t
))
611 case BUILD_BLOB_ASN1_DER
:
612 blob
= va_arg(args
, chunk_t
);
624 private_x509_pkcs10_t
*cert
= create_empty();
626 cert
->encoding
= chunk_clone(blob
);
628 if (parse_certificate_request(cert
))
630 return &cert
->public;
640 x509_pkcs10_t
*x509_pkcs10_gen(certificate_type_t type
, va_list args
)
642 private_x509_pkcs10_t
*cert
;
643 private_key_t
*sign_key
= NULL
;
644 hash_algorithm_t digest_alg
= HASH_SHA1
;
646 cert
= create_empty();
649 switch (va_arg(args
, builder_part_t
))
651 case BUILD_SIGNING_KEY
:
652 sign_key
= va_arg(args
, private_key_t
*);
655 cert
->subject
= va_arg(args
, identification_t
*);
656 cert
->subject
= cert
->subject
->clone(cert
->subject
);
658 case BUILD_SUBJECT_ALTNAMES
:
660 enumerator_t
*enumerator
;
661 identification_t
*id
;
664 list
= va_arg(args
, linked_list_t
*);
665 enumerator
= list
->create_enumerator(list
);
666 while (enumerator
->enumerate(enumerator
, &id
))
668 cert
->subjectAltNames
->insert_last(cert
->subjectAltNames
,
671 enumerator
->destroy(enumerator
);
674 case BUILD_CHALLENGE_PWD
:
675 cert
->challengePassword
= chunk_clone(va_arg(args
, chunk_t
));
677 case BUILD_DIGEST_ALG
:
678 digest_alg
= va_arg(args
, int);
689 if (sign_key
&& generate(cert
, sign_key
, digest_alg
))
691 return &cert
->public;