3 * sscep -- Simple SCEP client implementation
4 * Copyright (c) Jarkko Turkulainen 2003. All rights reserved.
5 * See the file COPYRIGHT for licensing information.
15 handle_serial (char * serial)
17 int hex = NULL != strchr (serial, ':');
19 /* Convert serial to a decimal serial when input is
20 a hexidecimal representation of the serial */
24 char *tmp_serial = (char*) calloc (strlen (serial) + 1,1);
26 for (i=0,ii=0; '\0'!=serial[i];i++)
29 tmp_serial[ii++]=serial[i];
36 for (i=0; ! hex && '\0' != serial[i]; i++)
37 hex = 'a'==serial[i]||'b'==serial[i]||'c'==serial[i]||'d'==serial[i]||'e'==serial[i]||'f'==serial[i];
44 BIO* in = BIO_new_mem_buf(serial, -1);
46 ai=ASN1_INTEGER_new();
47 if (ai == NULL) return NULL;
48 if (!a2i_ASN1_INTEGER(in,ai,buf,1024))
52 ret=ASN1_INTEGER_to_BN(ai,NULL);
59 serial = BN_bn2dec(ret);
67 main(int argc, char **argv) {
68 int c, host_port = 80, count = 1;
69 char *host_name, *p, *dir_name = NULL;
70 char http_string[16384];
71 struct http_reply reply;
73 unsigned char md[EVP_MAX_MD_SIZE];
78 /* Initialize scep layer */
81 /* Set program name */
84 /* Define signal trap */
85 (void)signal(SIGALRM, catchalarm);
90 /* Check operation parameter */
93 } else if (!strncmp(argv[1], "getca", 5)) {
94 operation_flag = SCEP_OPERATION_GETCA;
95 } else if (!strncmp(argv[1], "enroll", 6)) {
96 operation_flag = SCEP_OPERATION_ENROLL;
97 } else if (!strncmp(argv[1], "getcert", 7)) {
98 operation_flag = SCEP_OPERATION_GETCERT;
99 } else if (!strncmp(argv[1], "getcrl", 6)) {
100 operation_flag = SCEP_OPERATION_GETCRL;
102 fprintf(stderr, "%s: missing or illegal operation parameter\n",
106 /* Skip first parameter and parse the rest of the command */
108 while ((c = getopt(argc, argv, "c:de:E:f:F:i:k:K:l:L:n:O:p:r:Rs:S:t:T:u:vw:")) != -1)
155 n_num = atoi(optarg);
175 s_char = handle_serial(optarg);
183 t_num = atoi(optarg);
187 T_num = atoi(optarg);
201 printf("argv: %s\n", argv[optind]);
207 /* If we debug, include verbose messages also */
211 /* Read in the configuration file: */
213 if (!(fp = fopen(f_char, "r")))
214 fprintf(stderr, "%s: cannot open %s\n", pname, f_char);
221 fprintf(stdout, "%s: starting sscep, version %s\n",
224 * Check argument logic.
227 if (operation_flag == SCEP_OPERATION_GETCA) {
229 "%s: missing CA certificate filename (-c)\n", pname);
230 exit (SCEP_PKISTATUS_ERROR);
233 "%s: missing CA certificate (-c)\n", pname);
234 exit (SCEP_PKISTATUS_ERROR);
237 if (operation_flag == SCEP_OPERATION_ENROLL) {
239 fprintf(stderr, "%s: missing private key (-k)\n",pname);
240 exit (SCEP_PKISTATUS_ERROR);
243 fprintf(stderr, "%s: missing request (-r)\n",pname);
244 exit (SCEP_PKISTATUS_ERROR);
248 fprintf(stderr, "%s: missing local cert (-l)\n",pname);
249 exit (SCEP_PKISTATUS_ERROR);
251 /* Set polling limits */
253 n_num = MAX_POLL_COUNT;
257 T_num = MAX_POLL_TIME;
259 if (operation_flag == SCEP_OPERATION_GETCERT) {
261 fprintf(stderr, "%s: missing local cert (-l)\n",pname);
262 exit (SCEP_PKISTATUS_ERROR);
265 fprintf(stderr, "%s: missing serial no (-s)\n", pname);
266 exit (SCEP_PKISTATUS_ERROR);
269 fprintf(stderr, "%s: missing cert file (-w)\n",pname);
270 exit (SCEP_PKISTATUS_ERROR);
273 fprintf(stderr, "%s: missing private key (-k)\n",pname);
274 exit (SCEP_PKISTATUS_ERROR);
277 if (operation_flag == SCEP_OPERATION_GETCRL) {
279 fprintf(stderr, "%s: missing local cert (-l)\n",pname);
280 exit (SCEP_PKISTATUS_ERROR);
283 fprintf(stderr, "%s: missing crl file (-w)\n",pname);
284 exit (SCEP_PKISTATUS_ERROR);
287 fprintf(stderr, "%s: missing private key (-k)\n",pname);
288 exit (SCEP_PKISTATUS_ERROR);
292 /* Break down the URL */
294 fprintf(stderr, "%s: missing URL (-u)\n", pname);
295 exit (SCEP_PKISTATUS_ERROR);
297 if (strncmp(url_char, "http://", 7) && !p_flag) {
298 fprintf(stderr, "%s: illegal URL %s\n", pname, url_char);
299 exit (SCEP_PKISTATUS_ERROR);
302 host_name = strdup(p_char);
306 /* Break down the URL */
308 fprintf(stderr, "%s: missing URL (-u)\n", pname);
309 exit (SCEP_PKISTATUS_ERROR);
311 if (strncmp(url_char, "http://", 7) && !p_flag) {
312 fprintf(stderr, "%s: illegal URL %s\n", pname, url_char);
313 exit (SCEP_PKISTATUS_ERROR);
316 host_name = strdup(p_char);
318 } else if (!(host_name = strdup(url_char + 7)))
323 if (*p == '/' && !p_flag && !c) {
325 if (*(p+1)) dir_name = p + 1;
330 if (*(p+1)) host_port = atoi(p+1);
335 fprintf(stderr, "%s: illegal URL\n", pname);
336 exit (SCEP_PKISTATUS_ERROR);
338 if (host_port < 1 || host_port > 65550) {
339 fprintf(stderr, "%s: illegal port number %d\n", pname,
341 exit (SCEP_PKISTATUS_ERROR);
344 fprintf(stdout, "%s: hostname: %s\n", pname, host_name);
345 fprintf(stdout, "%s: directory: %s\n", pname, dir_name);
346 fprintf(stdout, "%s: port: %d\n", pname, host_port);
349 /* Check algorithms */
351 enc_alg = (EVP_CIPHER *)EVP_des_cbc();
352 } else if (!strncmp(E_char, "blowfish", 8)) {
353 enc_alg = (EVP_CIPHER *)EVP_bf_cbc();
354 } else if (!strncmp(E_char, "des", 3)) {
355 enc_alg = (EVP_CIPHER *)EVP_des_cbc();
356 } else if (!strncmp(E_char, "3des", 4)) {
357 enc_alg = (EVP_CIPHER *)EVP_des_ede3_cbc();
359 fprintf(stderr, "%s: unsupported algorithm: %s\n",
361 exit (SCEP_PKISTATUS_ERROR);
364 sig_alg = (EVP_MD *)EVP_md5();
365 } else if (!strncmp(S_char, "md5", 3)) {
366 sig_alg = (EVP_MD *)EVP_md5();
367 } else if (!strncmp(S_char, "sha1", 4)) {
368 sig_alg = (EVP_MD *)EVP_sha1();
370 fprintf(stderr, "%s: unsupported algorithm: %s\n",
372 exit (SCEP_PKISTATUS_ERROR);
374 /* Fingerprint algorithm */
376 fp_alg = (EVP_MD *)EVP_md5();
377 } else if (!strncmp(F_char, "md5", 3)) {
378 fp_alg = (EVP_MD *)EVP_md5();
379 } else if (!strncmp(F_char, "sha1", 4)) {
380 fp_alg = (EVP_MD *)EVP_sha1();
382 fprintf(stderr, "%s: unsupported algorithm: %s\n",
384 exit (SCEP_PKISTATUS_ERROR);
388 * Switch to operation specific code
390 switch(operation_flag) {
391 case SCEP_OPERATION_GETCA:
393 fprintf(stdout, "%s: SCEP_OPERATION_GETCA\n",
396 /* Set CA identifier */
398 i_char = CA_IDENTIFIER;
400 /* Forge the HTTP message */
401 snprintf(http_string, sizeof(http_string),
402 "GET %s%s?operation=GetCACert&message=%s "
403 "HTTP/1.0\r\n\r\n", p_flag ? "" : "/", dir_name,
405 printf("%s: requesting CA certificate\n", pname);
407 fprintf(stdout, "%s: scep msg: %s", pname,
411 * Response is written to http_response struct "reply".
413 reply.payload = NULL;
414 if ((c = send_msg (&reply, http_string, host_name,
415 host_port, operation_flag)) == 1) {
416 fprintf(stderr, "%s: error while sending "
418 exit (SCEP_PKISTATUS_NET);
420 if (reply.payload == NULL) {
421 fprintf(stderr, "%s: no data, perhaps you "
422 "should define CA identifier (-i)\n", pname);
423 exit (SCEP_PKISTATUS_SUCCESS);
425 printf("%s: valid response from server\n", pname);
426 if (reply.type == SCEP_MIME_GETCA_RA) {
427 /* XXXXXXXXXXXXXXXXXXXXX chain not verified */
430 /* Read payload as DER X.509 object: */
431 bp = BIO_new_mem_buf(reply.payload, reply.bytes);
432 cacert = d2i_X509_bio(bp, NULL);
434 /* Read and print certificate information */
435 if (!X509_digest(cacert, fp_alg, md, &n)) {
436 ERR_print_errors_fp(stderr);
437 exit (SCEP_PKISTATUS_ERROR);
439 printf("%s: %s fingerprint: ", pname,
440 OBJ_nid2sn(EVP_MD_type(fp_alg)));
441 for (c = 0; c < (int)n; c++) {
442 printf("%02X%c",md[c],
443 (c + 1 == (int)n) ?'\n':':');
446 /* Write PEM-formatted file: */
447 if (!(fp = fopen(c_char, "w"))) {
448 fprintf(stderr, "%s: cannot open CA file for "
450 exit (SCEP_PKISTATUS_ERROR);
452 if (PEM_write_X509(fp, cacert) != 1) {
453 fprintf(stderr, "%s: error while writing CA "
455 ERR_print_errors_fp(stderr);
456 exit (SCEP_PKISTATUS_ERROR);
458 printf("%s: CA certificate written as %s\n",
461 pkistatus = SCEP_PKISTATUS_SUCCESS;
464 case SCEP_OPERATION_GETCERT:
465 case SCEP_OPERATION_GETCRL:
466 /* Read local certificate */
468 fprintf(stderr, "%s: missing local cert (-l)\n", pname);
469 exit (SCEP_PKISTATUS_FILE);
471 read_cert(&localcert, l_char);
473 case SCEP_OPERATION_ENROLL:
475 * Read in CA cert, private key and certificate
476 * request in global variables.
481 fprintf(stderr, "%s: missing private key (-k)\n", pname);
482 exit (SCEP_PKISTATUS_FILE);
484 read_key(&rsa, k_char);
486 if ((K_flag && !O_flag) || (!K_flag && O_flag)) {
487 fprintf(stderr, "%s: -O also requires -K (and vice-versa)\n", pname);
488 exit (SCEP_PKISTATUS_FILE);
492 read_key(&renewal_key, K_char);
496 read_cert(&renewal_cert, O_char);
499 if (operation_flag == SCEP_OPERATION_ENROLL)
503 * Create a new SCEP transaction and self-signed
504 * certificate based on cert request
507 fprintf(stdout, "%s: new transaction\n", pname);
508 new_transaction(&scep_t);
509 if (operation_flag != SCEP_OPERATION_ENROLL)
512 fprintf(stdout, "%s: generating selfsigned "
513 "certificate\n", pname);
516 new_selfsigned(&scep_t);
518 /* Use existing certificate */
519 scep_t.signercert = renewal_cert;
520 scep_t.signerkey = renewal_key;
523 /* Write the selfsigned certificate if requested */
525 /* Write PEM-formatted file: */
526 if (!(fp = fopen(L_char, "w"))) {
527 fprintf(stderr, "%s: cannot open "
528 "file for writing\n", pname);
529 exit (SCEP_PKISTATUS_ERROR);
531 if (PEM_write_X509(fp,scep_t.signercert) != 1) {
532 fprintf(stderr, "%s: error while "
533 "writing certificate file\n", pname);
534 ERR_print_errors_fp(stderr);
535 exit (SCEP_PKISTATUS_ERROR);
537 printf("%s: selfsigned certificate written "
538 "as %s\n", pname, L_char);
541 /* Write issuer name and subject (GetCertInitial): */
542 if (!(scep_t.ias_getcertinit->subject =
543 X509_REQ_get_subject_name(request))) {
544 fprintf(stderr, "%s: error getting subject "
545 "for GetCertInitial\n", pname);
546 ERR_print_errors_fp(stderr);
547 exit (SCEP_PKISTATUS_ERROR);
550 if (!(scep_t.ias_getcertinit->issuer =
551 X509_get_issuer_name(cacert))) {
552 fprintf(stderr, "%s: error getting issuer "
553 "for GetCertInitial\n", pname);
554 ERR_print_errors_fp(stderr);
555 exit (SCEP_PKISTATUS_ERROR);
557 /* Write issuer name and serial (GETC{ert,rl}): */
558 scep_t.ias_getcert->issuer =
559 scep_t.ias_getcertinit->issuer;
560 scep_t.ias_getcrl->issuer =
561 scep_t.ias_getcertinit->issuer;
562 if (!(scep_t.ias_getcrl->serial =
563 X509_get_serialNumber(cacert))) {
564 fprintf(stderr, "%s: error getting serial "
565 "for GetCertInitial\n", pname);
566 ERR_print_errors_fp(stderr);
567 exit (SCEP_PKISTATUS_ERROR);
569 /* User supplied serial number */
571 if (!(ASN1_INTEGER_set(scep_t.ias_getcert->serial,
572 (long)atoi(s_char)))) {
573 fprintf(stderr, "%s: error converting "
575 ERR_print_errors_fp(stderr);
576 exit (SCEP_PKISTATUS_ERROR);
581 switch(operation_flag) {
582 case SCEP_OPERATION_ENROLL:
585 "%s: SCEP_OPERATION_ENROLL\n", pname);
586 /* Resum mode: set GetCertInitial */
589 exit (SCEP_PKISTATUS_SUCCESS);
590 printf("%s: requesting certificate (#1)\n",
592 scep_t.request_type = SCEP_REQUEST_GETCERTINIT;
595 printf("%s: sending certificate request\n",
597 scep_t.request_type = SCEP_REQUEST_PKCSREQ;
601 case SCEP_OPERATION_GETCERT:
604 "%s: SCEP_OPERATION_GETCERT\n", pname);
606 scep_t.request_type = SCEP_REQUEST_GETCERT;
607 printf("%s: requesting certificate\n",pname);
610 case SCEP_OPERATION_GETCRL:
613 "%s: SCEP_OPERATION_GETCRL\n", pname);
615 scep_t.request_type = SCEP_REQUEST_GETCRL;
616 printf("%s: requesting crl\n",pname);
620 /* Enter polling loop */
621 while (scep_t.pki_status != SCEP_PKISTATUS_SUCCESS) {
626 p = url_encode(scep_t.request_payload,
629 /* Forge the HTTP message */
630 snprintf(http_string, sizeof(http_string),
631 "GET %s%s?operation="
632 "PKIOperation&message="
633 "%s HTTP/1.0\r\n\r\n",
634 p_flag ? "" : "/", dir_name, p);
637 fprintf(stdout, "%s: scep msg: %s",
641 reply.payload = NULL;
642 if ((c = send_msg (&reply, http_string, host_name,
643 host_port, operation_flag)) == 1) {
644 fprintf(stderr, "%s: error while sending "
646 exit (SCEP_PKISTATUS_NET);
648 /* Verisign Onsite returns strange reply...
649 * XXXXXXXXXXXXXXXXXXX */
650 if ((reply.status == 200) && (reply.payload == NULL)) {
652 scep_t.pki_status = SCEP_PKISTATUS_PENDING;
655 exit (SCEP_PKISTATUS_ERROR);
657 printf("%s: valid response from server\n", pname);
660 scep_t.reply_len = reply.bytes;
661 scep_t.reply_payload = reply.payload;
662 pkcs7_unwrap(&scep_t);
663 pkistatus = scep_t.pki_status;
665 switch(scep_t.pki_status) {
666 case SCEP_PKISTATUS_SUCCESS:
668 case SCEP_PKISTATUS_PENDING:
669 /* Check time limits */
670 if (((t_num * count) >= T_num) ||
674 scep_t.request_type =
675 SCEP_REQUEST_GETCERTINIT;
677 /* Wait for poll interval */
679 printf("%s: waiting for %d secs\n",
682 printf("%s: requesting certificate "
683 "(#%d)\n", pname, count);
689 case SCEP_PKISTATUS_FAILURE:
692 switch (scep_t.fail_info) {
693 case SCEP_FAILINFO_BADALG:
694 exit (SCEP_PKISTATUS_BADALG);
695 case SCEP_FAILINFO_BADMSGCHK:
696 exit (SCEP_PKISTATUS_BADMSGCHK);
697 case SCEP_FAILINFO_BADREQ:
698 exit (SCEP_PKISTATUS_BADREQ);
699 case SCEP_FAILINFO_BADTIME:
700 exit (SCEP_PKISTATUS_BADTIME);
701 case SCEP_FAILINFO_BADCERTID:
702 exit (SCEP_PKISTATUS_BADCERTID);
703 /* Shouldn't be there... */
705 exit (SCEP_PKISTATUS_ERROR);
708 fprintf(stderr, "%s: unknown "
709 "pkiStatus\n", pname);
710 exit (SCEP_PKISTATUS_ERROR);
713 /* We got SUCCESS, analyze the reply */
714 switch (scep_t.request_type) {
716 /* Local certificate */
717 case SCEP_REQUEST_PKCSREQ:
718 case SCEP_REQUEST_GETCERTINIT:
719 write_local_cert(&scep_t);
722 /* Other end entity certificate */
723 case SCEP_REQUEST_GETCERT:
724 write_other_cert(&scep_t);
729 case SCEP_REQUEST_GETCRL:
738 fprintf(stdout, "\nsscep version %s\n\n" , VERSION);
739 fprintf(stdout, "Usage: %s OPERATION [OPTIONS]\n"
740 "\nAvailable OPERATIONs are\n"
741 " getca Get CA/RA certificate(s)\n"
742 " enroll Enroll certificate\n"
743 " getcert Query certificate\n"
744 " getcrl Query CRL\n"
745 "\nGeneral OPTIONS\n"
746 " -u <url> SCEP server URL\n"
747 " -p <host:port> Use proxy server at host:port\n"
748 " -f <file> Use configuration file\n"
749 " -c <file> CA certificate file (write if OPERATION is getca)\n"
750 " -E <name> PKCS#7 encryption algorithm (des|3des|blowfish)\n"
751 " -S <name> PKCS#7 signature algorithm (md5|sha1)\n"
752 " -v Verbose operation\n"
753 " -d Debug (even more verbose operation)\n"
754 "\nOPTIONS for OPERATION getca are\n"
755 " -i <string> CA identifier string\n"
756 " -F <name> Fingerprint algorithm\n"
757 "\nOPTIONS for OPERATION enroll are\n"
758 " -k <file> Private key file\n"
759 " -r <file> Certificate request file\n"
760 " -K <file> Signature private key file, use with -O\n"
761 " -O <file> Signature certificate (used instead of self-signed)\n"
762 " -l <file> Write enrolled certificate in file\n"
763 " -e <file> Use different CA cert for encryption\n"
764 " -L <file> Write selfsigned certificate in file\n"
765 " -t <secs> Polling interval in seconds\n"
766 " -T <secs> Max polling time in seconds\n"
767 " -n <count> Max number of GetCertInitial requests\n"
768 " -R Resume interrupted enrollment\n"
769 "\nOPTIONS for OPERATION getcert are\n"
770 " -k <file> Private key file\n"
771 " -l <file> Local certificate file\n"
772 " -s <number> Certificate serial number\n"
773 " -w <file> Write certificate in file\n"
774 "\nOPTIONS for OPERATION getcrl are\n"
775 " -k <file> Private key file\n"
776 " -l <file> Local certificate file\n"
777 " -w <file> Write CRL in file\n\n", pname);
782 catchalarm(int signo) {
783 fprintf(stderr, "%s: connection timed out\n", pname);
784 exit (SCEP_PKISTATUS_TIMEOUT);