2 * Copyright (C) 2005 Jan Hutter, Martin Willi
3 * Copyright (C) 2009 Andreas Steffen
5 * HSR Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include "x509_pkcs10.h"
23 #include <asn1/asn1.h>
24 #include <asn1/asn1_parser.h>
25 #include <credentials/keys/private_key.h>
26 #include <utils/linked_list.h>
27 #include <utils/identification.h>
29 typedef struct private_x509_pkcs10_t private_x509_pkcs10_t
;
32 * Private data of a x509_pkcs10_t object.
34 struct private_x509_pkcs10_t
{
36 * Public interface for this certificate.
41 * PKCS#10 certificate request encoding in ASN.1 DER format
46 * PKCS#10 request body over which signature is computed
48 chunk_t certificationRequestInfo
;
51 * Version of the PKCS#10 certificate request
56 * ID representing the certificate subject
58 identification_t
*subject
;
61 * List of subjectAltNames as identification_t
63 linked_list_t
*subjectAltNames
;
66 * certificate's embedded public key
68 public_key_t
*public_key
;
73 chunk_t challengePassword
;
86 * Is the certificate request self-signed?
91 * Certificate request parsed from blob/file?
102 * Imported from x509_cert.c
104 extern void x509_parse_generalNames(chunk_t blob
, int level0
, bool implicit
, 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 x509_parse_generalNames(object
, level
, FALSE
,
294 this->subjectAltNames
);
305 success
= parser
->success(parser
);
306 parser
->destroy(parser
);
311 * Parses a PKCS#10 challenge password
313 static bool parse_challengePassword(private_x509_pkcs10_t
*this, chunk_t blob
, int level
)
319 DBG1(DBG_ASN
, "L%d - challengePassword: ASN.1 object smaller "
320 "than 2 octets", level
);
324 if (tag
< ASN1_UTF8STRING
|| tag
> ASN1_IA5STRING
)
326 DBG1(DBG_ASN
, "L%d - challengePassword: ASN.1 object is not "
327 "a character string", level
);
330 if (asn1_length(&blob
) == ASN1_INVALID_LENGTH
)
332 DBG1(DBG_ASN
, "L%d - challengePassword: ASN.1 object has an "
333 "invalid length", level
);
336 DBG2(DBG_ASN
, "L%d - challengePassword:", level
);
337 DBG4(DBG_ASN
, " '%.*s'", (int)blob
.len
, blob
.ptr
);
342 * ASN.1 definition of a PKCS#10 certificate request
344 static const asn1Object_t certificationRequestObjects
[] = {
345 { 0, "certificationRequest", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 0 */
346 { 1, "certificationRequestInfo", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 1 */
347 { 2, "version", ASN1_INTEGER
, ASN1_BODY
}, /* 2 */
348 { 2, "subject", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 3 */
349 { 2, "subjectPublicKeyInfo", ASN1_SEQUENCE
, ASN1_RAW
}, /* 4 */
350 { 2, "attributes", ASN1_CONTEXT_C_0
, ASN1_LOOP
}, /* 5 */
351 { 3, "attribute", ASN1_SEQUENCE
, ASN1_NONE
}, /* 6 */
352 { 4, "type", ASN1_OID
, ASN1_BODY
}, /* 7 */
353 { 4, "values", ASN1_SET
, ASN1_LOOP
}, /* 8 */
354 { 5, "value", ASN1_EOC
, ASN1_RAW
}, /* 9 */
355 { 4, "end loop", ASN1_EOC
, ASN1_END
}, /* 10 */
356 { 2, "end loop", ASN1_EOC
, ASN1_END
}, /* 11 */
357 { 1, "signatureAlgorithm", ASN1_EOC
, ASN1_RAW
}, /* 12 */
358 { 1, "signature", ASN1_BIT_STRING
, ASN1_BODY
}, /* 13 */
359 { 0, "exit", ASN1_EOC
, ASN1_EXIT
}
361 #define PKCS10_CERT_REQUEST_INFO 1
362 #define PKCS10_VERSION 2
363 #define PKCS10_SUBJECT 3
364 #define PKCS10_SUBJECT_PUBLIC_KEY_INFO 4
365 #define PKCS10_ATTR_TYPE 7
366 #define PKCS10_ATTR_VALUE 9
367 #define PKCS10_ALGORITHM 12
368 #define PKCS10_SIGNATURE 13
371 * Parses a PKCS#10 certificate request
373 static bool parse_certificate_request(private_x509_pkcs10_t
*this)
375 asn1_parser_t
*parser
;
378 int attr_oid
= OID_UNKNOWN
;
379 bool success
= FALSE
;
381 parser
= asn1_parser_create(certificationRequestObjects
, this->encoding
);
383 while (parser
->iterate(parser
, &objectID
, &object
))
385 u_int level
= parser
->get_level(parser
)+1;
389 case PKCS10_CERT_REQUEST_INFO
:
390 this->certificationRequestInfo
= object
;
393 if (object
.len
> 0 && *object
.ptr
!= 0)
395 DBG1(DBG_ASN
, "PKCS#10 certificate request format is "
401 this->subject
= identification_create_from_encoding(ID_DER_ASN1_DN
, object
);
402 DBG2(DBG_ASN
, " '%Y'", this->subject
);
404 case PKCS10_SUBJECT_PUBLIC_KEY_INFO
:
405 this->public_key
= lib
->creds
->create(lib
->creds
, CRED_PUBLIC_KEY
,
406 KEY_ANY
, BUILD_BLOB_ASN1_DER
, object
, BUILD_END
);
407 if (this->public_key
== NULL
)
412 case PKCS10_ATTR_TYPE
:
413 attr_oid
= asn1_known_oid(object
);
415 case PKCS10_ATTR_VALUE
:
418 case OID_EXTENSION_REQUEST
:
419 if (!parse_extension_request(this, object
, level
))
424 case OID_CHALLENGE_PASSWORD
:
425 if (!parse_challengePassword(this, object
, level
))
434 case PKCS10_ALGORITHM
:
435 this->algorithm
= asn1_parse_algorithmIdentifier(object
, level
, NULL
);
437 case PKCS10_SIGNATURE
:
438 this->signature
= object
;
444 success
= parser
->success(parser
);
447 parser
->destroy(parser
);
450 /* check if the certificate request is self-signed */
451 if (issued_by(this, &this->public.interface
.interface
, NULL
))
453 this->self_signed
= TRUE
;
457 DBG1(DBG_LIB
, "certificate request is not self-signed");
464 METHOD(certificate_t
, destroy
, void,
465 private_x509_pkcs10_t
*this)
467 if (ref_put(&this->ref
))
469 this->subjectAltNames
->destroy_offset(this->subjectAltNames
,
470 offsetof(identification_t
, destroy
));
471 DESTROY_IF(this->subject
);
472 DESTROY_IF(this->public_key
);
473 chunk_free(&this->encoding
);
475 { /* only parsed certificate requests point these fields to "encoded" */
476 chunk_free(&this->certificationRequestInfo
);
477 chunk_free(&this->challengePassword
);
478 chunk_free(&this->signature
);
485 * create an empty but initialized PKCS#10 certificate request
487 static private_x509_pkcs10_t
* create_empty(void)
489 private_x509_pkcs10_t
*this;
495 .get_type
= _get_type
,
496 .get_subject
= _get_subject
,
497 .get_issuer
= _get_subject
,
498 .has_subject
= _has_subject
,
499 .has_issuer
= _has_subject
,
500 .issued_by
= _issued_by
,
501 .get_public_key
= _get_public_key
,
502 .get_validity
= _get_validity
,
503 .get_encoding
= _get_encoding
,
508 .get_challengePassword
= _get_challengePassword
,
509 .create_subjectAltName_enumerator
= _create_subjectAltName_enumerator
,
512 .subjectAltNames
= linked_list_create(),
520 * Generate and sign a new certificate request
522 static bool generate(private_x509_pkcs10_t
*cert
, private_key_t
*sign_key
,
525 chunk_t key_info
, subjectAltNames
, attributes
;
526 chunk_t extensionRequest
= chunk_empty
;
527 chunk_t challengePassword
= chunk_empty
;
528 signature_scheme_t scheme
;
529 identification_t
*subject
;
531 subject
= cert
->subject
;
532 cert
->public_key
= sign_key
->get_public_key(sign_key
);
534 /* select signature scheme */
535 cert
->algorithm
= hasher_signature_algorithm_to_oid(digest_alg
,
536 sign_key
->get_type(sign_key
));
537 if (cert
->algorithm
== OID_UNKNOWN
)
541 scheme
= signature_scheme_from_oid(cert
->algorithm
);
543 if (!cert
->public_key
->get_encoding(cert
->public_key
,
544 PUBKEY_SPKI_ASN1_DER
, &key_info
))
549 /* encode subjectAltNames */
550 subjectAltNames
= x509_build_subjectAltNames(cert
->subjectAltNames
);
552 if (subjectAltNames
.ptr
)
554 extensionRequest
= asn1_wrap(ASN1_SEQUENCE
, "mm",
555 asn1_build_known_oid(OID_EXTENSION_REQUEST
),
556 asn1_wrap(ASN1_SET
, "m",
557 asn1_wrap(ASN1_SEQUENCE
, "m", subjectAltNames
)
560 if (cert
->challengePassword
.len
> 0)
562 asn1_t type
= asn1_is_printablestring(cert
->challengePassword
) ?
563 ASN1_PRINTABLESTRING
: ASN1_T61STRING
;
565 challengePassword
= asn1_wrap(ASN1_SEQUENCE
, "mm",
566 asn1_build_known_oid(OID_CHALLENGE_PASSWORD
),
567 asn1_wrap(ASN1_SET
, "m",
568 asn1_simple_object(type
, cert
->challengePassword
)
572 attributes
= asn1_wrap(ASN1_CONTEXT_C_0
, "mm", extensionRequest
,
575 cert
->certificationRequestInfo
= asn1_wrap(ASN1_SEQUENCE
, "ccmm",
577 subject
->get_encoding(subject
),
581 if (!sign_key
->sign(sign_key
, scheme
, cert
->certificationRequestInfo
,
587 cert
->encoding
= asn1_wrap(ASN1_SEQUENCE
, "cmm",
588 cert
->certificationRequestInfo
,
589 asn1_algorithmIdentifier(cert
->algorithm
),
590 asn1_bitstring("c", cert
->signature
));
597 x509_pkcs10_t
*x509_pkcs10_load(certificate_type_t type
, va_list args
)
599 chunk_t blob
= chunk_empty
;
603 switch (va_arg(args
, builder_part_t
))
605 case BUILD_BLOB_ASN1_DER
:
606 blob
= va_arg(args
, chunk_t
);
618 private_x509_pkcs10_t
*cert
= create_empty();
620 cert
->encoding
= chunk_clone(blob
);
622 if (parse_certificate_request(cert
))
624 return &cert
->public;
634 x509_pkcs10_t
*x509_pkcs10_gen(certificate_type_t type
, va_list args
)
636 private_x509_pkcs10_t
*cert
;
637 private_key_t
*sign_key
= NULL
;
638 hash_algorithm_t digest_alg
= HASH_SHA1
;
640 cert
= create_empty();
643 switch (va_arg(args
, builder_part_t
))
645 case BUILD_SIGNING_KEY
:
646 sign_key
= va_arg(args
, private_key_t
*);
649 cert
->subject
= va_arg(args
, identification_t
*);
650 cert
->subject
= cert
->subject
->clone(cert
->subject
);
652 case BUILD_SUBJECT_ALTNAMES
:
654 enumerator_t
*enumerator
;
655 identification_t
*id
;
658 list
= va_arg(args
, linked_list_t
*);
659 enumerator
= list
->create_enumerator(list
);
660 while (enumerator
->enumerate(enumerator
, &id
))
662 cert
->subjectAltNames
->insert_last(cert
->subjectAltNames
,
665 enumerator
->destroy(enumerator
);
668 case BUILD_CHALLENGE_PWD
:
669 cert
->challengePassword
= chunk_clone(va_arg(args
, chunk_t
));
671 case BUILD_DIGEST_ALG
:
672 digest_alg
= va_arg(args
, int);
683 if (sign_key
&& generate(cert
, sign_key
, digest_alg
))
685 return &cert
->public;