CACreateCert: remove "OfIncorporation" from EV subject OIDs
[ezcert.git] / CACreateCert
blob7ce55e0f2bd97c37dd88e859a28630f86cd8d085
1 #!/usr/bin/env perl
3 # CACreateCert - Create various types of certificates
4 # Copyright (C) 2011-2017 Kyle J. McKay.
5 # All rights reserved.
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/>.
20 exit(&main());
22 use strict;
23 use warnings;
24 use bytes;
26 use MIME::Base64;
27 use IPC::Open2;
28 use Digest::MD5 qw(md5 md5_hex md5_base64);
29 use Getopt::Long qw(:config gnu_getopt);
31 our $VERSION;
32 my $VERSIONMSG;
33 my $HELP;
34 my $USAGE;
36 my $hasSha2;
38 BEGIN {
39 *VERSION = \'1.3.3';
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";
48 BEGIN {
49 $hasSha2 = 0;
51 eval {
52 require Digest::SHA;
53 Digest::SHA->import(
54 qw(
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
61 ); $hasSha2=1} ||
62 eval {
63 require Digest::SHA::PurePerl;
64 require Digest::SHA1;
65 Digest::SHA1->import(
66 qw(
67 sha1 sha1_hex sha1_base64
70 Digest::SHA::PurePerl->import(
71 qw(
72 sha224 sha224_hex sha224_base64
73 sha256 sha256_hex sha256_base64
74 sha384 sha384_hex sha384_base64
75 sha512 sha512_hex sha512_base64
77 ); $hasSha2=1} ||
78 eval {
79 require Digest::SHA::PurePerl;
80 Digest::SHA::PurePerl->import(
81 qw(
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
88 ); $hasSha2=1} ||
89 eval {
90 require Digest::SHA1;
91 Digest::SHA1->import(
92 qw(
93 sha1 sha1_hex sha1_base64
95 ); 1} ||
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";
103 BEGIN {
104 $USAGE = <<USAGE;
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"
112 USAGE
113 $HELP = <<HELP;
114 NAME
115 CACreateCert -- create X.509 certificate
117 SYNOPSIS
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
129 DESCRIPTION
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
133 certificates.
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
158 UTF-8.
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
181 are used.
183 Also note that the certificates created by this utility do not expire.
185 OPTIONS
186 -h/--help
187 Show this help
189 -V/--version
190 Show the CACreateCert version
192 -v/--verbose
193 Produce extra informational messages to standard error.
194 Suppresses --quiet.
196 --list-oid-names
197 Show a list of recognized key names for the --dni option. If
198 given other options are ignored.
200 --man
201 Same as --verbose --help.
203 --debug
204 Show debugging information. Automatically enables --verbose.
205 Suppresses --quiet.
207 --quiet
208 Suppress all messages except errors. Ignored if --debug or
209 --verbose given.
211 --check
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.
216 --now
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
223 arguments.
225 --pubx509/--pubX509
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 "-".
240 --digest name
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.
252 --root
253 --subca
254 --server
255 --codesign
256 --applecodesign
257 --email
258 --client
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
276 each option given).
278 --acme
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.
284 --pathlen n
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.
289 --rootauth
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).
294 --authext
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.
300 --random
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.
317 --no-random
318 Ignored unless --root given. Turns off the default --random
319 option that is normally enabled by default when --root is given.
321 --key priv_key_file
322 The RSA private key in either PEM or DER format. This option
323 is always required.
325 --cert signing_cert
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.
330 --in pub_key_file
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.
338 --out out_cert.pem
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.
343 --suffix suffix.pem
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.
374 --no-max-len
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).
391 --dnq qual
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.
401 name string
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
417 --dnq value).
419 NOTES
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
424 instead.
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.
488 TIPS
489 Display the currently available version of OpenSSL with:
491 openssl version
493 Display the currently available version of OpenSSH with:
495 ssh -V
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
507 EXAMPLES
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.)
590 BUGS
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
596 to be valid.
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.
607 HELP
610 sub IsUTF8($)
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
615 use bytes;
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));
620 next if $c < 0x80;
621 return 0 if $c < 0xC0 || $c >= 0xF8;
622 if ($c <= 0xDF) {
623 # Need 1 more byte
624 ++$i;
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;
630 next;
632 if ($c <= 0xEF) {
633 # Need 2 more bytes
634 $i += 2;
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;
642 next;
644 # Need 3 more bytes
645 $i += 3;
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)
654 | ($c4 & 0x3F);
655 return 0 if $u < 0x10000 || $u >= 0x10FFFE || (($u & 0xFFFF) >= 0xFFFE);
657 return 1;
660 sub Make1252()
662 use bytes;
663 our %W1252;
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
695 sub MakeUTF8($)
697 use bytes;
698 our %W1252;
700 return $_[0] if (IsUTF8($_[0]));
701 my $ans = '';
702 foreach my $c (unpack('C*',$_[0])) {
703 if ($c < 0x80) {
704 $ans .= chr($c);
706 else {
707 # Ass/u/me we have Latin-1 (ISO-8859-1) but per the HTML 5 draft treat
708 # it as windows-1252
709 if ($c >= 0xA0 || !defined($W1252{$c})) {
710 $ans .= chr(0xC0 | ($c >> 6));
711 $ans .= chr(0x80 | ($c & 0x3F));
713 else {
714 $ans .= $W1252{$c};
718 return $ans;
721 sub formatbold($;$)
723 my $str = shift;
724 my $fancy = shift || 0;
725 if ($fancy) {
726 $str = join('',map($_."\b".$_, split(//,$str)));
728 return $str;
731 sub formatul($;$)
733 my $str = shift;
734 my $fancy = shift || 0;
735 if ($fancy) {
736 $str = join('',map("_\b".$_, split(//,$str)));
738 return $str;
741 sub formatman($;$)
743 my $man = shift;
744 my $fancy = shift || 0;
745 my @inlines = split(/\n/, $man, -1);
746 my @outlines = ();
747 foreach my $line (@inlines) {
748 if ($line =~ /^[A-Z]+$/) {
749 $line = formatbold($line, $fancy);
751 else {
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;
759 return $result;
762 my %oidnames;
763 my %knownoids;
764 my %oidstringtypes;
765 my %oidstringlengths;
766 my %oidstringrestrictions;
767 my $oidnamelist;
769 BEGIN {
770 my %oiddata = (
771 'commonName' => '2.5.4.3',
772 'CN' => '2.5.4.3',
773 'surname' => '2.5.4.4',
774 'SN' => '2.5.4.4',
775 'serialNumber' => '2.5.4.5',
776 'serial' => '2.5.4.5',
777 'countryName' => '2.5.4.6',
778 'C' => '2.5.4.6',
779 'localityName' => '2.5.4.7',
780 'L' => '2.5.4.7',
781 'stateOrProvinceName' => '2.5.4.8',
782 'ST' => '2.5.4.8',
783 'streetAddress' => '2.5.4.9',
784 'street' => '2.5.4.9',
785 'organizationName' => '2.5.4.10',
786 'O' => '2.5.4.10',
787 'organizationalUnitName' => '2.5.4.11',
788 'OU' => '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',
796 'GN' => '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',
825 'city' => '2.5.4.7',
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',
832 'zip' => '2.5.4.17',
833 'zipCode' => '2.5.4.17',
834 'phone' => '2.5.4.20',
835 'phoneNumber' => '2.5.4.20',
836 'fax' => '2.5.4.23',
837 'faxNumber' => '2.5.4.23',
838 'DNQ' => '2.5.4.46',
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
843 %oidstringtypes = (
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
880 my %aliases = ();
881 my $maxlen = 0;
882 %oidnames = ();
883 %knownoids = ();
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;
892 push(@$nlist, $key);
894 my @list = ();
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)));
907 sub DERLength($)
909 # return a DER encoded length
910 my $len = shift;
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);
919 sub SingleOID($)
921 # return a single DER encoded OID component
922 no warnings;
923 my $num = shift;
924 $num += 0;
925 my $result = pack('C', $num & 0x7F);
926 $num >>= 7;
927 while ($num) {
928 $result = pack('C', 0x80 | ($num & 0x7F)) . $result;
929 $num >>= 7;
931 return $result;
934 sub DEROID($)
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.
939 no warnings;
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
943 my $ans = '';
944 foreach my $num (@ids) {
945 $ans .= SingleOID($num);
947 return pack('C',0x6).DERLength(length($ans)).$ans;
950 sub DERTime($)
952 my $t = shift; # a time() value
953 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($t);
954 $year += 1900;
955 ++$mon;
956 my $tag;
957 my $tstr;
958 if (1950 <= $year && $year < 2050) {
959 # UTCTime
960 $tag = 0x17;
961 $tstr = sprintf("%02d%02d%02d%02d%02d%02dZ", $year % 100, $mon, $mday,
962 $hour, $min, $sec);
964 else {
965 # GeneralizedTime
966 $tag = 0x18;
967 $tstr = sprintf("%04d%02d%02d%02d%02d%02dZ", $year, $mon, $mday,
968 $hour, $min, $sec);
970 return pack('C',$tag).DERLength(length($tstr)).$tstr;
973 sub DERInteger($)
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);
982 sub RandomID(;$)
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";
991 my $result = '';
992 for (my $cnt = 0; $cnt < 20; ++$cnt) {
993 my $byte;
994 sysread(RANDIN, $byte, 1)
995 or die "Cannot read from $randfile: $!\n";
996 if (!$cnt) {
997 my $val = unpack('C', $byte);
998 $val &= 0x7F;
999 $byte = pack('C', $val);
1001 $result .= $byte;
1003 close(RANDIN);
1004 print STDERR "...done creating serial number.\n" unless $suppress;
1005 return $result;
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
1012 my $der = shift;
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
1019 my $val = 0;
1020 for (my $i = 0; $i < $cnt; ++$i) {
1021 $val <<= 8;
1022 $val |= unpack('C',substr($der,$i+1,1));
1024 return ($val, $cnt+1);
1027 sub DERTimeStr($)
1029 my $der = shift;
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;
1035 return undef
1036 unless ($byte == 0x17 && $len == 13) || ($byte == 0x18 && $len == 15);
1037 substr($der,0,1+$lenbytes) = '';
1038 if ($byte == 0x17) {
1039 no warnings;
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
1054 # the key id
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
1072 my $input = shift;
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;
1078 my @parts = ();
1079 while (length($data) >= 4) {
1080 my $len = unpack('N',substr($data,0,4));
1081 my $value = '';
1082 if ($len > 0) {
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;
1090 return $parts[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));
1108 if (!$byte) {
1109 $bits -= 8;
1111 else {
1112 return undef if $byte & 0x80; # negative modulus is not allowed
1113 while (!($byte & 0x80)) {
1114 --$bits;
1115 $byte <<= 1;
1119 my $rawexp = $parts[1];
1120 my $exp;
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;
1126 else {
1127 $exp = 0;
1128 while (length($rawexp)) {
1129 $exp <<= 8;
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);
1138 sub GetKeyInfo($)
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
1144 # the key id
1145 # the OpenSSH md5 fingerprint
1146 # the OpenSSH sha1 fingerprint
1147 # or undef if the key is unparseable
1149 # Expected format is:
1150 # SEQUENCE {
1151 # SEQUENCE {
1152 # OBJECT IDENTIFIER :rsaEncryption = 1.2.840.113549.1.1.1
1153 # NULL
1155 # BIT STRING (primitive) {
1156 # 0 unused bits
1157 # SEQUENCE { # this part is the contents of an "RSA PUBLIC KEY" file
1158 # INTEGER modulus
1159 # INTEGER publicExponent
1164 no warnings;
1165 my $der = shift;
1166 my $rawmod;
1167 my $rawexp;
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
1175 return undef
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;
1197 $rawmod = $der;
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));
1201 if (!$byte) {
1202 $bits -= 8;
1204 else {
1205 return undef if $byte & 0x80; # negative modulus is not allowed
1206 while (!($byte & 0x80)) {
1207 --$bits;
1208 $byte <<= 1;
1212 $der = $derexp;
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
1218 $rawexp = $der;
1219 my $exp;
1220 if ($len > 8) {
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;
1225 else {
1226 $exp = 0;
1227 while (length($der)) {
1228 $exp <<= 8;
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));
1240 sub GetCertInfo($)
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
1256 no warnings;
1257 my $der = shift;
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));
1269 my $ver = 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
1275 $ver = $byte + 1;
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;
1339 my $skid = undef;
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) = '';
1353 next;
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);
1369 last;
1371 return ($ver,$serial,$issuer,$vst,$vnd,$subj,$subjkey,$skid)
1374 sub BreakLine($$)
1376 my ($line,$width) = @_;
1377 my @ans = ();
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);
1384 return @ans;
1387 sub whirlpool($)
1389 my $data = shift;
1390 my $hash;
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",
1396 "-binary"))
1397 or die "Cannot start openssl dgst\n";
1398 print CHLD_IN $data;
1399 close(CHLD_IN);
1400 local $/;
1401 die "Error reading whirlpool digest from openssl dgst\n"
1402 unless !!($hash = <CHLD_OUT>);
1403 waitpid($pid, 0);
1404 close(CHLD_OUT);
1405 #open(STDERR, ">&", $olderr) or die "Cannot dup \$olderr: $!";
1407 return $hash;
1410 sub GetDigest($)
1412 my $dgst = shift;
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';
1426 my $h = undef;
1427 my $oid = undef;
1428 my $func = undef;
1429 for (;;) {
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';
1440 last;
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;
1465 return 'sha512';
1468 sub toupper($)
1470 my $str = shift;
1471 $str =~ tr/a-z/A-Z/;
1472 return $str;
1475 sub tolower($)
1477 my $str = shift;
1478 $str =~ tr/A-Z/a-z/;
1479 return $str;
1482 sub RSASign($$)
1484 my ($data, $keyfile) = @_;
1485 my $sig;
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;
1494 close(CHLD_IN);
1495 local $/;
1496 die "Error reading RSA signature from openssl rsautl\n"
1497 unless !!($sig = <CHLD_OUT>);
1498 waitpid($pid, 0);
1499 close(CHLD_OUT);
1500 #open(STDERR, ">&", $olderr) or die "Cannot dup \$olderr: $!";
1502 return $sig;
1505 my %rsadsa_known_strengths;
1506 BEGIN {
1507 %rsadsa_known_strengths = (
1508 1024 => 80,
1509 2048 => 112,
1510 3072 => 128,
1511 7680 => 192,
1512 15360 => 256,
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};
1522 my $guess;
1523 if ($rsadsabits < 1024) {
1524 $guess = 80 * sqrt($rsadsabits/1024);
1525 } elsif ($rsadsabits > 15360) {
1526 $guess = 256 * sqrt($rsadsabits/15360);
1527 } else {
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);
1544 sub is_ipv4($)
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.
1553 sub parseipv6($)
1555 my $a = shift;
1556 return undef unless $a =~ /^[:0-9a-fA-F.]+$/;
1557 my $two = 0;
1558 my @group1 = ();
1559 my @group2 = ();
1560 if ($a =~ /^(.*)::(.*)$/) {
1561 @group1 = split(/:/, $1) if $1;
1562 @group2 = split(/:/, $2) if $2;
1563 $two = 1;
1564 } else {
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;
1574 if ($two) {
1575 my $zcomps = 8 - (@group1 + @group2);
1576 for (my $i=0; $i < $zcomps; ++$i) {
1577 push(@group1, 0);
1580 my $ans = '';
1581 foreach my $comp (@group1,@group2) {
1582 return undef unless $comp =~ /^[0-9a-fA-F]{1,4}$/;
1583 $ans .= pack('n', hex($comp));
1585 return $ans;
1588 sub parseip($)
1590 my $a = shift;
1591 if (is_ipv4($a)) {
1592 return pack('CCCC', split(/\./, $a, 4));
1593 } else {
1594 return parseipv6($a);
1598 # See these RFCs:
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
1603 sub is_dns_valid($)
1605 my $dns = shift;
1606 defined($dns) or $dns = '';
1607 return 0 if $dns eq '' || $dns =~ /\s/;
1608 my @labels = split(/\./, $dns, -1);
1609 # Check each label
1610 my $i = -1;
1611 foreach my $label (@labels) {
1612 ++$i;
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;
1618 return 1;
1621 sub handle_dns_opt($$)
1623 my $val = shift;
1624 my $altsref = shift;
1625 my $ip = parseip($val);
1626 if (defined($ip)) {
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]);
1630 } else {
1631 $val =~ s/\.$//;
1632 die "Not a valid dns name or IPv4/IPv6 address: $val\n"
1633 unless is_dns_valid($val);
1634 push(@$altsref, [0x82, $val]);
1638 sub is_oid_valid($)
1640 my $oid = shift;
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;
1646 return 1;
1649 sub is_email_valid($;$$)
1651 my ($val, $emailatok, $emailatseen) = @_;
1652 my $isat;
1653 if ($val eq "@") {
1654 return 0 if !$emailatok || $$emailatseen;
1655 $val = $emailatok;
1656 $isat = 1;
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;
1662 if ($isat) {
1663 $_[0] = $emailatok;
1664 $$emailatseen = 1;
1666 return 1;
1669 sub validate_oid_value($$$;$$)
1671 our $quiet;
1672 our $nomaxlen;
1673 our $useRandom;
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";
1680 return 0;
1682 } elsif ($len =~ /^,(\d+)$/) {
1683 my $max = $1;
1684 if (length($value) > $max) {
1685 warn "--dni key type '$key' requires no more than $max characters\n"
1686 unless $quiet;
1687 return 0 unless $nomaxlen;
1689 } elsif ($len =~ /^(\d+),$/) {
1690 my $min = $1;
1691 if (length($value) < $min) {
1692 warn "--dni key type '$key' requires at least $min characters\n";
1693 return 0;
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;
1702 } else {
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);
1735 return 1;
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;
1749 return 12;
1752 sub handle_dni_opt($$$$$)
1754 my $opt = shift;
1755 my $listref = shift;
1756 my $stdinokref = shift;
1757 my $emailatok = shift;
1758 my $emailatseen = shift;
1759 my $dor;
1760 if ($$stdinokref && $$stdinokref > 2) {
1761 $dor = sub {warn "@_"; return 0}
1762 } else {
1763 $dor = sub {die "@_"}
1765 $opt =~ s/^\s+//;
1766 $opt =~ s/\s+$//;
1767 if ($opt =~ /^\@(.*)$/os) {
1768 my $fn = $1;
1769 if ($$stdinokref && $$stdinokref > 2) {
1770 warn "May not use \@filename syntax within a --dni \@filename file\n";
1771 return 0;
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 '-';
1778 my $stdinnotok = 3;
1779 my $input;
1780 my $infilename;
1781 if ($fn ne '-') {
1782 $infilename = "\"$fn\"";
1783 open($input, '<', $fn)
1784 or die "Cannot open $infilename for input: $!\n";
1785 } else {
1786 $input = *STDIN;
1787 $infilename = 'standard input';
1789 my $lineno = 0;
1790 while (my $line = <$input>) {
1791 ++$lineno;
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 '-';
1798 return 1;
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);
1803 my $oid;
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};
1809 $oid = $key;
1810 } else {
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]);
1821 return 1;
1824 sub main
1826 Make1252(); # Set up the UTF-8 auxiliary conversion table
1828 my $help = '';
1829 my $verbose = '';
1830 our $quiet = '';
1831 our $nomaxlen = '';
1832 my $acme = '';
1833 my $keyfile = '';
1834 my $certfile = '';
1835 my $useNow = '';
1836 our $useRandom = '';
1837 my $useNoRandom = '';
1838 my $termOK = '';
1839 my $server = '';
1840 my @serverAltNames = ();
1841 my $codesign = '';
1842 my $applecodesign = '';
1843 my $client = '';
1844 my $email = '';
1845 my $subca = '';
1846 our $root = '';
1847 my $rootauth = '';
1848 my $authext = '';
1849 my $digest = $hasSha2 ? 'sha256' : 'sha1';
1850 my $digestChoice = '';
1851 my $debug = 0;
1852 my $pubx509 = '';
1853 my $check = '';
1854 my $pathlen = '';
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";
1875 my $infile = '-';
1876 my $outfile = '-';
1877 my @suffixfiles = ();
1878 my $suffix = '';
1879 my $qualifier = undef;
1880 my @dnilist = ();
1882 eval {GetOptions(
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)},
1888 "debug" => \$debug,
1889 "quiet" => \$quiet,
1890 "pubx509" => \$pubx509,
1891 "pubX509" => \$pubx509,
1892 "check" => \$check,
1893 "acme" => \$acme,
1894 "now" => \$useNow,
1895 "random" => \$useRandom,
1896 "no-random" => \$useNoRandom,
1897 "no-max-len" => \$nomaxlen,
1898 "t" => \$termOK,
1899 "server" => \$server,
1900 "codesign" => \$codesign,
1901 "applecodesign" => \$applecodesign,
1902 "email" => \$email,
1903 "client" => \$client,
1904 "subca" => \$subca,
1905 "root" => \$root,
1906 "rootauth" => \$rootauth,
1907 "authext" => \$authext,
1908 "digest=s" => \$digestChoice,
1909 "key|k=s" => \$keyfile,
1910 "cert|c=s" => \$certfile,
1911 "pathlen=i" => \$pathlen,
1912 "in=s" => \$infile,
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])}
1918 )} || $help
1919 or die $USAGE;
1920 if ($help) {
1921 local *MAN;
1922 my $pager = $ENV{'PAGER'} || 'less';
1923 if (-t STDOUT && open(MAN, "|-", $pager)) {
1924 print MAN formatman($HELP,1);
1925 close(MAN);
1927 else {
1928 print formatman($HELP);
1930 exit(0);
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;
1936 my @dnseq = ();
1937 if ($acme) {
1938 $useNow = '';
1939 $useNoRandom = '';
1940 $useRandom = 1;
1941 $root = 1;
1942 $rootauth = '';
1943 $qualifier = undef;
1944 @serverAltNames = ();
1945 $client = $subca = $server = $codesign = $applecodesign = $email = '';
1946 @suffixfiles = ();
1947 @dnseq = (
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;
1964 $client = 1 if
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];
1970 $emailatseen = 0;
1972 foreach my $dnitem (@dnilist) {
1973 handle_dni_opt($dnitem, \@dnseq, \$dnistdinok, $emailatok, \$emailatseen);
1975 if ($useRandom) {
1976 my $stuffcount = 0;
1977 my $seenbad = 0;
1978 RDN: foreach my $rdn (@dnseq) {
1979 if (@$rdn == 1) {
1980 ++$stuffcount if ${$$rdn[0]}[0] eq '2.5.4.5' && ${$$rdn[0]}[1] eq '#';
1981 } else {
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';
1998 my $n = 'n';
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 "
2005 . "public key)\n"
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 '') {
2010 if ($client) {
2011 # Okay to be empty if we've seen a user id singleton
2012 $emptynameok = 1 if $seensingletons{$userIdOID};
2013 } elsif (!$email) {
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 '') {
2024 my ($oid, $key);
2025 if ($client) {
2026 $oid = $userIdOID;
2027 $key = 'UID';
2028 } elsif ($email) {
2029 $oid = $emailAddressOID;
2030 $key = 'emailAddress';
2031 } else {
2032 $oid = $commonNameOID;
2033 $key = 'CN';
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;
2039 my $opensshdotpub;
2040 my $infilename;
2041 foreach my $suffixfile (@suffixfiles) {
2042 open(SUFFIX, '<', $suffixfile)
2043 or die "Cannot open '$suffixfile' for input: $!\n";
2044 local $/;
2045 $suffix .= <SUFFIX>;
2046 close(SUFFIX);
2048 if (!$root) {
2049 local $/ if $pubx509;
2050 my $input;
2051 if ($infile ne '-') {
2052 $infilename = "\"$infile\"";
2053 open($input, '<', $infile)
2054 or die "Cannot open $infilename for input: $!\n";
2055 } else {
2056 $input = *STDIN;
2057 $infilename = 'standard input';
2059 !!($opensshdotpub = <$input>)
2060 or die "Cannot read $keytype public key from $infilename\n";
2061 if (!$pubx509) {
2062 my $auto509 = 0;
2063 if ($opensshdotpub =~ /^----[- ]BEGIN PUBLIC KEY[- ]----/) {
2064 $auto509 = 1;
2066 else {
2067 my $input = $opensshdotpub;
2068 $input =~ s/((?:\r\n|\n|\r).*)$//os;
2069 my @fields = split(' ', $input, 3);
2070 if (@fields < 2 ||
2071 length($fields[1]) < 16 ||
2072 $fields[1] !~ m|^[0-9A-Za-z+/=]+$|) {
2073 $auto509 = 1;
2076 if ($auto509) {
2077 $pubx509 = 1;
2078 $keytype = 'pubx509';
2079 print STDERR "auto detected --pubx509 option\n" if $debug;
2080 local $/;
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);
2091 if ($root) {
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
2096 elsif ($pubx509) {
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;
2107 close(WRITEKEY);
2108 local $/;
2109 die "Error reading X.509 format RSA public key from $infilename\n"
2110 unless !!($opensshpub = <READKEY>);
2111 waitpid($pid, 0);
2112 close(READKEY);
2113 $sshcmnt = undef;
2114 ($sshkeybits,$sshkeyexp,$sshkeyid,$sfmd5,$sfsha1) = GetKeyInfo($opensshpub);
2115 die "Unparseable X.509 public key format read from $infilename\n"
2116 unless $sshkeybits;
2118 else {
2119 ($sshkeybits,$sshkeyexp,$sshkeyid,$sfmd5,$sfsha1,$sshcmnt,$opensshpub) =
2120 GetOpenSSHKeyInfo($opensshdotpub);
2121 die "Unparseable OpenSSH public key read from $infilename\n"
2122 unless $sshkeybits;
2123 die "Unsupported OpenSSH public key type ($sshkeybits), must be ssh-rsa\n"
2124 unless $sshkeyexp;
2126 my $sshkeystrength;
2127 if (!$root) {
2128 my $sshkeyapprox;
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';
2158 my $pubkey;
2160 local *READKEY;
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: $!";
2167 local $/;
2168 die "Error reading RSA private key in \"$keyfile\"\n"
2169 unless !!($pubkey = <READKEY>);
2170 close(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;
2210 } else {
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"
2220 unless $quiet;
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);
2239 if ($root) {
2240 $vst = $v3Begin;
2241 $vnd = $noExpiry;
2242 $subjkeyid = $pubkeyid;
2244 else {
2245 $inform = -T $certfile ? 'PEM' : 'DER';
2246 print STDERR "certfile -inform $inform\n" if $debug;
2247 my $signcert;
2249 local *READCERT;
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: $!";
2256 local $/;
2257 die "Error reading X.509 certificate in \"$certfile\"\n"
2258 unless !!($signcert = <READCERT>);
2259 close(READCERT);
2261 ($cver,$cser,$issuer,$vst,$vnd,$subj,$subjkey,$subjkeyid) =
2262 GetCertInfo($signcert);
2263 die "Unparseable certificate format in \"$certfile\"\n" unless $cver;
2264 my $dser = $cser;
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"
2268 if $verbose;
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 "
2286 . "public key.\n"
2287 . "*** They must be different for security reasons.\n"
2288 if $pubkey eq $opensshpub;
2290 return 0 if $check;
2292 my $version = pack('CCCCC', 0xA0, 0x03, 0x02, 0x01, 0x02); # v3
2293 my $randval;
2294 my $serialRDN;
2295 if ($useRandom) {
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;
2307 $stuffedrand = 1;
2308 last;
2313 my $name = '';
2314 foreach my $rdn (@dnseq) {
2315 my $packedrdn = '';
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;
2332 if ($qualifier) {
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;
2338 $name .= $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;
2344 my $extCAVal;
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;
2352 else {
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))
2365 =~ /1/g]});
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)) {
2400 my $extSubjAlt;
2401 if ($email) {
2402 $extSubjAlt = MakeUTF8($ARGV[0]);
2403 $extSubjAlt = pack('C',0x81).DERLength(length($extSubjAlt)).$extSubjAlt;
2404 } else {
2405 $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;
2418 my $serial;
2419 if ($useRandom) {
2420 $serial = pack('C',0x2).DERLength(length($randval)).$randval;
2422 else {
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));
2427 $byte0 &= 0x7F;
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";
2444 my $output;
2445 if ($outfile ne '-') {
2446 open($output, ">", $outfile)
2447 or die "Cannot open \"$outfile\" for output: $!\n";
2448 } else {
2449 $output = *STDOUT;
2451 print $output "-----BEGIN CERTIFICATE-----\n",
2452 $base64,
2453 "-----END CERTIFICATE-----\n",
2454 $suffix;
2455 close($output) if $outfile ne '-';
2456 return 0;