3 # CACreateCert - Create various types of certificates
4 # Copyright (C) 2011-2017 Kyle J. McKay.
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
28 use Digest
::MD5
qw(md5 md5_hex md5_base64);
29 use Getopt
::Long
qw(:config gnu_getopt);
40 $VERSIONMSG = "CACreateCert version $VERSION\n" .
41 "Copyright (c) 2011-2015 Kyle J. McKay. All rights reserved.\n" .
42 "License AGPLv3+: GNU Affero GPL version 3 or later.\n" .
43 "http://gnu.org/licenses/agpl.html\n" .
44 "This is free software: you are free to change and redistribute it.\n" .
45 "There is NO WARRANTY, to the extent permitted by law.\n";
55 sha1 sha1_hex sha1_base64
56 sha224 sha224_hex sha224_base64
57 sha256 sha256_hex sha256_base64
58 sha384 sha384_hex sha384_base64
59 sha512 sha512_hex sha512_base64
63 require Digest
::SHA
::PurePerl
;
67 sha1 sha1_hex sha1_base64
70 Digest
::SHA
::PurePerl
->import(
72 sha224 sha224_hex sha224_base64
73 sha256 sha256_hex sha256_base64
74 sha384 sha384_hex sha384_base64
75 sha512 sha512_hex sha512_base64
79 require Digest
::SHA
::PurePerl
;
80 Digest
::SHA
::PurePerl
->import(
82 sha1 sha1_hex sha1_base64
83 sha224 sha224_hex sha224_base64
84 sha256 sha256_hex sha256_base64
85 sha384 sha384_hex sha384_base64
86 sha512 sha512_hex sha512_base64
93 sha1 sha1_hex sha1_base64
96 die "One of Digest::SHA1 or Digest::SHA or Digest::SHA::PurePerl "
97 . "must be available\n";
99 eval {(`openssl version -v 2>/dev/null` || '') =~ /^(?:Open|Libre)SSL /} ||
100 die "OpenSSL/LibreSSL (as the openssl command) is not available in the PATH\n";
105 Usage: CACreateCert [-h] [--version] [--verbose] [--debug] [--quiet] [--check]
106 [--now] [--pubx509] [-t] [--digest=sha1|sha224|sha256|sha384|sha512]
107 [--root | --subca | --server | --codesign | --applecodesign | --email]
108 [--client] [--rootauth] [--authext] [--pathlen n] [--suffix suffix.pem]
109 [--random | --no-random] [--in pub_key_file] [--out out_cert.pem]
110 [--cert signing_cert] [--dni k=v] [--dns "name-or-ip"] [--dnq "qual"]
111 --key priv_key_file "name string"
115 CACreateCert -- create X.509 certificate
118 CACreateCert [-h] [--version] [--verbose] [--debug] [--quiet] [--check]
119 [--now] [--pubx509] [-t] [--digest=sha1|sha224|sha256|sha384|sha512]
120 [--root | --subca | --server | --codesign | --applecodesign | --email]
121 [--client] [--rootauth] [--authext] [--pathlen n] [--suffix suffix.pem]
122 [--random | --no-random] [--in pubkeyfile] [--out pemcertfile]
123 [--cert signing_cert] [--dni k=v] [--dns "name-or-ip"] [--dnq "qual"]
124 --key priv_key_file "name string"
125 CACreateCert --root [--random] --key priv_key_file "name string" > ca.pem
126 CACreateCert --key priv_key_file --cert signing_cert "name string"
127 < pub_key_file > out_cert.pem
130 CACreateCert creates a new certificate. Various certificate types are
131 supported including root CA certificates, sub CA certificates, server
132 certificates, code signing certificates, email certificates and client
135 When creating a certificate, the only private key required is that of
136 the signer. This means that, for example, a new client authentication
137 certifcate can be created given only the public key of the client and
138 the private key of the signing certificate.
140 The public key for the certificate being created may be provided in
141 either OpenSSH .pub format or X.509 public key format. The
142 "openssl rsa -pubout" or "openssl x509 -noout -pubkey" commands can be
143 used to produce an X.509 format public key from either an RSA private key
144 or an X.509 certificate respectively. Note that only OpenSSH RSA public
145 keys in protocol format 2 are supported (they start with "ssh-rsa ").
147 When creating a root certificate no public key is required.
149 The "name string" value given must be appropriate for the type of
150 certificate being created. For a client authentication certificate it
151 will typically be a *nix user login (all lowercase) on the server to
152 which clients connect to. For an email certificate it will typically
153 be the full email address. For a server it will generally be the
154 canonical DNS name of the server.
156 No validation is performed on the "name string" value except that it
157 must not be the empty string. It may be provided in either Latin-1 or
160 The priv_key_file must be an RSA private key file in PEM or DER format
161 and furthermore it must not have a password (both openssl genrsa and
162 ssh-keygen -t rsa can create these kinds of RSA private key files). If
163 a host is running an OpenSSH sshd daemon, then it probably already has a
164 suitable host private RSA key in either /etc/ssh/ssh_host_rsa_key or
165 /etc/ssh_host_rsa_key that can be used if desired.
167 The signing_cert must be an X.509 certificate that uses priv_key_file as
168 its private key. It may be in either PEM or DER format. The created
169 certificate will be signed by the signing_cert. The CACreateCert utility
170 can be used to create a suitable signing_cert certificate authority
171 certificate from the priv_key_file if desired by using the --root option.
173 When creating a root certificate no signing_cert is required.
175 On success the new certificate is written in PEM format to standard
176 output. (All error/information messages are written to standard error.)
178 Note that certificates created by CACreateCert are deterministic (i.e.
179 the bytewise identical certificate will be output) given the identical
180 input arguments only so long as neither the --random nor --now options
183 Also note that the certificates created by this utility do not expire.
190 Show the CACreateCert version
193 Produce extra informational messages to standard error.
197 Show a list of recognized key names for the --dni option. If
198 given other options are ignored.
201 Same as --verbose --help.
204 Show debugging information. Automatically enables --verbose.
208 Suppress all messages except errors. Ignored if --debug or
212 Perform all normal validation checks (except for a non-empty
213 "name string") but do not actually produce a certificate.
214 Automatically enables --verbose.
217 Normally the validity not before date will be set to the signing
218 certificate's not before date or the approval date of the X.509
219 v3 standard (root certificates). Using this option causes the
220 not before validity date of the generated certificate to be set
221 to the current time. Use of this option will preclude production
222 of byte-exact matching output certificates for the same input
226 Force the public key read from standard input to be interpreted
227 as an X.509 format public key. Normally this should be
228 automatically detected and this option should not be needed.
229 This option is ignored if --root is given.
232 Allow reading the public key from standard input when standard
233 input is a tty. In most cases attempting to read the public key
234 from standard input that is a tty indicates that the public key
235 was accidentally omitted. If that is not the case, the -t option
236 must be given to allow reading the public key from standard input
237 when standard input is a tty. This option is always implied if
238 the --in option is used with a value other than "-".
241 Select the digest to use in the generated certificate. Must be
242 one of sha1, sha224, sha256, sha384 or sha512. By default sha256
243 will be used if available otherwise sha1 will be used (and a
244 warning issued). All systems support sha1 digest certificates,
245 but sha1 should really not be used anymore (see NIST
246 recommendation SP 800-131A). OpenSSL starting with version 0.9.8
247 (released 2005-07-05) supports the SHA-2 family of hash functions
248 (sha224, sha256, sha384 and sha512) which should be used instead
249 of sha1. Note that either Digest::SHA or Digest::SHA::PurePerl
250 must be available to use sha224, sha256, sha384 or sha512.
259 Select the type of certificate to generate. If --root is given
260 then a root certificate will be created and any --cert option will
261 be ignored as well as standard input. If none of these options is
262 given then --client will be assumed. Both --root and --subca
263 generate certificate authority certificates (CA:TRUE).
264 Specifying any of --root, --subca, --server, --codesign or
265 --applecodesign will cause the "name string" to be embedded as
266 a commonName (CN). Otherwise if --email is specified
267 "name string" will be embedded as an emailAddress (and a subject
268 alternative name email type). Finally if none of those apply then
269 "name string" will be embedded as a userId (UID) instead
270 (client certificates). The certificate's key usage bits will be
271 set to one of four values. --root or --subca select the first,
272 --server selects the second, --email selects the third otherwise
273 the fourth is used. If any of --server, --client (explicit or
274 implied), --codesign, --email or --applecodesign are given then
275 extended key usage items will be included (up to five -- one for
279 Giving this option causes most of the other options to be ignored.
280 A unique "Acme" root certificate will be created and output each
281 time CACreateCert is run with this option. The only required
282 option is --key when generating an "Acme" certificate.
285 The --pathlen option will be ignored unless --subca is given in
286 which case the X509v3 Basic Constraints will include the
287 specified pathlen value.
290 Ignored unless --root given. Normally --root certificates do not
291 include an X509v3 Authority Key Identifier. If this option is
292 given then they will (with only a keyid value).
295 Ignored if --root given. Normally non --root certificates include
296 an X509v3 Authority Key Identifier section with only a keyid
297 value. If this option is given, then the name and serial number
298 will also be included.
301 Use a random serial number instead of one determined by the actual
302 contents of the certificate. When generating a --root certificate
303 the issuer name will also include a serialNumber attribute as the
304 first item followed by the "name string" value (as the CN
305 attribute). This can be modified by use of one or more --dni
306 options and if a --dni serialNumber=# option is used then the '#'
307 will be replaced by the actual serial number generated and in the
308 case of a --root certificate the extra serialNumber attribute at
309 the beginning of the issuer name will be suppressed. Only one
310 --dni serialNumber=# is allowed and it must be non-multivalued.
311 Use of this option will preclude production of byte-exact matching
312 output certificates for the same input arguments. This is now the
313 default when --root is given. Roughly equivalent to adding a
314 --dni serialNumber=randomvalue option BEFORE any other --dni
315 options and then using --no-random.
318 Ignored unless --root given. Turns off the default --random
319 option that is normally enabled by default when --root is given.
322 The RSA private key in either PEM or DER format. This option
326 Ignored if --root is given. The signing X.509 certificate in
327 either PEM or DER format. The public key embedded in signing_cert
328 must match the one in the priv_key_file or an error will occur.
331 Ignored if --root is given. The public key for the certificate
332 to be created. Must be different than the public key contained in
333 priv_key_file. May be an OpenSSH protocol 2 format RSA public key
334 or an X.509 format public key (in either PEM or DER format). See
335 also the --pubx509 option. If pub_key_file is "-" or this option
336 is omitted then standard input is read.
339 The generated certificate will be written to out_cert.pem. If
340 this option is omitted or out_cert.pem is "-" then the generated
341 certificate is written to standard output.
344 Primarily intended to be used when generating client certificates,
345 if this option is given, then the entire contents of suffix.pem is
346 written to the same location as the generated certificate
347 immediately following the certificate. This option may be given
348 more than once in which case the files will be appended to the
349 output in the order the --suffix options were given. See the
350 NOTES section below for relevant information on use of --suffix.
352 --dni [+]key=value | \@filename
353 Add distinguished name information. This option may be repeated
354 multiple times as desired. Key is either the name of an OID
355 attribute to add or a dotted OID number. Recognized names may be
356 listed with the --list-oid-names option. Value is a string value
357 to assign to the key. Whitespace before and after as well as any
358 surrounding the '+' and '=' is completely ignored. If a '+' is
359 prefixed to the key, the value will be combined with the previous
360 --dni item to make it multi-valued. The '+' prefix requires at
361 least one previous --dni item that does NOT have a '+' prefix.
362 Instead of '[+]key=value', '\@filename' may be used instead. If
363 filename is '-' then standard input will be read provided it is
364 not already being used to read the public key. The 'filename'
365 must contain one --dni directive per line (with the '--dni' part
366 omitted). Blank lines and lines with the first non-blank
367 character a '#' are ignored. The directives in the file are
368 processed exactly as though they each appeared as a command line
369 '--dni' option in the order they appear in the file. See also
370 the description for the --random option for details of the
371 '--dni serialNumber=#' variant and the description of the "name
372 string" for details of the '--dni emailAddress=\@' variant.
375 Ignore any maximum length limits on most --dni values. A warning
376 will still be issued unless --quiet is also used.
378 --dns domain-name-or-ip
379 Ignored unless --server given. Adds the given domain-name-or-ip
380 as a subject alternative name (either DNS or IPAddress). May
381 be repeated to add multiple alternative names. A DNS name must
382 satisfy RFC 1034 section 3.5 as modified by RFC 1123 section 2.1
383 except that the leftmost label may be the single character '*'.
384 An IP address may be either IPv4 or IPv6 (do NOT use surrounding
385 '[', ']' characters on an IPv6 address). An IPv6 address MUST NOT
386 have a scope identifier. Note that when --server is given, the
387 common name (CN) value is NOT automatically added as a subject
388 alternative name -- it must be specified explicitly with a --dns
389 option if that is desired (and normally it IS desirable).
392 Optional for all certificate types. If given must not be the
393 empty string. Will be embedded into the subject's distinguished
394 name as the final component. Use of this option is not
395 recommended when using --server or --email. The value must be
396 a PrintableString (needs to match [A-Za-z0-9 '()+,./:=?-]+).
397 Equivalent to adding a --dni dnQualifier=qual option after any
398 other --dni options except that it is also placed AFTER any
399 "name string" value as well.
402 The name to embed in the certificate as the subject. This will
403 be embedded as a common name (CN) except when --client is in
404 effect in which case it will be embedded as a user id (UID) or
405 when --email is in effect in which case it will be embedded as
406 an email address in both the subject and subject alternative name.
407 The "name string" value may never be omitted but may be explictly
408 given as the empty string ('' or "") when generating a root
409 certificate using a random serial number or when the distinguished
410 name attribute key that would be embedded (e.g. CN or UID) has
411 already had a value specified using a --dni option AND the
412 --email option has NOT been given. The name string is ignored,
413 if present, when --acme is used. For --email, however, if a
414 --dni emailAddress=\@ option appears then the email address will
415 be embedded into the subject at that location rather than at the
416 default location (which is after the --dni values but before the
420 All systems support sha1 digest certificates, but sha1 should really not
421 be used anymore (NIST recommendation SP 800-131A). OpenSSL starting
422 with versions 0.9.8 (released 2005-07-05) supports the SHA-2 family of
423 hash functions (sha224, sha256, sha384 and sha512) which should be used
426 NIST SP 800-131A requires use of an RSA key with 2048 or more bits and
427 a hash function with 224 or more bits after December 31 2010.
429 RFC 6194 states sha256 is the most commonly used alternative to sha1
430 (and will be used by default if a suitable SHA module is available).
432 Note that NIST SP 800-78-3 requires RSA public key exponents to be
433 greater than or equal to 65537. OpenSSH version 5.4 and later generate
434 RSA keys with a public exponent of 65537 otherwise openssl genrsa can
435 be used together with ssh-keygen -y to create a suitable OpenSSH key that
436 uses an exponent of 65537 instead of 35.
438 A client attempting to authenticate a server and following the rules in
439 RFC 6125 will COMPLETELY IGNORE the value given for a server certificate's
440 common name (CN) if any alternative name DNS values are present. Although
441 that standard does not discuss IP alternative names, the user of this
442 utility is strongly advised to include the value from the server's CN
443 as one of the --dns values given if ANY --dns values are given.
445 Using an IPv6 address as a --dns value will not interfere with the
446 correct working of an IPv4 address and/or DNS name as a --dns value, but
447 some older clients do not seem to reliably support checking IPv6 address
448 alternative name values so do not rely on them actually working
449 everywhere in practice. The problematic clients stubbornly insist there
450 is a host name mismatch.
452 Link-local IPv6 addresses are unlikely to work since there's no way to
453 embed a scope value (and client support for a scope specifier is rare).
454 However, some platforms (i.e. Darwin) allow the scope number to be
455 embedded as octets 2 and 3 so that address FE80::... with a scope_id
456 value of 5 can be represented as FE80:5::... instead. Of course the
457 correct scope_id value is host-specific, so a range of possibilities
458 would have to be included although since the loopback interface is
459 almost always scope_id 1 that one can be skipped. Current versions
460 of the cURL command line utility DO actually correctly handle scope
461 ids as described in RFC 6874, correctly stripping off the zone value
462 from the host name included in the 'Host:' HTTP header.
464 The TLS (Transport Layer Security -- also sometimes referred to by the
465 previous standard's name, SSL [Secure Sockets Layer]) requires the peer
466 (either the server when the client is authenticating it or the client
467 when the server is authenticating it if client certificates are in use)
468 to supply the ENTIRE certificate chain in order from the leaf
469 certificate (that identifies the peer) on up through and INCLUDING the
470 root certificate (also known as the certificate authority). Reference
471 RFC 5246 section 7.4.2 and the description of "certificate_list". But
472 there is an exception listed, you MAY omit the last certificate (aka the
473 "root" certificate, "certificate authority" certificate or "self-signed"
474 certificate) under the assumption that the peer already has that or it
475 would not be able to decide whether or not to trust the certificate
476 even if it turns out to be a valid one. What this means is that if you
477 are generating a client certificate and the client certificate's issuer
478 is not the root certificate for the chain, then you should probably add
479 the --suffix <path_to_issuer_cert> option for its issuer and if the
480 issuer's issuer is not the root certificate, add another --suffix option
481 and so on until the issuer of the certificate listed with the final
482 --suffix option is the root certificate (you may also go ahead and
483 include one more --suffix option for the root certificate but it's
484 likely unnecessary since the server should have that). But, if the
485 server is using public key pinning to validate the root certificate then
486 you must include the root certificate just as RFC 5246 says to do.
489 Display the currently available version of OpenSSL with:
493 Display the currently available version of OpenSSH with:
497 When adding --dni values, the convention is to list the most general
498 first (e.g. country) moving on to more specific and ending with common
499 name (CN) (and for email, the emailAddress), possibly finally followed
500 by a dnQualifier if any. This ordering is not set in stone but is
501 generally recommended.
503 Convert a regular certificate into a certificate request using openssl:
505 openssl x509 -in cert.pem -signkey key.pem -x509toreq -out csr.pem
508 Valid certificate chains always must contain at least two certificates,
509 the leaf certificate and the root certificate. There may be additional
510 intermediate certificates in the chain between them.
512 '''CREATING A SERVER CERTIFICATE'''
514 Here's an example of how to quickly create a valid web server
515 certificate chain for an 'example.com' web server:
517 1. Create a root certificate key:
519 openssl genrsa -f4 -out root_key.pem 3072
521 2. Generate a server certificate key and extract its public key:
523 openssl genrsa -f4 -out server_key.pem 3072
524 openssl rsa -in server_key.pem -pubout -out server_key_pub.pem
526 3. Generate an Acme root certificate:
528 CACreateCert --acme --key root_key.pem --out root_cert.pem
530 4. Generate a server certificate for 'example.com':
532 CACreateCert --server --key root_key.pem --cert root_cert.pem \
533 --in server_key_pub.pem --out server_cert.pem "example.com"
535 Optionally add one or more --dns and/or --dni options when generating
536 the --server certificate.
538 To configure a web server to serve 'example.com' over https the
539 root_cert.pem, server_cert.pem and server_key.pem files will be needed.
541 Clients connecting to 'example.com' over https will get an untrusted
542 root certificate error unless they trust root_cert.pem as a certificate
543 authority or otherwise add an exception.
545 '''CREATING AN EMAIL CERTIFICATE'''
547 Here's an example of how to quickly create a valid email certificate
548 chain for the name "John Smith" with email address <jsmith\@example.com>:
550 1. Create a root certificate key:
552 openssl genrsa -f4 -out root_key.pem 3072
554 2. Generate an email certificate key and extract its public key:
556 openssl genrsa -f4 -out email_key.pem 3072
557 openssl rsa -in email_key.pem -pubout -out email_key_pub.pem
559 3. Generate an Acme root certificate:
561 CACreateCert --acme --key root_key.pem --out root_cert.pem
563 4. Generate an email certificate for "John Smith" <jsmith\@example.com>:
565 CACreateCert --email --key root_key.pem --cert root_cert.pem \
566 --in email_key_pub.pem --out email_cert.pem --dni CN="John Smith" \
567 "jsmith\@example.com"
569 Additional information may be added with more --dni options or the
570 common name (CN) may even just be omitted entirely.
572 5. Create a .p12 file containing the two certs and the private key:
574 This step is optional but may be the only way to get the key into your
575 system (e.g. Mac OS X Keychain -- which also requires you to either
576 trust the root for basic X.509 policy or the cert itself for S/MIME).
578 openssl pkcs12 -export -inkey email_key.pem -in email_cert.pem \
579 -certfile root_cert.pem -nodes -out email_certs.p12 \
580 -passout pass:"dummy" -name "My Email Key"
582 Use a better password than dummy though. :)
584 To configure an email client to use the new certificate, the
585 root_cert.pem, email_cert.pem and email_key.pem files will be needed or
586 alternatively the email_certs.p12 file.
587 (Some email software will let you squeak by without root_cert.pem.)
591 The ability to create self-signed types other than --root by combining
592 the --root option with one of the others (e.g. --client, --email,
593 --codesign, --server) is poorly documented. Furthermore, since the
594 standard (see RFC 5280) effectively requires at least two certificates
595 in a valid certificate chain since a chain must have a non-root leaf
598 DSA is not supported even though it is possible to create a valid
599 certificate that uses dsaWithSHA1. But since SHA-1 should not be used
600 any longer after 2010-12-31 (NIST SP 800-131A) it's no big loss. And
601 there do not seem to be any identifiers available for DSA with longer
602 hash algorithms anyway.
604 The ability to sign using whirlpool, which requires use of an unofficial
605 OID (1.2.840.113549.1.1.15) should, perhaps, not be allowed.
612 # Return 0 if non-UTF-8 sequences present
613 # Return -1 if no characters > 0x7F found
614 # Return 1 if valid UTF-8 sequences present
616 return -1 if $_[0] !~ /[\x80-\xFF]/so;
617 my $l = length($_[0]);
618 for (my $i=0; $i<$l; ++$i) {
619 my $c = ord(substr($_[0],$i,1));
621 return 0 if $c < 0xC0 || $c >= 0xF8;
625 return 0 if $i >= $l;
626 my $c2 = ord(substr($_[0],$i,1));
627 return 0 if $c2 < 0x80 || $c2 > 0xBF;
628 my $u = (($c & 0x1F) << 6) | ($c2 & 0x3F);
629 return 0 if $u < 0x80;
635 return 0 if $i >= $l;
636 my $c2 = ord(substr($_[0],$i-1,1));
637 return 0 if $c2 < 0x80 || $c2 > 0xBF;
638 my $c3 = ord(substr($_[0],$i,1));
639 return 0 if $c3 < 0x80 || $c3 > 0xBF;
640 my $u = (($c & 0x0F) << 12) | (($c2 & 0x3F) << 6) | ($c3 & 0x3F);
641 return 0 if $u < 0x800 || ($u >= 0xD800 && $u <= 0xDFFFF) || $u >= 0xFFFE;
646 return 0 if $i >= $l;
647 my $c2 = ord(substr($_[0],$i-2,1));
648 return 0 if $c2 < 0x80 || $c2 > 0xBF;
649 my $c3 = ord(substr($_[0],$i-1,1));
650 return 0 if $c3 < 0x80 || $c3 > 0xBF;
651 my $c4 = ord(substr($_[0],$i,1));
652 return 0 if $c4 < 0x80 || $c4 > 0xBF;
653 my $u = (($c & 0x07) << 18) | (($c2 & 0x3F) << 12) | (($c3 & 0x3F) << 6)
655 return 0 if $u < 0x10000 || $u >= 0x10FFFE || (($u & 0xFFFF) >= 0xFFFE);
665 # Provide translations for 0x80-0x9F into UTF-8
666 $W1252{0x80} = pack('H*','E282AC'); # 0x20AC Euro
667 $W1252{0x82} = pack('H*','E2809A'); # 0X201A Single Low-9 Quote
668 $W1252{0x83} = pack('H*','C692'); # 0x0192 Latin Small Letter f With Hook
669 $W1252{0x84} = pack('H*','E2809E'); # 0x201E Double Low-9 Quote
670 $W1252{0x85} = pack('H*','E280A6'); # 0x2026 Horizontal Ellipsis
671 $W1252{0x86} = pack('H*','E280A0'); # 0x2020 Dagger
672 $W1252{0x87} = pack('H*','E280A1'); # 0x2021 Double Dagger
673 $W1252{0x88} = pack('H*','CB86'); # 0x02C6 Modifier Letter Circumflex Accent
674 $W1252{0x89} = pack('H*','E28080'); # 0x2030 Per Mille Sign
675 $W1252{0x8A} = pack('H*','C5A0'); # 0x0160 Latin Capital Letter S With Caron
676 $W1252{0x8B} = pack('H*','E28089'); # 0x2039 Left Single Angle Quote
677 $W1252{0x8C} = pack('H*','C592'); # 0x0152 Latin Capital Ligature OE
678 $W1252{0x8E} = pack('H*','C5BD'); # 0x017D Latin Capital Letter Z With Caron
679 $W1252{0x91} = pack('H*','E28098'); # 0x2018 Left Single Quote
680 $W1252{0x92} = pack('H*','E28099'); # 0x2019 Right Single Quote
681 $W1252{0x93} = pack('H*','E2809C'); # 0x201C Left Double Quote
682 $W1252{0x94} = pack('H*','E2809D'); # 0x201D Right Double Quote
683 $W1252{0x95} = pack('H*','E280A2'); # 0x2022 Bullet
684 $W1252{0x96} = pack('H*','E28093'); # 0x2013 En Dash
685 $W1252{0x97} = pack('H*','E28094'); # 0x2014 Em Dash
686 $W1252{0x98} = pack('H*','CB9C'); # 0x02DC Small Tilde
687 $W1252{0x99} = pack('H*','E284A2'); # 0x2122 Trade Mark Sign
688 $W1252{0x9A} = pack('H*','C5A1'); # 0x0161 Latin Small Letter s With Caron
689 $W1252{0x9B} = pack('H*','E2808A'); # 0x203A Right Single Angle Quote
690 $W1252{0x9C} = pack('H*','C593'); # 0x0153 Latin Small Ligature oe
691 $W1252{0x9E} = pack('H*','C5BE'); # 0x017E Latin Small Letter z With Caron
692 $W1252{0x9F} = pack('H*','C5B8'); # 0x0178 Latin Cap Letter Y With Diaeresis
700 return $_[0] if (IsUTF8
($_[0]));
702 foreach my $c (unpack('C*',$_[0])) {
707 # Ass/u/me we have Latin-1 (ISO-8859-1) but per the HTML 5 draft treat
709 if ($c >= 0xA0 || !defined($W1252{$c})) {
710 $ans .= chr(0xC0 | ($c >> 6));
711 $ans .= chr(0x80 | ($c & 0x3F));
724 my $fancy = shift || 0;
726 $str = join('',map($_."\b".$_, split(//,$str)));
734 my $fancy = shift || 0;
736 $str = join('',map("_\b".$_, split(//,$str)));
744 my $fancy = shift || 0;
745 my @inlines = split(/\n/, $man, -1);
747 foreach my $line (@inlines) {
748 if ($line =~ /^[A-Z]+$/) {
749 $line = formatbold
($line, $fancy);
752 $line =~ s/'''(.+?)'''/formatbold($1,$fancy)/gse;
753 $line =~ s/''(.+?)''/formatul($1,$fancy)/gse;
755 push (@outlines, $line);
757 my $result = join("\n", @outlines);
758 $result =~ s/\\\n//gso;
765 my %oidstringlengths;
766 my %oidstringrestrictions;
771 'commonName' => '2.5.4.3',
773 'surname' => '2.5.4.4',
775 'serialNumber' => '2.5.4.5',
776 'serial' => '2.5.4.5',
777 'countryName' => '2.5.4.6',
779 'localityName' => '2.5.4.7',
781 'stateOrProvinceName' => '2.5.4.8',
783 'streetAddress' => '2.5.4.9',
784 'street' => '2.5.4.9',
785 'organizationName' => '2.5.4.10',
787 'organizationalUnitName' => '2.5.4.11',
789 'title' => '2.5.4.12',
790 'description' => '2.5.4.13',
791 'businessCategory' => '2.5.4.15',
792 'postalCode' => '2.5.4.17',
793 'telephoneNumber' => '2.5.4.20',
794 'facsimileTelephoneNumber' => '2.5.4.23',
795 'givenName' => '2.5.4.42',
797 'initials' => '2.5.4.43',
798 'generationQualifier' => '2.5.4.44',
799 'dnQualifier' => '2.5.4.46',
800 'pseudonym' => '2.5.4.65',
801 'organizationIdentifier' => '2.5.4.97',
802 'userId' => '0.9.2342.19200300.100.1.1',
803 'UID' => '0.9.2342.19200300.100.1.1',
804 'domainComponent' => '0.9.2342.19200300.100.1.25',
805 'DC' => '0.9.2342.19200300.100.1.25',
806 'emailAddress' => '1.2.840.113549.1.9.1',
807 'jurisdictionOfIncorporationLocalityName'=> '1.3.6.1.4.1.311.60.2.1.1',
808 'jurisdictionOfIncorporationLocality'=> '1.3.6.1.4.1.311.60.2.1.1',
809 'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1',
810 'jurisdictionLocality' => '1.3.6.1.4.1.311.60.2.1.1',
811 'jurisdictionOfIncorporationStateOrProvinceName'=> '1.3.6.1.4.1.311.60.2.1.2',
812 'jurisdictionOfIncorporationStateOrProvince'=> '1.3.6.1.4.1.311.60.2.1.2',
813 'jurisdictionStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2',
814 'jurisdictionStateOrProvince' => '1.3.6.1.4.1.311.60.2.1.2',
815 'jurisdictionOfIncorporationCountryName'=> '1.3.6.1.4.1.311.60.2.1.3',
816 'jurisdictionOfIncorporationCountry'=> '1.3.6.1.4.1.311.60.2.1.3',
817 'jurisdictionCountryName' => '1.3.6.1.4.1.311.60.2.1.3',
818 'jurisdictionCountry' => '1.3.6.1.4.1.311.60.2.1.3',
820 # Some extra help here for those last long ones and some obvious abbreviations
821 'joiL' => '1.3.6.1.4.1.311.60.2.1.1',
822 'joiST' => '1.3.6.1.4.1.311.60.2.1.2',
823 'joiC' => '1.3.6.1.4.1.311.60.2.1.3',
824 'country' => '2.5.4.6',
826 'locality' => '2.5.4.7',
827 'state' => '2.5.4.8',
828 'province' => '2.5.4.8',
829 'stateOrProvince' => '2.5.4.8',
830 'organization' => '2.5.4.10',
831 'organizationalUnit' => '2.5.4.11',
833 'zipCode' => '2.5.4.17',
834 'phone' => '2.5.4.20',
835 'phoneNumber' => '2.5.4.20',
837 'faxNumber' => '2.5.4.23',
839 'email' => '1.2.840.113549.1.9.1'
841 # p => PrintableString, i => IA5String, u => UTF8String
842 # If not listed prefer PrintableString if compatible otherwise UTF8String
844 '2.5.4.5' => 'p', # serialNumber
845 '2.5.4.6' => 'p', # countryName
846 '2.5.4.46' => 'p', # dnQualifier
847 '1.2.840.113549.1.9.1' => 'i', # emailAddress
848 '0.9.2342.19200300.100.1.25' => 'i' # domainComponent
850 # Exact number of characters required if single number "n"
851 # Minimum number of characters required if "n,"
852 # Maximum number of characters if ",n"
853 # Minimum and maximum number of characters if "n,m"
854 %oidstringlengths = (
855 '2.5.4.3' => ',64', # commonName
856 '2.5.4.4' => ',40', # surname
857 '2.5.4.5' => ',64', # serialNumber
858 '2.5.4.6' => 2 , # countryName
859 '2.5.4.7' => ',128', # localityName
860 '2.5.4.8' => ',128', # stateOrProvinceName
861 '2.5.4.9' => ',30', # streetAddress
862 '2.5.4.10' => ',64', # organizationName
863 '2.5.4.11' => ',32', # organizationUnitName
864 '2.5.4.12' => ',64', # title
865 '2.5.4.17' => ',16', # postalCode
866 '2.5.4.42' => ',16', # givenName
867 '2.5.4.43' => ',5', # initials
868 '2.5.4.44' => ',3', # generationQualifier
869 '2.5.4.65' => ',128', # pseudonym
870 '1.2.840.113549.1.9.1' => ',255' # emailAddress
872 # 'e' => must be an email address, 'd' => must be a domain name label
873 # 'c' => must be ISO 3166 2-character country code (2 'A'-'Z' will do)
874 %oidstringrestrictions = (
875 '2.5.4.6' => 'c', # 2-character country code
876 '1.2.840.113549.1.9.1' => 'e', # emailAddress RFC 5280 4.2.1.6 rfc822Name
877 # See 'Mailbox' Section 4.1.2 of RFC 2821
878 '0.9.2342.19200300.100.1.25' => 'd' # domainComponent RFC 4519
884 foreach my $key (keys(%oiddata)) {
885 my $l = length($key);
886 $maxlen = $l if $l > $maxlen;
887 my $value = $oiddata{$key};
888 $knownoids{$value} = 1;
889 $oidnames{lc($key)} = $value;
890 my $nlist = $aliases{$value};
891 $nlist = [], $aliases{$value} = $nlist unless $nlist;
895 foreach my $oid (keys(%aliases)) {
896 my $aliases = $aliases{$oid};
897 my @sorted = sort({length($b) <=> length($a)} @
$aliases);
898 my $canon = shift(@sorted);
899 push(@list, sprintf('%-*s = %s', $maxlen, $canon, $oid));
900 foreach my $alias (@sorted) {
901 push(@list, sprintf('%-*s -> %s', $maxlen, $alias, $canon));
904 $oidnamelist = join('', map("$_\n", sort({lc($a) cmp lc($b)} @list)));
909 # return a DER encoded length
911 return pack('C',$len) if $len <= 127;
912 return pack('C2',0x81, $len) if $len <= 255;
913 return pack('Cn',0x82, $len) if $len <= 65535;
914 return pack('CCn',0x83, ($len >> 16), $len & 0xFFFF) if $len <= 16777215;
915 # Silently returns invalid result if $len > 2^32-1
916 return pack('CN',0x84, $len);
921 # return a single DER encoded OID component
925 my $result = pack('C', $num & 0x7F);
928 $result = pack('C', 0x80 | ($num & 0x7F)) . $result;
936 # return a DER encoded OID complete with leading 0x06 and DER length
937 # Input is a string of decimal numbers separated by '.' with at least
938 # two numbers required.
940 my @ids = split(/[.]/,$_[0]);
941 push(@ids, 0) while @ids < 2; # return something that's kind of valid
942 unshift(@ids, shift(@ids) * 40 + shift(@ids)); # combine first two
944 foreach my $num (@ids) {
945 $ans .= SingleOID
($num);
947 return pack('C',0x6).DERLength
(length($ans)).$ans;
952 my $t = shift; # a time() value
953 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($t);
958 if (1950 <= $year && $year < 2050) {
961 $tstr = sprintf("%02d%02d%02d%02d%02d%02dZ", $year % 100, $mon, $mday,
967 $tstr = sprintf("%04d%02d%02d%02d%02d%02dZ", $year, $mon, $mday,
970 return pack('C',$tag).DERLength
(length($tstr)).$tstr;
975 my $int = shift; # an integer value, may be negative
976 my @bytes = unpack('C*',pack('N',$int));
977 shift @bytes while @bytes >= 2 && $bytes[0] == 255 && ($bytes[1] & 0x80);
978 shift @bytes while @bytes >= 2 && $bytes[0] == 0 && !($bytes[1] & 0x80);
979 return pack('C*',0x02,scalar(@bytes),@bytes);
984 # return 20 random bytes except that the first byte has its high bit clear
985 my $suppress = shift || 0;
986 print STDERR
"Generating serial number, please wait...\n" unless $suppress;
987 my $randfile = "/dev/random";
988 $randfile = "/dev/urandom" if -e
"/dev/urandom";
989 open(RANDIN
, "<", $randfile)
990 or die "Cannot open $randfile for input: $!\n";
992 for (my $cnt = 0; $cnt < 20; ++$cnt) {
994 sysread(RANDIN
, $byte, 1)
995 or die "Cannot read from $randfile: $!\n";
997 my $val = unpack('C', $byte);
999 $byte = pack('C', $val);
1004 print STDERR
"...done creating serial number.\n" unless $suppress;
1008 sub ReadDERLength
($)
1010 # Input is a DER encoded length with possibly extra trailing bytes
1011 # Output is an array of length and bytes-used-for-encoded-length
1013 return undef unless length($der);
1014 my $byte = unpack('C',substr($der,0,1));
1015 return ($byte, 1) if $byte <= 127;
1016 return undef if $byte == 128 || $byte > 128+8; # Fail if greater than 2^64
1017 my $cnt = $byte & 0x7F;
1018 return undef unless length($der) >= $cnt+1; # Fail if not enough bytes
1020 for (my $i = 0; $i < $cnt; ++$i) {
1022 $val |= unpack('C',substr($der,$i+1,1));
1024 return ($val, $cnt+1);
1030 return undef unless length($der) >= 2;
1031 my $byte = unpack('C',substr($der,0,1));
1032 return undef unless $byte == 0x17 || $byte == 0x18;
1033 my ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1034 return undef unless length($der) == 1 + $lenbytes + $len;
1036 unless ($byte == 0x17 && $len == 13) || ($byte == 0x18 && $len == 15);
1037 substr($der,0,1+$lenbytes) = '';
1038 if ($byte == 0x17) {
1040 my $year = substr($der,0,2) + 1900;
1041 $year += 100 if $year < 1950;
1042 $der = sprintf("%04d",$year).substr($der,2);
1044 return substr($der,0,4).'-'.substr($der,4,2).'-'.substr($der,6,2).'_'.
1045 substr($der,8,2).':'.substr($der,10,2).':'.substr($der,12,3);
1048 sub GetOpenSSHKeyInfo
($)
1050 # Input is an OpenSSH public key in .pub format
1051 # Output is an array of:
1052 # how many bits in the modulus
1053 # the public exponent
1055 # the OpenSSH md5 fingerprint
1056 # the OpenSSH sha1 fingerprint
1057 # the OpenSSH comment (may be '')
1058 # the OpenSSH public key in OpenSSL PUBLIC KEY DER format
1059 # or undef if the key is unparseable
1060 # or just the key type if it's not ssh-rsa
1062 # Expected format is:
1063 # ssh-rsa BASE64PUBLICKEYDATA optional comment here
1064 # where the BASE64PUBLICKEYDATA when decoded produces:
1065 # 4 Byte Big-Endian length of Key type (must be 7 for RSA)
1066 # Key type WITHOUT terminating NUL (must be ssh-rsa for RSA)
1067 # 4 Byte Big-Endian length of public exponent
1068 # Public exponent integer bytes
1069 # 4 Byte Big-Endian length of modulus
1070 # Modulus integer bytes
1071 # no extra trailing bytes are permitted
1073 $input =~ s/((?:\r\n|\n|\r).*)$//os;
1074 my @fields = split(' ', $input, 3);
1075 return undef unless @fields >= 2;
1076 my $data = decode_base64
($fields[1]);
1077 my $origData = $data;
1079 while (length($data) >= 4) {
1080 my $len = unpack('N',substr($data,0,4));
1083 return undef if $len + 4 > length($data);
1084 $value = substr($data,4,$len);
1086 push(@parts, $value);
1087 substr($data, 0, 4+$len) = '';
1089 return undef unless length($data) == 0;
1091 if @parts >= 1 && defined($parts[0]) && $parts[0] && $parts[0] ne 'ssh-rsa';
1092 return undef unless @parts == 3;
1094 my $rsaEncryption = DEROID
('1.2.840.113549.1.1.1'); # :rsaEncryption
1095 $rsaEncryption = pack('C',0x30).DERLength
(length($rsaEncryption)+2)
1096 .$rsaEncryption.pack('C2',0x05,0x00);
1097 my $pubrsa = pack('C',0x2).DERLength
(length($parts[2])).$parts[2]; # modulus
1098 $pubrsa .= pack('C',0x2).DERLength
(length($parts[1])).$parts[1]; # exponent
1099 $pubrsa = pack('C',0x30).DERLength
(length($pubrsa)).$pubrsa;
1100 my $id = sha1
($pubrsa); # The id is the sha1 hash of the private key part
1101 $pubrsa = pack('C',0x3).DERLength
(length($pubrsa)+1).pack('C',0x0).$pubrsa;
1102 $pubrsa = $rsaEncryption.$pubrsa;
1103 $pubrsa = pack('C',0x30).DERLength
(length($pubrsa)).$pubrsa;
1105 my $bits = length($parts[2]) * 8;
1106 # But we have to discount any leading 0 bits in the first byte
1107 my $byte = unpack('C',substr($parts[2],0,1));
1112 return undef if $byte & 0x80; # negative modulus is not allowed
1113 while (!($byte & 0x80)) {
1119 my $rawexp = $parts[1];
1121 if (length($rawexp) > 8) {
1122 # Fudge the result because it's bigger than a 64-bit number
1123 my $lastbyte = unpack('C',substr($rawexp,-1,1));
1124 $exp = $lastbyte & 0x01 ?
65537 : 65536;
1128 while (length($rawexp)) {
1130 $exp |= unpack('C',substr($rawexp,0,1));
1131 substr($rawexp,0,1) = '';
1135 return ($bits,$exp,$id,md5
($origData),sha1
($origData),$fields[2]||'',$pubrsa);
1140 # Input is an RSA PRIVATE KEY in DER format
1141 # Output is an array of:
1142 # how many bits in the modulus
1143 # the public exponent
1145 # the OpenSSH md5 fingerprint
1146 # the OpenSSH sha1 fingerprint
1147 # or undef if the key is unparseable
1149 # Expected format is:
1152 # OBJECT IDENTIFIER :rsaEncryption = 1.2.840.113549.1.1.1
1155 # BIT STRING (primitive) {
1157 # SEQUENCE { # this part is the contents of an "RSA PUBLIC KEY" file
1159 # INTEGER publicExponent
1169 return undef if unpack('C',substr($der,0,1)) != 0x30;
1170 my ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1171 return undef unless length($der) == 1 + $lenbytes + $len;
1172 substr($der, 0, 1 + $lenbytes) = '';
1174 # the algorithm part always encodes as 30 0d 06092a864886f70d010101 0500
1176 unless substr($der, 0, 15) = pack('H*',"300d06092a864886f70d0101010500");
1177 substr($der, 0, 15) = '';
1179 return undef if unpack('C',substr($der,0,1)) != 0x03;
1180 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1181 return undef unless length($der) == 1 + $lenbytes + $len && $len >= 1;
1182 return undef unless unpack('C',substr($der, 1 + $lenbytes, 1)) == 0x00;
1183 substr($der, 0, 1 + $lenbytes + 1) = '';
1185 return undef if unpack('C',substr($der,0,1)) != 0x30;
1186 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1187 return undef unless length($der) == 1 + $lenbytes + $len;
1188 my $id = sha1
($der); # The id is the sha1 hash of the private key part
1189 substr($der, 0, 1 + $lenbytes) = '';
1191 return undef if unpack('C',substr($der,0,1)) != 0x02;
1192 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1193 substr($der, 0, 1 + $lenbytes) = '';
1194 my $derexp = substr($der, $len);
1195 substr($der, $len) = '';
1196 return undef unless $len >= 1;
1198 my $bits = length($der) * 8;
1199 # But we have to discount any leading 0 bits in the first byte
1200 my $byte = unpack('C',substr($der,0,1));
1205 return undef if $byte & 0x80; # negative modulus is not allowed
1206 while (!($byte & 0x80)) {
1213 return undef if unpack('C',substr($der,0,1)) != 0x02;
1214 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1215 substr($der, 0, 1 + $lenbytes) = '';
1216 return undef unless length($der) == $len && $len >= 1;
1217 return undef if unpack('C',substr($der,0,1)) & 0x80; # negative pub exp bad
1221 # Fudge the result because it's bigger than a 64-bit number
1222 my $lastbyte = unpack('C',substr($der,-1,1));
1223 $exp = $lastbyte & 0x01 ?
65537 : 65536;
1227 while (length($der)) {
1229 $exp |= unpack('C',substr($der,0,1));
1230 substr($der,0,1) = '';
1234 my $tohash = pack('N',7)."ssh-rsa".pack('N',length($rawexp)).$rawexp
1235 .pack('N',length($rawmod)).$rawmod;
1237 return ($bits,$exp,$id,md5
($tohash),sha1
($tohash));
1242 # Input is an X.509 "Certificate" (RFC 5280) in DER format
1243 # Output is an array of:
1244 # version (1, 2, or 3)
1245 # serial number (just the serial number data bytes, no header or length)
1246 # issuer name as a DER "Name"
1247 # validity start as a DER "Time"
1248 # validity end as a DER "Time"
1249 # subject name as a DER "Name"
1250 # subject public key as a DER "SubjectPublicKeyInfo"
1251 # subject public key id if v3 Extension SubjectKeyIdentifier is present
1252 # otherwise undef. This is just the raw bytes of the key id, no DER
1253 # header. (Same format as returned by GetKeyInfo and GetOpenSSHKeyInfo.)
1254 # or undef if the certificate is unparseable
1258 my $subjectKeyIdentifier = DEROID
('2.5.29.14');
1259 return undef if unpack('C',substr($der,0,1)) != 0x30;
1260 my ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1261 return undef unless length($der) == 1 + $lenbytes + $len;
1262 substr($der, 0, 1 + $lenbytes) = '';
1263 return undef if unpack('C',substr($der,0,1)) != 0x30;
1264 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1265 return undef unless length($der) >= 1 + $lenbytes + $len;
1266 substr($der, 0, 1 + $lenbytes) = '';
1267 substr($der, $len) = '';
1268 my $byte = unpack('C',substr($der,0,1));
1270 if ($byte == 0xA0) {
1271 return undef if length($der) < 5 || substr($der,1,3) != pack('H*','030201');
1272 $byte = unpack('C',substr($der,4,1));
1273 # Zero shouldn't be allowed as it's DEFAULT but we'll let it go by
1274 return undef if $byte > 2; # unrecognized version
1276 substr($der,0,5) = '';
1278 return undef if unpack('C',substr($der,0,1)) != 0x02;
1279 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1280 return undef unless length($der) > 1+$lenbytes+$len && $len >= 1;
1281 substr($der, 0, 1 + $lenbytes) = '';
1282 my $serial = substr($der, 0, $len);
1283 substr($der, 0, $len) = '';
1284 return undef if unpack('C',substr($der,0,1)) != 0x30; # Alg ID
1285 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1286 return undef unless length($der) > 1+$lenbytes+$len;
1287 substr($der,0,1+$lenbytes+$len) = '';
1288 return undef if unpack('C',substr($der,0,1)) != 0x30; # Issuer
1289 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1290 return undef unless length($der) > 1+$lenbytes+$len;
1291 my $issuer = substr($der, 0, 1 + $lenbytes + $len);
1292 substr($der,0,1+$lenbytes+$len) = '';
1293 return undef if unpack('C',substr($der,0,1)) != 0x30; # Validity
1294 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1295 return undef unless length($der) > 1+$lenbytes+$len;
1296 my $validlen = $len;
1297 substr($der, 0, 1 + $lenbytes) = '';
1298 $byte = unpack('C', substr($der, 0, 1));
1299 return undef unless $byte == 0x17 || $byte == 0x18;
1300 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1301 return undef unless length($der) > 1+$lenbytes+$len;
1302 my $vst = substr($der, 0, 1 + $lenbytes + $len);
1303 substr($der, 0, 1+$lenbytes+$len) = '';
1304 $byte = unpack('C', substr($der, 0, 1));
1305 return undef unless $byte == 0x17 || $byte == 0x18;
1306 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1307 return undef unless length($der) > 1+$lenbytes+$len;
1308 my $vnd = substr($der, 0, 1 + $lenbytes + $len);
1309 substr($der, 0, 1+$lenbytes+$len) = '';
1310 return undef unless $validlen == length($vst) + length($vnd);
1311 return undef if unpack('C',substr($der,0,1)) != 0x30; # Subject
1312 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1313 return undef unless length($der) > 1+$lenbytes+$len;
1314 my $subj = substr($der, 0, 1 + $lenbytes + $len);
1315 substr($der, 0, 1+$lenbytes+$len) = '';
1316 return undef if unpack('C',substr($der,0,1)) != 0x30; # Subject PubKey
1317 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1318 return undef unless length($der) >= 1+$lenbytes+$len;
1319 my $subjkey = substr($der, 0, 1 + $lenbytes + $len);
1320 substr($der, 0, 1+$lenbytes+$len) = '';
1321 return ($ver,$serial,$issuer,$vst,$vnd,$subj,$subjkey,undef)
1322 if !length($der) || $ver < 3;
1323 $byte = unpack('C',substr($der,0,1));
1324 if ($byte == 0x81) {
1325 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1326 return undef unless length($der) >= 1+$lenbytes+$len;
1327 substr($der,0,1+$lenbytes+$len) = '';
1328 $byte = unpack('C',substr($der,0,1));
1330 if ($byte == 0x82) {
1331 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1332 return undef unless length($der) >= 1+$lenbytes+$len;
1333 substr($der,0,1+$lenbytes+$len) = '';
1334 $byte = unpack('C',substr($der,0,1));
1336 return undef if length($der) && $byte != 0xA3; # exts tag
1337 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1338 return undef unless length($der) == 1+$lenbytes+$len;
1340 substr($der, 0, 1+$lenbytes) = '';
1341 return undef unless unpack('C',substr($der,0,1)) == 0x30; # Extensions
1342 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1343 return undef unless length($der) == 1+$lenbytes+$len;
1344 substr($der, 0, 1+$lenbytes) = '';
1345 while (length($der)) {
1346 return undef unless unpack('C',substr($der,0,1)) == 0x30;
1347 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1348 return undef unless length($der) >= 1+$lenbytes+$len;
1349 substr($der,0,1+$lenbytes) = '';
1350 return undef unless unpack('C',substr($der,0,1)) == 0x06;
1351 if (substr($der,0,length($subjectKeyIdentifier)) ne $subjectKeyIdentifier) {
1352 substr($der,0,$len) = '';
1355 substr($der,0,length($subjectKeyIdentifier)) = '';
1356 if (unpack('C',substr($der,0,1)) == 0x01) {
1357 # SHOULDn't really be here, but allow it anyway
1358 return undef unless unpack('C',substr($der,1,1)) == 0x01;
1359 substr($der,0,3) = '';
1361 return undef unless unpack('C',substr($der,0,1)) == 0x04;
1362 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1363 return undef unless length($der) >= 1+$lenbytes+$len && $len > 1;
1364 substr($der,0,1+$lenbytes) = '';
1365 return undef unless unpack('C',substr($der,0,1)) == 0x04;
1366 ($len, $lenbytes) = ReadDERLength
(substr($der,1));
1367 return undef unless length($der) >= 1+$lenbytes+$len && $len >= 1;
1368 $skid = substr($der,1+$lenbytes,$len);
1371 return ($ver,$serial,$issuer,$vst,$vnd,$subj,$subjkey,$skid)
1376 my ($line,$width) = @_;
1378 return $line if $width < 1;
1379 while (length($line) > $width) {
1380 push(@ans, substr($line, 0, $width));
1381 substr($line, 0, $width) = '';
1383 push(@ans, $line) if length($line);
1392 local(*CHLD_OUT
, *CHLD_IN
);
1393 #open(my $olderr, ">&STDERR") or die "Cannot dup STDERR: $!\n";
1394 #open(STDERR, '>', "/dev/null") or die "Cannot redirect STDERR: $!";
1395 (my $pid = open2
(\
*CHLD_OUT
, \
*CHLD_IN
, "openssl", "dgst", "-whirlpool",
1397 or die "Cannot start openssl dgst\n";
1398 print CHLD_IN
$data;
1401 die "Error reading whirlpool digest from openssl dgst\n"
1402 unless !!($hash = <CHLD_OUT
>);
1405 #open(STDERR, ">&", $olderr) or die "Cannot dup \$olderr: $!";
1413 my $sha1 = DEROID
('1.3.14.3.2.26');
1414 my $sha224 = DEROID
('2.16.840.1.101.3.4.2.4');
1415 my $sha256 = DEROID
('2.16.840.1.101.3.4.2.1');
1416 my $sha384 = DEROID
('2.16.840.1.101.3.4.2.2');
1417 my $sha512 = DEROID
('2.16.840.1.101.3.4.2.3');
1418 my $whirlpoolAlgorithm = DEROID
('1.0.10118.3.0.55');
1419 my $sha1WithRSAEncryption = DEROID
('1.2.840.113549.1.1.5');
1420 my $sha224WithRSAEncryption = DEROID
('1.2.840.113549.1.1.14');
1421 my $sha256WithRSAEncryption = DEROID
('1.2.840.113549.1.1.11');
1422 my $sha384WithRSAEncryption = DEROID
('1.2.840.113549.1.1.12');
1423 my $sha512WithRSAEncryption = DEROID
('1.2.840.113549.1.1.13');
1424 my $whirlpoolWithRSAEncryption = DEROID
('1.2.840.113549.1.1.15');
1425 return ($sha1, $sha1WithRSAEncryption, \
&sha1
) if $dgst eq 'sha1';
1430 $h=$sha224,$oid=$sha224WithRSAEncryption,$func=\
&sha224
,last
1431 if $dgst eq 'sha224';
1432 $h=$sha256,$oid=$sha256WithRSAEncryption,$func=\
&sha256
,last
1433 if $dgst eq 'sha256';
1434 $h=$sha384,$oid=$sha384WithRSAEncryption,$func=\
&sha384
,last
1435 if $dgst eq 'sha384';
1436 $h=$sha512,$oid=$sha512WithRSAEncryption,$func=\
&sha512
,last
1437 if $dgst eq 'sha512';
1438 $h=$whirlpoolAlgorithm,$oid=$whirlpoolWithRSAEncryption,
1439 $func=\
&whirlpool
,last if $dgst eq 'whirlpool';
1442 die "Invalid digest ($dgst) must be one of:\n"
1443 . " sha1 sha224 sha256 sha384 sha512\n" unless $h && $oid;
1444 die "Digest $dgst requires Digest::SHA or Digest::SHA::PurePerl "
1445 . "to be available\n" if !$hasSha2;
1446 return ($h,$oid,$func);
1449 sub GetDigestStrength
($)
1451 return 80 if $_[0] eq 'sha1';
1452 return 112 if $_[0] eq 'sha224';
1453 return 128 if $_[0] eq 'sha256';
1454 return 192 if $_[0] eq 'sha384';
1455 return 256 if $_[0] eq 'sha512';
1456 return 256 if $_[0] eq 'whirlpool';
1459 sub GetDigestNameForBits
($)
1461 return 'sha1' if $_[0] <= 80;
1462 return 'sha224' if $_[0] <= 112;
1463 return 'sha256' if $_[0] <= 128;
1464 return 'sha384' if $_[0] <= 192;
1471 $str =~ tr/a-z/A-Z/;
1478 $str =~ tr/A-Z/a-z/;
1484 my ($data, $keyfile) = @_;
1487 local(*CHLD_OUT
, *CHLD_IN
);
1488 #open(my $olderr, ">&STDERR") or die "Cannot dup STDERR: $!\n";
1489 #open(STDERR, '>', "/dev/null") or die "Cannot redirect STDERR: $!";
1490 (my $pid = open2
(\
*CHLD_OUT
, \
*CHLD_IN
, "openssl", "rsautl", "-sign",
1491 "-inkey", $keyfile))
1492 or die "Cannot start openssl rsautl\n";
1493 print CHLD_IN
$data;
1496 die "Error reading RSA signature from openssl rsautl\n"
1497 unless !!($sig = <CHLD_OUT
>);
1500 #open(STDERR, ">&", $olderr) or die "Cannot dup \$olderr: $!";
1505 my %rsadsa_known_strengths;
1507 %rsadsa_known_strengths = (
1516 sub compute_rsa_strength
($)
1518 my $rsadsabits = shift;
1519 return 0 unless $rsadsabits && $rsadsabits > 0;
1520 return ($rsadsa_known_strengths{$rsadsabits},'')
1521 if $rsadsa_known_strengths{$rsadsabits};
1523 if ($rsadsabits < 1024) {
1524 $guess = 80 * sqrt($rsadsabits/1024);
1525 } elsif ($rsadsabits > 15360) {
1526 $guess = 256 * sqrt($rsadsabits/15360);
1528 $guess = 34.141 + sqrt(34.141*34.141 - 4*0.344*(1554.7-$rsadsabits));
1529 $guess = $guess / (2 * 0.344);
1531 $guess = 79 if $rsadsabits < 1024 && $guess >= 80;
1532 $guess = 80 if $rsadsabits > 1024 && $guess < 80;
1533 $guess = 111 if $rsadsabits > 1024 && $rsadsabits < 2048 && $guess >= 112;
1534 $guess = 112 if $rsadsabits > 2048 && $guess < 112;
1535 $guess = 127 if $rsadsabits > 2048 && $rsadsabits < 3072 && $guess >= 128;
1536 $guess = 128 if $rsadsabits > 3072 && $guess < 128;
1537 $guess = 191 if $rsadsabits > 3072 && $rsadsabits < 7680 && $guess >= 192;
1538 $guess = 192 if $rsadsabits > 7680 && $guess < 192;
1539 $guess = 255 if $rsadsabits > 7680 && $rsadsabits < 15360 && $guess >= 256;
1540 $guess = 256 if $rsadsabits > 15360 && $guess < 256;
1541 return (int($guess),1);
1546 my $octet = '(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])';
1547 return $_[0] =~ /^$octet\.$octet\.$octet\.$octet$/o;
1550 # 1-8 groups of 1-4 hex digits separated by ':' except that the groups may be
1551 # divided into two and separated by '::' instead and finally the last two
1552 # groups may be specified using IPv4 notation. No scope allowed.
1556 return undef unless $a =~ /^[:0-9a-fA-F.]+$/;
1560 if ($a =~ /^(.*)::(.*)$/) {
1561 @group1 = split(/:/, $1) if $1;
1562 @group2 = split(/:/, $2) if $2;
1565 @group2 = split(/:/, $a);
1567 if (@group2 && is_ipv4
($group2[@group2 - 1])) {
1568 my @ipv4 = split(/\./, pop(@group2));
1569 push(@group2, sprintf("%x", ($ipv4[0] << 8) | $ipv4[1]));
1570 push(@group2, sprintf("%x", ($ipv4[2] << 8) | $ipv4[3]));
1572 return undef unless @group1 + @group2 >= 1 && @group1 + @group2 <= 8;
1573 return undef if $two && @group1 + @group2 >= 8;
1575 my $zcomps = 8 - (@group1 + @group2);
1576 for (my $i=0; $i < $zcomps; ++$i) {
1581 foreach my $comp (@group1,@group2) {
1582 return undef unless $comp =~ /^[0-9a-fA-F]{1,4}$/;
1583 $ans .= pack('n', hex($comp));
1592 return pack('CCCC', split(/\./, $a, 4));
1594 return parseipv6
($a);
1599 # RFC 1034 section 3.5
1600 # RFC 1123 section 2.1
1601 # RFC 1738 section 3.1
1602 # RFC 3986 section 3.2.2
1606 defined($dns) or $dns = '';
1607 return 0 if $dns eq '' || $dns =~ /\s/;
1608 my @labels = split(/\./, $dns, -1);
1611 foreach my $label (@labels) {
1613 return 0 unless length($label) > 0 && length($label) <= 63;
1614 return 0 unless $label =~ /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/ ||
1615 ($i == 0 && $label eq '*' && @labels > 1);
1617 return 0 unless length($dns) <= 255;
1621 sub handle_dns_opt
($$)
1624 my $altsref = shift;
1625 my $ip = parseip
($val);
1627 die "Internal error: parsed IP not 4 or 16 bytes long"
1628 unless length($ip) == 4 || length($ip) == 16;
1629 push(@
$altsref, [0x87, $ip]);
1632 die "Not a valid dns name or IPv4/IPv6 address: $val\n"
1633 unless is_dns_valid
($val);
1634 push(@
$altsref, [0x82, $val]);
1641 return 0 unless $oid =~ /^\d+(?:\.\d+)*$/os;
1642 my @ids = split(/[.]/, $oid);
1643 return 0 unless @ids >= 2;
1644 return 0 unless $ids[0] <= 2;
1645 return 0 if $ids[0] < 2 && $ids[1] >= 40;
1649 sub is_email_valid
($;$$)
1651 my ($val, $emailatok, $emailatseen) = @_;
1654 return 0 if !$emailatok || $$emailatseen;
1658 return 0 unless $val =~ /^([^@\s]+)\@([A-Za-z0-9.-]+)$/;
1659 my ($local,$host) = ($1,$2);
1660 return 0 unless is_dns_valid
($host);
1661 return 0 unless $local =~ /^[[:print:]]+$/os;
1669 sub validate_oid_value
($$$;$$)
1674 my ($oid, $value, $key, $emailatok, $emailatseen) = @_;
1675 if (defined($oidstringlengths{$oid})) {
1676 my $len = $oidstringlengths{$oid};
1677 if ($len =~ /^\d+$/) {
1678 if (length($value) != $len) {
1679 warn "--dni key type '$key' requires exactly $len characters\n";
1682 } elsif ($len =~ /^,(\d+)$/) {
1684 if (length($value) > $max) {
1685 warn "--dni key type '$key' requires no more than $max characters\n"
1687 return 0 unless $nomaxlen;
1689 } elsif ($len =~ /^(\d+),$/) {
1691 if (length($value) < $min) {
1692 warn "--dni key type '$key' requires at least $min characters\n";
1695 } elsif ($len =~ /^(\d+),(\d+)$/) {
1696 my ($min,$max) = ($1, $2);
1697 if (length($value) < $min || length($value) > $max) {
1698 warn "--dni key type '$key' requires $min-$max characters\n"
1699 unless $quiet && length($value) >= $min;
1700 return 0 unless $nomaxlen && length($value) >= $min;
1703 die "Bad value \"$len\" in \%oidstringlengths for '$key'\n";
1706 warn("--dni key values may not be empty (key '$key')\n"), return 0
1707 unless length($value);
1708 if (defined($oidstringtypes{$oid})) {
1709 my $st = $oidstringtypes{$oid};
1710 die "Invalid \%oidstringtype value '$st' for $oid\n"
1711 unless $st eq 'u' || $st eq 'p' || $st eq 'i';
1712 warn("--dni key type '$key' requires a PrintableString (must match ".
1713 "[A-Za-z0-9 '()+,./:=?-]+)\n"), return 0
1714 if $st eq 'p' && $value !~ m
|^[A
-Za
-z0
-9 '()+,./:=?-]+$|os &&
1715 ($oid ne '2.5.4.5' || !$useRandom || $value ne '#');
1716 warn("--dni key type '$key' requires an IA5String (must match ".
1717 "[\x00-\x7F]+)\n"), return 0
1718 if $st eq 'i' && $value !~ m
|^[\x00-\x7F]+$|os
;
1719 warn("--dni key type '$key' requires a UTF8String\n"), return 0
1720 if $st eq 'u' && !IsUTF8
(MakeUTF8
($value));
1722 if (defined($oidstringrestrictions{$oid})) {
1723 my $r = $oidstringrestrictions{$oid};
1724 die "Invalid \%oidstringrestrictions value '$r' for $oid\n"
1725 unless $r eq 'c' || $r eq 'd' || $r eq 'e';
1726 warn("--dni key type '$key' requires 2 A-Z characters\n"), return 0
1727 if $r eq 'c' && $value !~ m
|^[A
-Z
]{2}$|;
1728 warn("--dni key type '$key' requires 1-63 character dns label ".
1729 "(letdig(letdighyp*)letdig*)\n"), return 0
1730 if $r eq 'd' && (length($value) > 63 ||
1731 $value !~ m
|^[A
-Za
-z0
-9](?
:[A
-Za
-z0
-9-]*[A
-Za
-z0
-9])?
$|);
1732 warn("--dni key type '$key' requires an email address (local\@host)\n"),
1733 return 0 if $r eq 'e' && !is_email_valid
($_[1], $emailatok, $emailatseen);
1738 sub get_oid_string_type
($$)
1740 my ($oid, $value) = @_;
1741 if (defined($oidstringtypes{$oid})) {
1742 my $st = $oidstringtypes{$oid};
1743 return 12 if $st eq 'u';
1744 return 19 if $st eq 'p';
1745 return 22 if $st eq 'i';
1746 die "Invalid \%oidstringtype value '$st' for $oid\n";
1748 return 19 if $value =~ m
|^[A
-Za
-z0
-9 '()+,./:=?-]*$|os;
1752 sub handle_dni_opt($$$$$)
1755 my $listref = shift;
1756 my $stdinokref = shift;
1757 my $emailatok = shift;
1758 my $emailatseen = shift;
1760 if ($$stdinokref && $$stdinokref > 2) {
1761 $dor = sub {warn "@_"; return 0}
1763 $dor = sub {die "@_"}
1767 if ($opt =~ /^\@(.*)$/os) {
1769 if ($$stdinokref && $$stdinokref > 2) {
1770 warn "May not use \@filename syntax within a --dni \@filename file\n";
1773 die "May not use --dni \@- if stdin is being used for a public key\n"
1774 if $fn eq '-' && !$$stdinokref;
1775 die "May not use --dni \@- more than once\n"
1776 if $fn eq '-' && $$stdinokref && $$stdinokref > 1;
1777 ++$$stdinokref if $fn eq '-';
1782 $infilename = "\"$fn\"";
1783 open($input, '<', $fn)
1784 or die "Cannot open $infilename for input: $!\n";
1787 $infilename = 'standard input
';
1790 while (my $line = <$input>) {
1792 $line =~ s/(?:\r|\r\n|\n)$//os;
1793 next if $line =~ /^\s*$/os || $line =~ /^\s*#/os;
1794 &handle_dni_opt($line, $listref, \$stdinnotok, $emailatok, $emailatseen)
1795 or die "$infilename:$lineno: error: --dni \@filename syntax error\n";
1797 close($input) if $fn ne '-';
1800 return &$dor("Bad --dni key=value option: $opt\n")
1801 unless $opt =~ /^([+]?)\s*([^+=\s]+)\s*=\s*(.+)$/;
1802 my ($mv,$key,$value) = ($1,$2,$3);
1804 if ($key =~ /^[\d.]+$/) {
1805 return &$dor("Bad --dni option invalid oid key: $key\n")
1806 unless is_oid_valid($key);
1807 warn "*** Warning: The OID $key is not recognized, value will be encoded ".
1808 "as a PrintableString or UTF8String type\n" unless $knownoids{$key};
1811 return &$dor("Bad --dni option unrecognized key name ".
1812 "(use --list-oid-names): $key\n") unless $oidnames{lc($key)};
1813 $oid = $oidnames{lc($key)};
1815 return &$dor("Bad --dni option '+key
=value
' requires previous non-'+': $opt\n")
1816 if $mv && !@$listref;
1817 return &$dor("Bad --dni option invalid value for key: $opt\n")
1818 unless validate_oid_value($oid, $value, $key, $emailatok, $emailatseen);
1819 push(@$listref, []) unless $mv;
1820 push(@{$$listref[$#$listref]}, [$oid, $value]);
1826 Make1252(); # Set up the UTF-8 auxiliary conversion table
1836 our $useRandom = '';
1837 my $useNoRandom = '';
1840 my @serverAltNames = ();
1842 my $applecodesign = '';
1849 my $digest = $hasSha2 ? 'sha256
' : 'sha1
';
1850 my $digestChoice = '';
1855 my $commonNameOID = '2.5.4.3'; # :commonName
1856 my $serialNumber = DEROID('2.5.4.5'); # :serialNumber
1857 my $userIdOID = '0.9.2342.19200300.100.1.1'; # :userId
1858 my $emailAddressOID = '1.2.840.113549.1.9.1'; # :emailAddress
1859 my $dnQualifier = DEROID('2.5.4.46'); # :dnQualifier
1860 my $basicConstraints = DEROID('2.5.29.19');
1861 my $keyUsage = DEROID('2.5.29.15');
1862 my $extKeyUsage = DEROID('2.5.29.37');
1863 my $serverAuth = DEROID('1.3.6.1.5.5.7.3.1');
1864 my $clientAuth = DEROID('1.3.6.1.5.5.7.3.2');
1865 my $codeSigning = DEROID('1.3.6.1.5.5.7.3.3');
1866 my $emailProtection = DEROID('1.3.6.1.5.5.7.3.4');
1867 my $appleCodeSigning = DEROID('1.2.840.113635.100.4.1');
1868 my $authKeyId = DEROID('2.5.29.35');
1869 my $subjKeyId = DEROID('2.5.29.14');
1870 my $subjAltName = DEROID('2.5.29.17');
1871 my $boolTRUE = pack('C
*',0x01,0x01,0xFF);
1872 my $boolFALSE = pack('C
*',0x01,0x01,0x00);
1873 my $v3Begin = pack('C
',0x17).DERLength(13)."970811000000Z";
1874 my $noExpiry = pack('C
',0x18).DERLength(15)."99991231235959Z";
1877 my @suffixfiles = ();
1879 my $qualifier = undef;
1883 "help|h" => sub{$help=1;die"!FINISH"},
1884 "verbose|v" => \$verbose,
1885 "man" => sub{$verbose=1;$help=1;die"!FINISH"},
1886 "version|V" => sub{print $VERSIONMSG;exit(0)},
1887 "list-oid-names" => sub{print $oidnamelist;exit(0)},
1890 "pubx509" => \$pubx509,
1891 "pubX509" => \$pubx509,
1895 "random" => \$useRandom,
1896 "no-random" => \$useNoRandom,
1897 "no-max-len" => \$nomaxlen,
1899 "server" => \$server,
1900 "codesign" => \$codesign,
1901 "applecodesign" => \$applecodesign,
1903 "client" => \$client,
1906 "rootauth" => \$rootauth,
1907 "authext" => \$authext,
1908 "digest=s" => \$digestChoice,
1909 "key|k=s" => \$keyfile,
1910 "cert|c=s" => \$certfile,
1911 "pathlen=i" => \$pathlen,
1913 "out=s" => \$outfile,
1914 "suffix=s" => sub{push(@suffixfiles, $_[1])},
1915 "dnq=s" => \$qualifier,
1916 "dns=s" => sub{handle_dns_opt($_[1], \@serverAltNames)},
1917 "dni=s" => sub{push(@dnilist, $_[1])}
1922 my $pager = $ENV{'PAGER
'} || 'less
';
1923 if (-t STDOUT && open(MAN, "|-", $pager)) {
1924 print MAN formatman($HELP,1);
1928 print formatman($HELP);
1932 die "--acme and --check are not compatible\n" if $acme && $check;
1933 die("May not combine --random and --no-random\n", $USAGE)
1934 if $root && $useRandom && $useNoRandom;
1935 $useRandom = 1 if $root && !$useNoRandom;
1944 @serverAltNames = ();
1945 $client = $subca = $server = $codesign = $applecodesign = $email = '';
1948 [[$oidnames{'o
'}, 'Acme Products Corporation
']],
1949 [[$oidnames{'ou
'}, 'Internet Services Division
']],
1950 [[$oidnames{'ou
'}, 'Acme Certificate Co
.']],
1951 [[$oidnames{'ou
'}, 'Certificate Services
']],
1952 [[$oidnames{'ou
'}, 'Root Certificate Production
']],
1953 [[$oidnames{'serial
'}, '#']],
1954 [[$oidnames{'cn'}, 'Acme Root Certificate']]
1957 die "--in requires a filename\n" if !$root && !$infile;
1958 die "--out requires a filename\n" if !$outfile;
1959 foreach my $suffixfile (@suffixfiles) {
1960 die "--suffix requires a filename\n" if defined($suffixfile) && !$suffixfile;
1961 die "--suffix file '$suffixfile' does not exist or is not readable\n"
1962 if ! -e
$suffixfile || ! -r
$suffixfile;
1965 !$root && !$subca && !$server && !$codesign && !$applecodesign && !$email;
1966 my $dnistdinok = $root || $infile ne '-';
1967 my ($emailatok, $emailatseen);
1968 if (!$client && $email && defined($ARGV[0]) && $ARGV[0] ne "") {
1969 $emailatok = $ARGV[0];
1972 foreach my $dnitem (@dnilist) {
1973 handle_dni_opt
($dnitem, \
@dnseq, \
$dnistdinok, $emailatok, \
$emailatseen);
1978 RDN
: foreach my $rdn (@dnseq) {
1980 ++$stuffcount if ${$$rdn[0]}[0] eq '2.5.4.5' && ${$$rdn[0]}[1] eq '#';
1982 foreach my $mv (@
$rdn) {
1983 $seenbad = 1, last RDN
if $$mv[0] eq '2.5.4.5' && $$mv[1] eq '#';
1987 die "--dni serialNumber=# can only be used at most once and only ".
1988 "non-multivalued\n" if $seenbad || $stuffcount > 1;
1990 my %seensingletons = ();
1991 foreach my $rdn (@dnseq) {
1992 $seensingletons{${$$rdn[0]}[0]} = 1 if @
$rdn == 1;
1994 $verbose = 1 if $debug || $check;
1995 $quiet = 0 if $verbose || $check;
1996 print STDERR
$VERSIONMSG if $verbose;
1997 my $keytype = 'OpenSSH';
1999 $keytype = 'pubx509', $n = '' if $pubx509;
2000 die("Missing required --key option\n", $USAGE) if !$keyfile;
2001 die("Missing required --cert option\n", $USAGE) if !$root && !$certfile;
2002 die("Must have exactly one \"name string\" argument\n", $USAGE)
2003 if !$check && !$acme && @ARGV != 1;
2004 die "Standard input is a tty (which is an unlikely source of a$n $keytype "
2006 . "If that's what you truly meant, add the -t option to allow it.\n"
2007 if !$root && $infile eq '-' && -t STDIN
&& !$termOK;
2008 my $emptynameok = $check || ($root && $useRandom);
2009 if (!$emptynameok && $ARGV[0] eq '') {
2011 # Okay to be empty if we've seen a user id singleton
2012 $emptynameok = 1 if $seensingletons{$userIdOID};
2014 # Okay to be empty if we've seen a common name singleton
2015 $emptynameok = 1 if $seensingletons{$commonNameOID};
2017 die "\"name string\" may not be empty\n" unless $emptynameok;
2019 die "Distinguished name qualifier may not be empty string\n"
2020 unless !defined($qualifier) || $qualifier ne '';
2021 die "Invalid distinguished name qualifier (must match [A-Za-z0-9 '()+,./:=?-]+)\n"
2022 unless !$qualifier || $qualifier =~ m
|^[A
-Za
-z0
-9 '()+,./:=?-]+$|;
2023 if (!$check && @ARGV && $ARGV[0] ne '') {
2029 $oid = $emailAddressOID;
2030 $key = 'emailAddress
';
2032 $oid = $commonNameOID;
2035 die "Bad \"name string\" value\n"
2036 unless $emailatseen || validate_oid_value($oid, $ARGV[0], $key);
2037 push(@dnseq, [[$oid, $ARGV[0]]]) unless $emailatseen;
2041 foreach my $suffixfile (@suffixfiles) {
2042 open(SUFFIX, '<', $suffixfile)
2043 or die "Cannot open '$suffixfile' for input: $!\n";
2045 $suffix .= <SUFFIX>;
2049 local $/ if $pubx509;
2051 if ($infile ne '-') {
2052 $infilename = "\"$infile\"";
2053 open($input, '<', $infile)
2054 or die "Cannot open $infilename for input: $!\n";
2057 $infilename = 'standard input
';
2059 !!($opensshdotpub = <$input>)
2060 or die "Cannot read $keytype public key from $infilename\n";
2063 if ($opensshdotpub =~ /^----[- ]BEGIN PUBLIC KEY[- ]----/) {
2067 my $input = $opensshdotpub;
2068 $input =~ s/((?:\r\n|\n|\r).*)$//os;
2069 my @fields = split(' ', $input, 3);
2071 length($fields[1]) < 16 ||
2072 $fields[1] !~ m|^[0-9A-Za-z+/=]+$|) {
2078 $keytype = 'pubx509
';
2079 print STDERR "auto detected --pubx509 option\n" if $debug;
2081 my $extra = <$input>;
2082 $opensshdotpub .= $extra if $extra;
2085 close($input) if $infile ne '-';
2087 die "Cannot read key file $keyfile\n" if ! -r $keyfile;
2088 die "Cannot read certificate file $certfile\n" if !$root && ! -r $certfile;
2090 my ($sshkeybits,$sshkeyexp,$sshkeyid,$sfmd5,$sfsha1,$sshcmnt,$opensshpub);
2092 # need to set $sshkeyid to $pubkeyid
2093 # need to set $opensshpub to $pubkey
2094 # but don't have either yet
, so
do it later
2097 local (*READKEY
, *WRITEKEY
);
2098 my $inform = $opensshdotpub =~ m
|^[\t\n\r\x20-\x7E]*$|os ?
'PEM' : 'DER';
2099 print STDERR
"pubx509 -inform $inform\n" if $debug;
2100 open(my $olderr, ">&STDERR") or die "Cannot dup STDERR: $!\n";
2101 open(STDERR
, '>', "/dev/null") or die "Cannot redirect STDERR: $!";
2102 my $pid = open2
(\
*READKEY
, \
*WRITEKEY
, "openssl", "rsa", "-inform",
2103 $inform, "-pubin", "-outform", "DER", "-pubout");
2104 open(STDERR
, ">&", $olderr) or die "Cannot dup \$olderr: $!";
2105 $pid or die "Cannot start openssl rsa\n";
2106 print WRITEKEY
$opensshdotpub;
2109 die "Error reading X.509 format RSA public key from $infilename\n"
2110 unless !!($opensshpub = <READKEY
>);
2114 ($sshkeybits,$sshkeyexp,$sshkeyid,$sfmd5,$sfsha1) = GetKeyInfo
($opensshpub);
2115 die "Unparseable X.509 public key format read from $infilename\n"
2119 ($sshkeybits,$sshkeyexp,$sshkeyid,$sfmd5,$sfsha1,$sshcmnt,$opensshpub) =
2120 GetOpenSSHKeyInfo
($opensshdotpub);
2121 die "Unparseable OpenSSH public key read from $infilename\n"
2123 die "Unsupported OpenSSH public key type ($sshkeybits), must be ssh-rsa\n"
2129 ($sshkeystrength, $sshkeyapprox) = compute_rsa_strength
($sshkeybits);
2130 printf(STDERR
"$keytype Public Key Info:\n".
2131 " bits=$sshkeybits pubexp=$sshkeyexp secstrenth=%s%s\n",
2132 $sshkeystrength, ($sshkeyapprox ?
' (approximately)' : '')) if $verbose;
2133 print STDERR
" keyid=",
2134 join(":", toupper
(unpack("H*",$sshkeyid))=~/../g), "\n" if $verbose;
2135 print STDERR
" fingerprint(md5)=",
2136 join(":", tolower
(unpack("H*",$sfmd5))=~/../g), "\n" if $verbose;
2137 print STDERR
" fingerprint(sha1)=",
2138 join(":", tolower
(unpack("H*",$sfsha1))=~/../g), "\n" if $verbose;
2139 print STDERR
" comment=",$sshcmnt||'<none present>',"\n"
2140 if $verbose && !$pubx509;
2141 die "*** Error: $keytype key has less than 512 bits ($sshkeybits)\n"
2142 . "*** You might as well just donate your system to hackers now.\n"
2143 if $sshkeybits < 512;
2144 die "*** Error: The $keytype key's public exponent is even ($sshkeyexp)!\n"
2145 if !($sshkeyexp & 0x01);
2146 warn "*** Warning: The $keytype key has less than 2048 bits ($sshkeybits), "
2147 . "continuing anyway\n" if !$quiet && $sshkeybits < 2048;
2148 die "*** Error: The $keytype public key's exponent of $sshkeyexp is "
2149 . "unacceptably weak!\n" if $sshkeyexp < 35; # OpenSSH used 35 until v5.4
2150 warn "*** Warning: The $keytype public key's exponent ($sshkeyexp) is weak "
2151 . "(< 65537), continuing anyway\n" if !$quiet && $sshkeyexp < 65537;
2154 my $inform = -T
$keyfile ?
'PEM' : 'DER';
2155 print STDERR
"keyfile -inform $inform\n" if $debug;
2156 die "Input key does not appear to be in PEM format: $keyfile\n"
2157 unless $inform eq 'PEM';
2161 open(my $olderr, ">&STDERR") or die "Cannot dup STDERR: $!\n";
2162 open(STDERR
, '>', "/dev/null") or die "Cannot redirect STDERR: $!";
2163 open(READKEY
, "-|", "openssl", "rsa", "-inform", $inform, "-outform", "DER",
2164 "-pubout", "-passin", "pass:", "-in", $keyfile)
2165 or die "Cannot read RSA private key in \"$keyfile\": $!\n";
2166 open(STDERR
, ">&", $olderr) or die "Cannot dup \$olderr: $!";
2168 die "Error reading RSA private key in \"$keyfile\"\n"
2169 unless !!($pubkey = <READKEY
>);
2172 $opensshpub = $pubkey if $root;
2173 my ($pubkeybits,$pubkeyexp,$pubkeyid,$pfmd5,$pfsha1) = GetKeyInfo
($pubkey);
2174 $sshkeyid = $pubkeyid if $root;
2175 die "Unparseable public key format in \"$keyfile\"\n" unless $pubkeybits;
2176 my ($pubkeystrength, $pubkeyapprox) = compute_rsa_strength
($pubkeybits);
2177 printf(STDERR
"RSA Private Key $keyfile:\n".
2178 " bits=$pubkeybits pubexp=$pubkeyexp secstrength=%s%s\n",
2179 $pubkeystrength, ($pubkeyapprox?
' (approximately)':'')) if $verbose;
2180 print STDERR
" keyid=",
2181 join(":", toupper
(unpack("H*",$pubkeyid))=~/../g), "\n" if $verbose;
2182 print STDERR
" fingerprint(md5)=",
2183 join(":", tolower
(unpack("H*",$pfmd5))=~/../g), "\n" if $verbose;
2184 print STDERR
" fingerprint(sha1)=",
2185 join(":", tolower
(unpack("H*",$pfsha1))=~/../g), "\n" if $verbose;
2186 die "*** Error: Private key has less than 512 bits ($pubkeybits)\n"
2187 . "*** You might as well just donate your system to hackers now.\n"
2188 if $pubkeybits < 512;
2189 die "*** Error: The private key's public exponent is even ($pubkeyexp)!\n"
2190 if !($pubkeyexp & 0x01);
2191 warn "*** Warning: The private key has less than 2048 bits ($pubkeybits), "
2192 . "continuing anyway\n" if !$quiet && $pubkeybits < 2048;
2193 die "*** Error: The private key's public key exponent of $pubkeyexp is "
2194 . "unacceptably weak!\n" if $pubkeyexp < 35; # ssh-keygen used 35 'til v5.4
2195 warn "*** Warning: The private key's public exponent ($pubkeyexp) is weak "
2196 . "(< 65537), continuing anyway\n" if !$quiet && $pubkeyexp < 65537;
2198 my $maxkeystrength = $pubkeystrength;
2199 $maxkeystrength = $sshkeystrength
2200 if $sshkeystrength && $sshkeystrength > $maxkeystrength;
2201 my $digeststrength = GetDigestStrength
($digestChoice || $digest);
2202 my $digestsuggest = GetDigestNameForBits
($maxkeystrength);
2203 my $digestsuggestbits = GetDigestStrength
($digestsuggest);
2204 # Never warn or auto-choose if both keys are <= 1024 bits in length
2205 if ($maxkeystrength > 80) {
2206 if (!$digestChoice) {
2207 if (!$hasSha2 && $digestsuggestbits > $digeststrength) {
2208 warn "*** Warning: automatic digest selection $digestsuggest ".
2209 "support not available\n" unless $quiet;
2211 $digest = $digestsuggest if $digestsuggestbits > $digeststrength;
2215 my ($did, $dalg, $dfunc) = GetDigest
($digestChoice || $digest);
2216 print STDERR
"default digest: $digest\n" if $debug;
2217 if ($digestChoice && $digestsuggestbits > $digeststrength) {
2218 warn "*** Warning: $digestsuggest (or stronger) is recommended for ".
2219 "security strength $maxkeystrength keys, continuing anyway\n"
2222 warn "*** Warning: defaulting to sha1 since SHA-2 support not available\n"
2223 if !$quiet && $digest eq 'sha1' && !$digestChoice;
2224 $digest = $digestChoice if $digestChoice;
2225 warn "*** Warning: sha1 use is strongly discouraged, continuing anyway\n"
2226 if !$quiet && $digest eq 'sha1';
2227 warn <<EOT if !$quiet && $digest eq 'whirlpool';
2228 *** Warning: whirlpool use requires an unofficial OID (1.2.840.113549.1.1.15)
2229 *** be used for whirlpoolWithRSAEncryption. See the following:
2230 *** http://openssl.6102.n7.nabble.com/Creating-a-x509-request-with-Whirlpool-td27209.html#message27213
2231 *** Such certificates are unlikely to work. So unless you have a
2232 *** specific application that you know supports the unofficial value
2233 *** for whirlpoolWithRSAEncryption you should select a different
2234 *** signing digest. Continuing anyway.
2236 print STDERR
"Using digest $digest\n" if $verbose;
2238 my ($cver,$cser,$issuer,$vst,$vnd,$subj,$subjkey,$subjkeyid);
2242 $subjkeyid = $pubkeyid;
2245 $inform = -T
$certfile ?
'PEM' : 'DER';
2246 print STDERR
"certfile -inform $inform\n" if $debug;
2250 #open(my $olderr, ">&STDERR") or die "Cannot dup STDERR: $!\n";
2251 #open(STDERR, '>', "/dev/null") or die "Cannot redirect STDERR: $!";
2252 open(READCERT
, "-|", "openssl", "x509", "-inform", $inform, "-outform",
2253 "DER", "-in", $certfile)
2254 or die "Cannot read X.509 certificate in \"$certfile\"\n";
2255 #open(STDERR, ">&", $olderr) or die "Cannot dup \$olderr: $!";
2257 die "Error reading X.509 certificate in \"$certfile\"\n"
2258 unless !!($signcert = <READCERT
>);
2261 ($cver,$cser,$issuer,$vst,$vnd,$subj,$subjkey,$subjkeyid) =
2262 GetCertInfo
($signcert);
2263 die "Unparseable certificate format in \"$certfile\"\n" unless $cver;
2265 substr($dser,0,1) = '' if unpack('C',substr($cser,0,1)) == 0x00;
2266 print STDERR
"X.509 Certificate $certfile:\n",
2267 " ver=v$cver serial=", join(":", tolower
(unpack("H*",$dser))=~/../g),"\n"
2269 print STDERR
" notBefore=",DERTimeStr
($vst)||'Invalid Time',
2270 " notAfter=",DERTimeStr
($vnd)||'Invalid Time',"\n" if $verbose;
2271 #print STDERR " issuer=",DERNameStr($issuer),"\n" if $verbose;
2272 #print STDERR " name=",DERNameStr($subj),"\n" if $verbose;
2273 print STDERR
" subj_keyid=", join(":", toupper
(
2274 unpack("H*",$subjkeyid))=~/../g), "\n" if defined($subjkeyid) && $verbose;
2275 die "The private key is not the correct one for the certificate:\n".
2276 " certificate: $certfile\n".
2277 " private key: $keyfile\n" unless $subjkey eq $pubkey;
2278 if (!defined($subjkeyid)) {
2279 warn "*** Warning: The certificate has no subjectKeyIdentifier, "
2280 . "using RFC 5280 (1)\n";
2281 $subjkeyid = $pubkeyid;
2283 warn "*** Warning: subjectKeyIdentifier non-standard, continuing anyway\n"
2284 unless $subjkeyid eq $pubkeyid;
2285 die "*** Error: The $keytype public key is the same as the certificate's "
2287 . "*** They must be different for security reasons.\n"
2288 if $pubkey eq $opensshpub;
2292 my $version = pack('CCCCC', 0xA0, 0x03, 0x02, 0x01, 0x02); # v3
2296 $randval = RandomID
($quiet);
2297 $serialRDN = join(":", tolower
(unpack("H*",$randval))=~/../g) if $root;
2299 my $sigAlg = $dalg . pack('CC',0x05,0x00);
2300 $sigAlg = pack('C',0x30).DERLength
(length($sigAlg)).$sigAlg;
2301 my $stuffedrand = '';
2302 if ($root && $useRandom && $seensingletons{'2.5.4.5'}) {
2303 foreach my $rdn (@dnseq) {
2304 if (@
$rdn == 1 && ${$$rdn[0]}[0] eq '2.5.4.5') {
2305 if (${$$rdn[0]}[1] eq '#') {
2306 ${$$rdn[0]}[1] = $serialRDN;
2314 foreach my $rdn (@dnseq) {
2316 foreach my $pair (@
$rdn) {
2317 my $packedoid = DEROID
($$pair[0]);
2318 my $val = MakeUTF8
($$pair[1]);
2319 my $st = get_oid_string_type
($$pair[0], $$pair[1]);
2320 my $packed = $packedoid.pack('C',$st).DERLength
(length($val)).$val;
2321 $packedrdn .= pack('C',0x30).DERLength
(length($packed)).$packed;
2323 $name .= pack('C',0x31).DERLength
(length($packedrdn)).$packedrdn;
2325 if ($root && $useRandom && !$stuffedrand) {
2326 $serialRDN = pack('C',0x13).DERLength
(length($serialRDN)).$serialRDN;
2327 $serialRDN = $serialNumber . $serialRDN;
2328 $serialRDN = pack('C',0x30).DERLength
(length($serialRDN)).$serialRDN;
2329 $serialRDN = pack('C',0x31).DERLength
(length($serialRDN)).$serialRDN;
2330 $name = $serialRDN . $name;
2333 my $dnq = $qualifier;
2334 $dnq = pack('C',0x13).DERLength
(length($dnq)).$dnq;
2335 $dnq = $dnQualifier . $dnq;
2336 $dnq = pack('C',0x30).DERLength
(length($dnq)).$dnq;
2337 $dnq = pack('C',0x31).DERLength
(length($dnq)).$dnq;
2340 $name = pack('C',0x30).DERLength
(length($name)).$name;
2341 $subj = $name if $root;
2342 my $validity = ($useNow ? DERTime
(time()) : $vst).$vnd;
2343 $validity = pack('C',0x30).DERLength
(length($validity)).$validity;
2345 if ($subca || $root) {
2346 $extCAVal = $boolTRUE;
2347 if ($subca && $pathlen ne '') {
2348 $extCAVal .= DERInteger
($pathlen);
2350 $extCAVal = pack('C',0x30).DERLength
(length($extCAVal)).$extCAVal;
2353 #$extCAVal = pack('C',0x30).DERLength(length($boolFALSE)).$boolFALSE;
2354 $extCAVal = pack('C',0x30).DERLength
(0); # do not include DEFAULT value
2356 $extCAVal = pack('C',0x04).DERLength
(length($extCAVal)).$extCAVal;
2357 $extCAVal = $basicConstraints . $boolTRUE . $extCAVal;
2358 $extCAVal = pack('C',0x30).DERLength
(length($extCAVal)).$extCAVal;
2359 my $extKeyBits = 0x80;
2360 $extKeyBits |= 0x06 if $subca || $root;
2361 $extKeyBits |= 0x20 if $server;
2362 $extKeyBits |= 0x60 if $email;
2363 my $extKeySpare = scalar(@
{[
2364 unpack("B*", chr((($extKeyBits & ($extKeyBits-1)) ^ $extKeyBits) - 1))
2366 my $extKeyUse = pack('H*', '04040302').pack('CC',$extKeySpare,$extKeyBits);
2367 $extKeyUse = $keyUsage . $boolTRUE. $extKeyUse;
2368 $extKeyUse = pack('C',0x30).DERLength
(length($extKeyUse)).$extKeyUse;
2369 my $extXKeyUse = '';
2370 if ($server || $client || $codesign || $email || $applecodesign) {
2371 $extXKeyUse .= $serverAuth if $server;
2372 $extXKeyUse .= $clientAuth if $client;
2373 $extXKeyUse .= $codeSigning if $codesign;
2374 $extXKeyUse .= $emailProtection if $email;
2375 $extXKeyUse .= $appleCodeSigning if $applecodesign;
2376 $extXKeyUse = pack('C',0x30).DERLength
(length($extXKeyUse)).$extXKeyUse;
2377 $extXKeyUse = pack('C',0x04).DERLength
(length($extXKeyUse)).$extXKeyUse;
2378 $extXKeyUse = $extKeyUsage . $boolTRUE . $extXKeyUse;
2379 $extXKeyUse = pack('C',0x30).DERLength
(length($extXKeyUse)).$extXKeyUse;
2381 my $extSubjKey = pack('C',0x04).DERLength
(length($sshkeyid)).$sshkeyid;
2382 $extSubjKey = pack('C',0x04).DERLength
(length($extSubjKey)).$extSubjKey;
2383 $extSubjKey = $subjKeyId . $extSubjKey;
2384 $extSubjKey = pack('C',0x30).DERLength
(length($extSubjKey)).$extSubjKey;
2385 my $extAuthKey = '';
2386 if (!$root || $rootauth) {
2387 $extAuthKey = pack('C',0x80).DERLength
(length($pubkeyid)).$pubkeyid;
2388 if (!$root && $authext) {
2389 my $gen = pack('C',0xA4).DERLength
(length($issuer)).$issuer;
2390 $extAuthKey .= pack('C',0xA1).DERLength
(length($gen)).$gen;
2391 $extAuthKey .= pack('C',0x82).DERLength
(length($cser)).$cser;
2393 $extAuthKey = pack('C',0x30).DERLength
(length($extAuthKey)).$extAuthKey;
2394 $extAuthKey = pack('C',0x04).DERLength
(length($extAuthKey)).$extAuthKey;
2395 $extAuthKey = $authKeyId . $extAuthKey;
2396 $extAuthKey = pack('C',0x30).DERLength
(length($extAuthKey)).$extAuthKey;
2398 my $exts = $extCAVal . $extKeyUse . $extXKeyUse . $extSubjKey . $extAuthKey;
2399 if ($email || ($server && @serverAltNames)) {
2402 $extSubjAlt = MakeUTF8
($ARGV[0]);
2403 $extSubjAlt = pack('C',0x81).DERLength
(length($extSubjAlt)).$extSubjAlt;
2406 foreach my $alt (@serverAltNames) {
2407 $extSubjAlt .= pack('C',$$alt[0]).DERLength
(length($$alt[1])).$$alt[1];
2410 $extSubjAlt = pack('C',0x30).DERLength
(length($extSubjAlt)).$extSubjAlt;
2411 $extSubjAlt = pack('C',0x04).DERLength
(length($extSubjAlt)).$extSubjAlt;
2412 $extSubjAlt = $subjAltName . $extSubjAlt; # not crit unless empty DN
2413 $extSubjAlt = pack('C',0x30).DERLength
(length($extSubjAlt)).$extSubjAlt;
2414 $exts .= $extSubjAlt;
2416 $exts = pack('C',0x30).DERLength
(length($exts)).$exts;
2417 $exts = pack('C',0xA3).DERLength
(length($exts)).$exts;
2420 $serial = pack('C',0x2).DERLength
(length($randval)).$randval;
2423 my $idtohash = $version.$sigAlg.$subj.$validity.$name.$opensshpub.$exts;
2424 $idtohash = pack('C',0x30).DERLength
(length($idtohash)).$idtohash;
2425 my $idhash = sha1
($idtohash);
2426 my $byte0 = unpack('C',substr($idhash,0,1));
2428 substr($idhash,0,1) = pack('C',$byte0);
2429 $serial = pack('C',0x2).DERLength
(length($idhash)).$idhash;
2431 my $tbs = $version.$serial.$sigAlg.$subj.$validity.$name.$opensshpub.$exts;
2432 $tbs = pack('C',0x30).DERLength
(length($tbs)).$tbs;
2433 my $tbsseq = &$dfunc($tbs);
2434 $tbsseq = pack('C',0x04).DERLength
(length($tbsseq)).$tbsseq;
2435 my $algid = $did . pack('CC',0x05,0x00);
2436 $algid = pack('C',0x30).DERLength
(length($algid)).$algid;
2437 $tbsseq = $algid . $tbsseq;
2438 $tbsseq = pack('C',0x30).DERLength
(length($tbsseq)).$tbsseq;
2439 my $sig = RSASign
($tbsseq, $keyfile);
2440 $sig = pack('C',0x03).DERLength
(length($sig)+1).pack('C',0x00).$sig;
2441 my $cert = $tbs . $sigAlg . $sig;
2442 $cert = pack('C',0x30).DERLength
(length($cert)).$cert;
2443 my $base64 = join("\n", BreakLine
(encode_base64
($cert, ''), 64))."\n";
2445 if ($outfile ne '-') {
2446 open($output, ">", $outfile)
2447 or die "Cannot open \"$outfile\" for output: $!\n";
2451 print $output "-----BEGIN CERTIFICATE-----\n",
2453 "-----END CERTIFICATE-----\n",
2455 close($output) if $outfile ne '-';