3 # Written by Martin Bartosch and Alexander Klink
4 # for the OpenXPKI project 2006
5 # Copyright (c) 2006 by The OpenXPKI Project
8 our $VERSION = "[% deployment.version %]";
23 use OpenXPKI
::VERSION
;
25 use OpenXPKI
::Server
::Context
qw( CTX );
27 # settings determined by openxpki-metaconf
29 prefix
=> "[% dir.prefix %]",
30 exec_prefix
=> "[% dir.exec_prefix %]",
31 template_prefix
=> "[% dir.templatedir %]",
32 sysconfdir
=> "[% dir.sysconfdir %]",
33 localedir
=> "[% dir.localedir %]",
34 openxpkiconfdir
=> "[% dir.openxpkiconfdir %]",
37 my $configfile = "$config{openxpkiconfdir}/config.xml";
39 # read configuration from deployed OpenXPKI instance
42 my @additional_tasks = @_;
44 return OpenXPKI
::Server
::Init
::init
(
63 my $type = $OpenXPKI::Server
::Init
::current_xml_config
->get_xpath (
64 XPATH
=> [ 'common/database/type' ],
68 print STDERR
"Database type: $type\n";
70 my @databases = qw( log );
72 # SQLite needs special treatment: three databases instead of one must
74 if ($type =~ m{ SQLite }xms) {
75 push @databases, 'workflow', 'backend';
79 foreach my $db (@databases) {
81 $params->{PURPOSE
} = $db;
83 if (defined $params->{PURPOSE
}) {
84 print STDERR
"Setting up database '$db'\n";
86 my $dbi = OpenXPKI
::Server
::Init
::get_dbi
($params);
93 print STDERR
"ERROR: Could not connect to '$db' database ($EVAL_ERROR)\n";
97 if ($args->{DRYRUN
}) {
98 print $dbi->init_schema(MODE
=> 'DRYRUN') . "\n";
102 if ($args->{FORCE
}) {
103 $args{MODE
} = 'FORCE';
106 #### args for init_schema : Dumper(\%args)
108 $dbi->init_schema(%args);
111 print STDERR
"ERROR: init_schema on '$db' failed (${EVAL_ERROR})\n";
114 print STDERR
"Database '$db' initialized.\n";
124 my $targetdir = $args->{TARGETDIR
};
125 my $template_prefix = $args->{TEMPLATE_PREFIX
};
126 my $template = $args->{TEMPLATE
};
127 my @metaconf_opts = @
{$args->{METACONF_OPTS
}};
129 if (! defined $targetdir || ($targetdir eq '')) {
130 print STDERR
"No target directory specified.\n";
138 print STDERR
"Directory $targetdir does not exist or is not writable.\n";
142 print STDERR
"Deploying OpenXPKI configuration file set.\n";
143 print STDERR
"Template set: $template\n";
144 print STDERR
"Template source directory: $template_prefix\n";
145 print STDERR
"Target directory: $targetdir\n";
146 if (scalar @metaconf_opts) {
147 print STDERR
"openxpki-metaconf options: " . join(' ', @metaconf_opts) . "\n";
150 if ($args->{DRYRUN
}) {
154 if (! -d
$template_prefix) {
155 print STDERR
"ERROR: template directory $template_prefix not found\n";
159 my $srcfile = File
::Spec
->catfile($template_prefix,
163 # 2006-06-21 Martin Bartosch:
164 # The deployment procedure now will create a new meta configuration
165 # file from the one that is installed in the template directory.
167 # determine new configuration file directory (below $targetdir)
171 '--config', qq( $srcfile ),
172 '--setcfg', "dir.prefix='$targetdir'",
173 '--getcfg', 'dir.openxpkiconfdir',
177 my $cmd = join(' ', @cmd);
178 my $openxpkiconfdir = `$cmd`;
179 chomp($openxpkiconfdir);
182 if (! -d
$openxpkiconfdir) {
183 if (! mkpath
($openxpkiconfdir, 1, 0750)) {
184 print STDERR
"Could not create configuration directory $openxpkiconfdir\n";
189 my $dstfile = File
::Spec
->catfile($openxpkiconfdir, 'openxpki.conf');
192 if (! $args->{FORCE
}) {
193 print STDERR
"ERROR: $dstfile already exists\n";
196 move
($dstfile, $dstfile . '.last');
200 print STDERR
"ERROR: $srcfile not found\n";
204 # the new configuration file will reference two types of files/dirs:
205 # - files/dirs that are specific for this particular deployment
206 # (e. g. configuration, log files, server socket...)
207 # - files/dirs that are shared among ALL installed instances
211 ### $config{localedir}
214 '--config', qq( $srcfile ),
215 '--writecfg', qq( $dstfile ),
216 '--setcfg', "dir.prefix='$targetdir'",
217 '--setcfg', "dir.localedir='$config{localedir}'",
221 if (system(join(' ', @cmd)) != 0) {
222 print STDERR
"ERROR: could not deploy target configuration file $dstfile\n";
226 print STDERR
"wrote $dstfile\n";
233 my $config = OpenXPKI
::XML
::Config
->new(
234 CONFIG
=> $configfile,
236 my $nr_of_realms = $config->get_xpath_count(
237 XPATH
=> [ 'pki_realm' ],
242 for (my $i=0; $i < $nr_of_realms; $i++) {
243 push @realms, $config->get_xpath(
244 XPATH
=> [ 'pki_realm', 'name' ],
245 COUNTER
=> [ $i , 0 ],
254 my $config = OpenXPKI
::XML
::Config
->new(
255 CONFIG
=> $configfile,
258 my $nr_of_realms = $config->get_xpath_count(
259 XPATH
=> [ 'pki_realm' ],
263 # find the realm index we have to use
265 for (my $i=0; $i < $nr_of_realms; $i++) {
266 next if ($arg_ref->{REALM
} ne $config->get_xpath(
267 XPATH
=> [ 'pki_realm', 'name' ],
268 COUNTER
=> [ $i , 0 ]));
273 foreach my $type qw( ca scep ) { # iterate over ca and scep entries
274 print uc($type) . " keys:\n";
275 my $type_count = $config->get_xpath_count(
276 XPATH
=> [ 'pki_realm', $type ],
277 COUNTER
=> [ $realm_index ],
280 for (my $i = 0; $i < $type_count; $i++) { # iterate over CAs, SCEPs
281 my $type_id = $config->get_xpath(
282 XPATH
=> [ 'pki_realm' , $type, 'id' ],
283 COUNTER
=> [ $realm_index, $i , 0 ],
285 print ' Key for purpose ' . uc($type) . ' with ID: '
288 my $token_count = $config->get_xpath_count(
289 XPATH
=> [ 'pki_realm' , $type, 'token' ],
290 COUNTER
=> [ $realm_index, $i ],
293 # base path and counter for later use
294 my @token_path = ( 'pki_realm' , $type, 'token' );
295 my @token_counter = ( $realm_index, $i , 0 );
297 my $key_count = $config->get_xpath_count(
298 XPATH
=> [ @token_path , 'key' ],
299 COUNTER
=> [ @token_counter ],
301 my $secret_count = $config->get_xpath_count(
302 XPATH
=> [ @token_path , 'secret' ],
303 COUNTER
=> [ @token_counter ],
306 if ($key_count != 1) { # there should only be one key per token!
307 print STDERR
" ! Misconfiguration detected: $key_count"
308 . " keys configured!\n";
310 if ($secret_count != 1) { # there should only be one secret
311 # definition per token
312 print STDERR
" ! Misconfiguration detected: $secret_count"
313 . " secret definitions configured!\n";
316 if ($key_count == 1 && $secret_count == 1) { # everything is fine
317 my $key = $config->get_xpath(
318 XPATH
=> [ @token_path , 'key' ],
319 COUNTER
=> [ @token_counter, 0 ],
321 my $status_flag = '?';
322 if (-e
$key && (! -s
$key)) {
323 $status_flag = '0'; # file exists but is of size zero
325 elsif (-e
$key) { # file exists and is non-zero
328 else { # file does not exist (yet)
331 print ' ' . $status_flag . ' ' . $key . "\n";
333 my $secret_group_name = $config->get_xpath(
334 XPATH
=> [ @token_path , 'secret' ],
335 COUNTER
=> [ @token_counter, 0 ],
337 my @secret_path = ( 'pki_realm' , 'common', 'secret' );
338 my @secret_counter = ( $realm_index, 0 , 0 );
339 my $secret_group_count = $config->get_xpath_count(
340 XPATH
=> [ @secret_path, 'group' ],
341 COUNTER
=> [ @secret_counter, ],
345 for (my $i = 0; $i < $secret_group_count; $i++) {
346 my $group_id = $config->get_xpath(
347 XPATH
=> [ @secret_path , 'group', 'id' ],
348 COUNTER
=> [ @secret_counter, $i , 0 ],
350 if ($group_id eq $secret_group_name) {
352 last SEARCH_GROUP_ID
;
355 if (! defined $group_index) {
356 print STDERR
"Could not find configuration for secret group '$secret_group_name'.\n";
360 my $secret_method = $config->get_xpath(
361 XPATH
=> [ @secret_path , 'group' , 'method', 'id' ],
362 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ],
364 if ($secret_method ne 'literal') { # all others have total_shares
365 my $quorum_n = $config->get_xpath(
366 XPATH
=> [ @secret_path , 'group' , 'method', 'total_shares' ],
367 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ],
370 my $quorum_k = __get_required_shares
({
372 TOKEN_PATH
=> [ @secret_path, 'group' ],
373 TOKEN_COUNTER
=> [ @secret_counter, $group_index ],
375 if (! defined $quorum_k) { # if it is not defined, n is
377 $quorum_k = $quorum_n;
379 $secret_method .= ' (n = ' . $quorum_n . ', k = '
382 print ' Secret group: ' . $secret_group_name . "\n";
383 print ' Secret method: ' . $secret_method . "\n";
393 my $realm = $arg_ref->{REALM
};
394 my $secret_group_name = $arg_ref->{GROUP
};
396 my $config = OpenXPKI
::XML
::Config
->new(
397 CONFIG
=> $configfile,
400 my $nr_of_realms = $config->get_xpath_count(
401 XPATH
=> [ 'pki_realm' ],
405 # figure out realm index
407 for (my $i=0; $i < $nr_of_realms; $i++) {
408 next if ($realm ne $config->get_xpath(
409 XPATH
=> [ 'pki_realm', 'name' ],
410 COUNTER
=> [ $i , 0 ]));
414 # default token which will be used to create the keys
415 my $default_token = CTX
('crypto_layer')->get_token(
419 # figure out secret group index
420 my @secret_path = ( 'pki_realm' , 'common', 'secret' );
421 my @secret_counter = ( $realm_index, 0 , 0 );
422 my $secret_group_count = $config->get_xpath_count(
423 XPATH
=> [ @secret_path, 'group' ],
424 COUNTER
=> [ @secret_counter, ],
428 for (my $i = 0; $i < $secret_group_count; $i++) {
429 my $group_id = $config->get_xpath(
430 XPATH
=> [ @secret_path , 'group', 'id' ],
431 COUNTER
=> [ @secret_counter, $i , 0 ],
433 if ($group_id eq $secret_group_name) {
435 last SEARCH_GROUP_ID
;
438 if (! defined $group_index) {
439 print STDERR
"Could not find configuration for secret group '$secret_group_name'.\n";
442 my $secret_method = $config->get_xpath(
443 XPATH
=> [ @secret_path , 'group' , 'method', 'id' ],
444 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ],
448 my $display_secret_method = $secret_method;
449 if ($secret_method ne 'literal') { # all others have total_shares
450 $quorum_n = $config->get_xpath(
451 XPATH
=> [ @secret_path , 'group' , 'method', 'total_shares' ],
452 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ]
455 $quorum_k = __get_required_shares
({
457 TOKEN_PATH
=> [ @secret_path, 'group' ],
458 TOKEN_COUNTER
=> [ @secret_counter, $group_index ],
460 if (! defined $quorum_k) { # if it is not defined, n is
462 $quorum_k = $quorum_n;
464 $display_secret_method .= ' (n = ' . $quorum_n
465 . ', k = ' . $quorum_k . ')';
467 print "Generating keys for secret group " . $secret_group_name . "\n";
468 print "Secret method is: " . $display_secret_method . "\n\n";
471 if ($secret_method eq 'literal') {
472 # in the literal case, this is just the config entry
473 $passwd = $config->get_xpath(
474 XPATH
=> [ @secret_path , 'group' , 'method' ],
475 COUNTER
=> [ @secret_counter, $group_index, 0 ],
481 if ($secret_method eq 'plain') {
482 $crypto_secret = OpenXPKI
::Crypto
::Secret
->new({
486 for (my $i = 1; $i <= $quorum_n; $i++) {
487 my $secret = __prompt_password
("Please enter "
488 . "password share $i/$quorum_n: ");
489 $crypto_secret->set_secret({
491 SECRET
=> "$secret", # $secret is of type IO::Prompt::Return
495 $passwd = $crypto_secret->get_secret();
497 elsif ($secret_method eq 'split') {
498 $crypto_secret = OpenXPKI
::Crypto
::Secret
->new({
504 TOKEN
=> $default_token,
506 my @shares = $crypto_secret->compute(); # TODO: use bitlength
507 # based on chosen algo
508 for (my $i = 0; $i < scalar(@shares); $i++) {
510 prompt
("Please make sure that share holder number $nr is ready"
511 . " to copy the share,\n"
512 . "then press enter to view the share.");
513 for (my $j = 0; $j < 500; $j++) {
514 print "\n"; # pseudo clearscreen
516 print "Please copy the following share:\n";
517 print $shares[$i] . "\n";
518 print "Press enter to continue (next share will not "
519 . "yet be shown).\n";
521 for (my $j = 0; $j < 500; $j++) {
522 print "\n"; # pseudo clearscreen
525 $passwd = $crypto_secret->get_secret();
528 foreach my $type qw( ca scep ) { # iterate over ca and scep entries
529 my $type_count = $config->get_xpath_count(
530 XPATH
=> [ 'pki_realm', $type ],
531 COUNTER
=> [ $realm_index ],
535 for (my $i = 0; $i < $type_count; $i++) { # iterate over CAs, SCEPs
536 my $type_id = $config->get_xpath(
537 XPATH
=> [ 'pki_realm' , $type, 'id' ],
538 COUNTER
=> [ $realm_index, $i , 0 ],
541 my $token_count = $config->get_xpath_count(
542 XPATH
=> [ 'pki_realm' , $type, 'token' ],
543 COUNTER
=> [ $realm_index, $i ],
546 # base path and counter for later use
547 my @token_path = ( 'pki_realm' , $type, 'token' );
548 my @token_counter = ( $realm_index, $i , 0 );
550 my $key_count = $config->get_xpath_count(
551 XPATH
=> [ @token_path , 'key' ],
552 COUNTER
=> [ @token_counter ],
554 my $secret_count = $config->get_xpath_count(
555 XPATH
=> [ @token_path , 'secret' ],
556 COUNTER
=> [ @token_counter ],
559 if ($key_count != 1) { # there should only be one key per token!
560 print STDERR
"Misconfiguration detected: $key_count "
561 . "key files configured for id $type_id, "
562 . "purpose" . uc $type . "!\n";
565 if ($secret_count != 1) { # there should only be one secret
566 # definition per token
567 print STDERR
"Misconfiguration detected: $secret_count "
568 . "secret groups configured for id $type_id, "
569 . "purpose" . uc $type . "!\n";
573 my $curr_secret_group_name = $config->get_xpath(
574 XPATH
=> [ @token_path , 'secret' ],
575 COUNTER
=> [ @token_counter, 0 ],
577 next KEYS
if ($curr_secret_group_name ne $secret_group_name);
579 my $key_filename = $config->get_xpath(
580 XPATH
=> [ @token_path , 'key' ],
581 COUNTER
=> [ @token_counter, 0 ],
583 if (-s
$key_filename) {
584 print STDERR
"Key for purpose $type with id $type_id "
585 . "already exists in $key_filename, cowardly "
586 . "refusing to overwrite it.\n";
590 my $fu = OpenXPKI
::FileUtils
->new();
592 # get possible types and options from token
593 my %command_params = %{$default_token->get_cmd_param('create_key')};
594 print "Choose options for key for purpose '" . uc($type)
595 . "' with id '$type_id'\n";
597 print "Please choose one of the following key types:\n";
598 foreach my $possible_type ( @
{$command_params{TYPE
}} ) {
599 print " - $possible_type\n";
601 my $type = prompt
('Key type: ');
602 my $type_parameters = $command_params{PARAMETERS
}->{"TYPE:" . $type};
604 while (! defined $type_parameters) {
605 # no parameter entry in command_params available for the chosen type
606 print "Invalid type chosen, please try again.\n";
607 $type = prompt
('Key type: ');
608 $type_parameters = $command_params{PARAMETERS
}->{"TYPE:" . $type};
611 my $parameters; # parameter hash used in the token command
612 my %already_configured_parameters;
614 if (defined $type_parameters->{KEY_LENGTH
}) {
615 # key length is only possible for some algos
616 print "\nPlease choose one of the following key lengths:\n";
617 my %valid_keylength = ( );
618 foreach my $possible_kl ( @
{$type_parameters->{KEY_LENGTH
}} ) {
619 print " - $possible_kl\n";
620 $valid_keylength{$possible_kl} = 1;
623 my $key_length = prompt
('Key length: ');
624 while (! defined $valid_keylength{$key_length}) {
625 print "Invalid key length chosen, please try again.\n";
626 $key_length = prompt
('Key length: ');
628 # implicit cast, $key_length is of type IO::Prompt::ReturnVal!
629 $parameters->{KEY_LENGTH
} = "$key_length";
631 $already_configured_parameters{KEY_LENGTH
} = 1;
634 if (defined $type_parameters->{ENC_ALG
}) { # currently defined for all,
636 print "\nPlease choose one of the following key encryption "
638 my %valid_enc_alg = ( );
639 foreach my $possible_enc_alg ( @
{$type_parameters->{ENC_ALG
}} ) {
640 if ($possible_enc_alg eq '__undef') {
641 $possible_enc_alg = 'default'; # present __undef as default
643 print " - $possible_enc_alg\n";
644 $valid_enc_alg{$possible_enc_alg} = 1;
646 my $enc_alg = prompt
('Encryption algorithm: ');
647 while (! defined $valid_enc_alg{$enc_alg}) {
648 print "Invalid encryption algorithm chosen, please try "
650 $enc_alg = prompt
('Encryption algorithm: ');
652 if ("$enc_alg" ne 'default') { # specific algorithm requested
653 $parameters->{ENC_ALG
} = "$enc_alg"; # implicit type cast!
655 $already_configured_parameters{ENC_ALG
} = 1;
658 # configure optional parameters
659 foreach my $param (keys %{$type_parameters}) {
660 if (! defined $already_configured_parameters{$param} ) {
662 if (ref $type_parameters->{$param} eq '') { # type param is int
663 if ($type_parameters->{$param} == 0) {
667 elsif (ref $type_parameters->{$param} eq 'ARRAY') {
668 $optional = __string_is_in_array
({
670 ARRAY
=> $type_parameters->{$param},
674 if ($optional == 1) {
675 my $answer = prompt
"Do you want to configure the optional"
676 . " parameter $param (y/n)? ", '-y';
677 if ($answer =~ /^[yY]$/) {
681 if ($optional != 1 || $want_config == 1) {
682 if (ref $type_parameters->{$param} eq 'ARRAY') {
683 print "Please choose one of the following values "
685 foreach my $elem (@
{$type_parameters->{$param}}) {
689 my $param_value = prompt
("Value for $param: ");
690 if (ref $type_parameters->{$param} eq 'ARRAY') {
692 while (! __string_is_in_array
({
693 STRING
=> $param_value,
694 ARRAY
=> $type_parameters->{$param},
696 print "Invalid value, please try again.\n";
697 $param_value = prompt
("Value for $param: ");
700 $parameters->{$param} = "$param_value"; # implicit cast!
705 print "Creating key, please be patient ...\n";
707 $key = $default_token->command({
708 COMMAND
=> 'create_key',
711 PARAMETERS
=> $parameters,
714 my $key_path = $key_filename;
715 $key_path =~ s/(.*)\/.*/$1/; # perl is greedy, so this is the path
716 if (! -d
$key_path) { # key path does not yet exist, create it
717 eval { # try to create
721 print STDERR
"Could not create key directory: $key_path";
726 FILENAME
=> $key_filename,
729 if (-s
$key_filename) { # key file exists and is nonzero
730 print "Key successfully written to $key_filename\n\n\n";
733 print STDERR
"Key creation failed!\n";
738 print STDERR
"Key creation failed!\n";
746 sub __get_required_shares
{
747 # returns required_shares from config file or undefined if
748 # required_shares is not defined in the configuration
750 my $config = $arg_ref->{CONFIG
};
751 my @token_path = @
{$arg_ref->{TOKEN_PATH
}};
752 my @token_counter = @
{$arg_ref->{TOKEN_COUNTER
}};
756 eval { # This crashes when k is not present!
757 push @token_path , ( 'method', 'required_shares' );
758 push @token_counter, ( 0 , 0 );
759 $quorum_k_count = $config->get_xpath_count(
760 XPATH
=> [ @token_path ],
761 COUNTER
=> [ @token_counter ],
764 if ($EVAL_ERROR) { # k is not present, noticed the hard way
768 if ($quorum_k_count != 0) { # 'k' configured,
769 push @token_counter, (0);
770 $quorum_k = $config->get_xpath(
771 XPATH
=> [ @token_path ],
772 COUNTER
=> [ @token_counter ],
778 sub __prompt_password
{
779 # prompt for password until verification password and password match
780 my $question = shift;
782 my $passwords_match = 0;
784 while (! $passwords_match) {
785 $password1 = prompt
($question, -echo
=> '');
786 print "Please enter the same password share again to make sure it was typed correctly.\n";
787 my $password2 = prompt
($question, -echo
=> '');
788 if ($password1 eq $password2) {
789 $passwords_match = 1;
791 if (! $passwords_match) {
792 print "Passwords do not match, please try again!\n\n";
798 sub __string_is_in_array
{
801 my $entry = $arg_ref->{STRING
};
802 my @array = @
{$arg_ref->{ARRAY
}};
804 foreach my $elem (@array) {
805 if ($entry eq $elem) {
812 sub __resolve_alias
{
814 my $dbi = $arg_ref->{DBI
};
815 my $name = $arg_ref->{NAME
};
816 my $realm = $arg_ref->{REALM
};
818 my $alias = $dbi->first(
825 if (defined $alias) {
826 return $alias->{IDENTIFIER
};
833 ###########################################################################
835 my @options_spec = qw(
836 cfg|cfgfile|config|config=s
838 ); # config is the only global option
840 my $cmd = shift @ARGV || '';
842 if ($cmd eq 'certificate' || $cmd eq 'key') { # those have subcommands
843 $subcmd = shift @ARGV || '';
846 # set command/subcommand specific allowed options
847 if ($cmd eq 'initdb') {
848 push @options_spec, qw(
854 if ($cmd eq 'deploy') {
855 push @options_spec, qw(
863 if ($cmd eq 'key' || $cmd eq 'certificate') {
864 push @options_spec, qw(
869 if ($cmd eq 'key' && $subcmd eq 'generate') {
870 push @options_spec, qw(
875 if ($cmd eq 'key' && $subcmd eq 'import') {
876 push @options_spec, qw(
883 if ($cmd eq 'key' && $subcmd eq 'use') {
884 push @options_spec, qw(
890 if ($cmd eq 'certificate' && $subcmd eq 'import') {
891 push @options_spec, qw(
896 force-really-self-signed
897 force-issuer-not-found
898 force-certificate-already-exists
902 if ($cmd eq 'certificate' && $subcmd eq 'list') {
903 push @options_spec, qw(
909 if ($cmd eq 'certificate' && $subcmd eq 'alias') {
910 push @options_spec, qw(
913 force-certificate-not-found
917 if ($cmd eq 'certificate' && $subcmd eq 'remove') {
918 push @options_spec, qw(
924 if ($cmd eq 'certificate' && $subcmd eq 'chain') {
925 push @options_spec, qw(
929 force-certificate-not-found
930 force-issuer-certificate-not-found
936 GetOptions
(\
%params, @options_spec) or pod2usage
(-verbose
=> 0);
938 my ($vol, $dir, $file) = File
::Spec
->splitpath($0);
939 if ($cmd eq 'version') {
940 print "OpenXPKI Core Version: $OpenXPKI::VERSION::VERSION\n";
941 print "$file Version: $VERSION\n";
945 pod2usage
(-exitstatus
=> 0, -verbose
=> 2) if ($cmd eq 'man');
946 pod2usage
(-verbose
=> 99, -sections
=> 'NAME|USAGE') if ($cmd eq 'help');
948 print STDERR
"Usage: $file COMMAND [SUBCOMMAND] [OPTIONS]\n";
949 print STDERR
"Hint: '$file help'\n";
953 if (defined $params{debug
}) {
954 @
{$params{debug
}} = split(m{,}, join(',', @
{$params{debug
}}));
956 foreach my $param (@
{$params{debug
}}) {
957 my ($module, $level) = ($param =~ m{ \A (.*?):?(\d*) \z }xms);
962 # if modules are not specified, debug everything except for
963 # XML::Cache and XML::Config (if you really want to debug these,
964 # just use --debug .*:<debug level>)
965 $OpenXPKI::Debug
::LEVEL
{'.*'} = $level;
966 $OpenXPKI::Debug
::LEVEL
{'OpenXPKI::XML::Cache'} = 0;
967 $OpenXPKI::Debug
::LEVEL
{'OpenXPKI::XML::Config'} = 0;
970 print STDERR
"Debug level for module '$module': $level\n";
971 $OpenXPKI::Debug
::LEVEL
{$module} = $level;
976 require OpenXPKI
::FileUtils
;
977 require OpenXPKI
::Server
::Init
;
978 require OpenXPKI
::XML
::Config
;
979 require OpenXPKI
::Crypto
::Secret
;
980 require OpenXPKI
::DateTime
;
983 if ((($cmd eq 'certificate' || $cmd eq 'key') && $subcmd eq '') ||
984 (($cmd eq 'certificate' || $cmd eq 'key') && substr($subcmd, 0, 1) eq '-')
986 print STDERR
"Usage: openxpkiadm $cmd SUBCOMMAND [OPTIONS]\n";
990 if (defined $params{cfg
}) {
991 $configfile = $params{cfg
};
995 ###########################################################################
999 if ($cmd eq 'initdb') {
1000 if (! get_config
($configfile)) {
1001 print STDERR
"Could not obtain OpenXPKI instance configuration\n";
1006 DRYRUN
=> $params{dryrun
},
1007 FORCE
=> $params{force
},
1009 print STDERR
"Could not initialize database.\n";
1015 if ($cmd eq 'deploy') {
1016 # get arguments following --
1017 my @metaconf_opts = @ARGV;
1021 # first non-options argument is target prefix
1023 if ($params{prefix
}) {
1024 $dir = $params{prefix
};
1027 if (! defined $dir) {
1028 $dir = $config{prefix
};
1031 if ($params{templatedir
}) {
1032 $config{template_prefix
} = $params{templatedir
};
1035 my $template = $params{template
} || 'default';
1039 TEMPLATE_PREFIX
=> $config{template_prefix
},
1040 TEMPLATE
=> $template,
1041 TARGETDIR
=> File
::Spec
->rel2abs($dir),
1042 METACONF_OPTS
=> \
@metaconf_opts,
1043 FORCE
=> $params{force
},
1044 DRYRUN
=> $params{dryrun
},
1046 print STDERR
"Could not deploy OpenXPKI instance.\n";
1050 print STDERR
"OpenXPKI instance successfully deployed to $dir.\n";
1051 print STDERR
"You may now want to run\n\n";
1052 print STDERR
"cd $dir\n";
1053 print STDERR
"openxpki-configure\n";
1058 if ($cmd eq 'certificate') {
1059 if (! get_config
($configfile, qw( dbi_backend xml_config crypto_layer ))) {
1060 print STDERR
"Could not obtain OpenXPKI instance configuration\n";
1066 # FIXME: compare OpenXPKI::Server::Init, we use the first realm if none
1067 # is available, this is a completely arbitrary choice!
1068 my @realms = list_realms
();
1069 my $firstrealm = $realms[0];
1071 my $realm = $params{realm
};
1072 if (! defined $realm || $realm eq '') {
1073 $realm = $firstrealm;
1075 my $defaulttoken = CTX
('crypto_layer')->get_token(
1078 PKI_REALM
=> $realm,
1081 if (! defined $defaulttoken) {
1082 print STDERR
"ERROR: Could not get default token for specified realm\n";
1086 my $dbi = CTX
('dbi_backend');
1087 if (! defined $dbi) {
1088 print STDERR
"ERROR: Could not instantiate database backend\n";
1093 if ($subcmd eq 'list') {
1095 if (defined $params{realm
}) {
1096 push @realms, $params{realm
};
1099 @realms = list_realms
();
1100 push @realms, undef; # add the magic empty realm
1103 foreach my $realm (@realms) {
1104 if (defined $realm) {
1105 print "\nCertificates in $realm:\n";
1108 print "\nCertificates in self-signed pseudo-realm:\n";
1111 if (defined $params{all
}) {
1112 $certificates = $dbi->select(
1113 TABLE
=> 'CERTIFICATE',
1115 PKI_REALM
=> $realm,
1120 $certificates = $dbi->select(
1121 TABLE
=> [ 'ALIASES', 'CERTIFICATE' ],
1122 COLUMNS
=> [ 'ALIASES.ALIAS',
1123 'ALIASES.IDENTIFIER',
1124 'CERTIFICATE.SUBJECT',
1125 'CERTIFICATE.ISSUER_DN',
1126 'CERTIFICATE.CERTIFICATE_SERIAL',
1127 'CERTIFICATE.ISSUER_IDENTIFIER',
1129 'CERTIFICATE.EMAIL',
1130 'CERTIFICATE.STATUS',
1132 'CERTIFICATE.PUBKEY',
1133 'CERTIFICATE.SUBJECT_KEY_IDENTIFIER',
1134 'CERTIFICATE.AUTHORITY_KEY_IDENTIFIER',
1135 'CERTIFICATE.NOTAFTER',
1137 'CERTIFICATE.NOTBEFORE',
1138 'CERTIFICATE.CSR_SERIAL',
1146 'ALIASES.PKI_REALM' => $realm,
1150 for (my $i = 0; $i < scalar @
{$certificates}; $i++) {
1151 my $cert = $certificates->[$i];
1153 if (defined $params{all
}) { # look up aliases
1154 $identifier = $cert->{IDENTIFIER
};
1155 my $status = $cert->{STATUS
};
1156 if (defined $status && $status eq 'REVOKED') {
1157 print "\n Identifier: " . $cert->{IDENTIFIER
}
1161 print "\n Identifier: " . $cert->{IDENTIFIER
} . "\n";
1163 my $aliases = $dbi->select(
1166 IDENTIFIER
=> $cert->{IDENTIFIER
},
1169 for (my $j = 0; $j < scalar @
{$aliases}; $j++) {
1171 . $aliases->[$j]->{ALIAS
}
1172 . " (in realm: " . $aliases->[$j]->{PKI_REALM
}
1177 $identifier = $cert->{'ALIASES.IDENTIFIER'};
1178 my $status = $cert->{'CERTIFICATE.STATUS'};
1179 if (defined $status && $status eq 'REVOKED') {
1180 print "\n Identifier: "
1181 . $cert->{'ALIASES.IDENTIFIER'} . " (REVOKED)\n";
1184 print "\n Identifier: " . $cert->{'ALIASES.IDENTIFIER'}
1188 . $cert->{'ALIASES.ALIAS'} . "\n";
1191 if (!defined $params{all
}) {
1192 $prefix = 'CERTIFICATE.';
1194 if (defined $params{v
} && $params{v
} > 0) {
1195 # show subject and issuer dn
1196 my $subject = $cert->{$prefix . 'SUBJECT'};
1197 my $issuer_dn = $cert->{$prefix . 'ISSUER_DN'};
1198 print " Subject:\n " . $subject . "\n";
1199 print " Issuer DN:\n " . $issuer_dn . "\n";
1201 if (defined $params{v
} && $params{v
} > 1) {
1203 my $api = CTX
('api');
1204 my $chain = $api->get_chain({
1205 START_IDENTIFIER
=> $identifier,
1208 = join (' -> ', @
{$chain->{IDENTIFIERS
}});
1210 print " Chain: $chain_str ";
1211 if ($chain->{COMPLETE
} == 1) {
1212 print "(complete)\n";
1215 print "(INcomplete!)\n";
1218 if (defined $params{v
} && $params{v
} > 2) {
1219 # show database entry
1221 SUBJECT_KEY_IDENTIFIER
1222 AUTHORITY_KEY_IDENTIFIER
1233 if ($params{v
} > 3) {
1234 push @fields, qw(PUBKEY DATA);
1237 foreach my $field (@fields) {
1239 if (defined $cert->{$prefix . $field}) {
1240 $value = $cert->{$prefix . $field};
1254 if ($subcmd eq 'remove') {
1255 my $name = $params{name
};
1256 my $realm = $params{realm
};
1258 my $identifier = $name;
1259 if (defined $realm) {
1260 $identifier = __resolve_alias
({
1266 # check if certificate is issuer of something
1267 my $children_dbi = $dbi->select(
1268 TABLE
=> 'CERTIFICATE',
1270 ISSUER_IDENTIFIER
=> $identifier,
1273 my @children = @
{$children_dbi};
1276 if (scalar @children > 0) {
1278 if (scalar @children == 1) {
1279 if ($children[0]->{'ISSUER_IDENTIFIER'} eq $identifier) {
1280 # only self-signed certificate, delete even though
1281 # it formally is the issuer of a certificate in the DB
1287 if ($is_issuer && ! defined $params{'force-is-issuer'}) {
1288 print STDERR
"ERROR: Certificate not deleted because it is referenced as the issuer of " . scalar @children . " certificate(s) in the database.\n";
1291 my $certificate = $dbi->first(
1292 TABLE
=> 'CERTIFICATE',
1294 IDENTIFIER
=> $identifier,
1297 if (defined $certificate) {
1299 TABLE
=> 'CERTIFICATE',
1301 IDENTIFIER
=> $identifier,
1305 print "Successfully deleted certificate $name "
1306 . "(identifier: $identifier) from database.\n";
1310 print STDERR
"ERROR: Certificate $name "
1311 . "(identifier: $identifier) not found in database.\n";
1316 if ($subcmd eq 'import') {
1317 my $filename = $params{file
};
1319 if (! -r
$filename) {
1320 print STDERR
"ERROR: filename '$filename' is not readable\n";
1324 my $FileUtils = OpenXPKI
::FileUtils
->new();
1325 my $certdata = $FileUtils->read_file($filename);
1327 if (! defined $certdata) {
1328 print STDERR
"ERROR: Could not parse certificate data\n";
1332 if ((! defined $params{issuer
} || $params{issuer
} eq '')
1333 && (defined $params{realm
})) {
1334 print STDERR
"ERROR: You have to specify an issuer (or leave "
1335 . "out --realm for self-signed certificates).\n";
1339 my ($extracted_certdata) = $certdata =~ m{ \A .* (-----BEGIN\ CERTIFICATE----- .* -----END\ CERTIFICATE-----) .* \z}xms;
1341 my $cert = OpenXPKI
::Crypto
::X509
->new(
1342 TOKEN
=> $defaulttoken,
1343 DATA
=> $extracted_certdata,
1345 #### cert : Dumper($cert)
1348 my $issuer_identifier;
1349 if (! defined $params{realm
} || $params{realm
} eq '') {
1350 # user wants to import a self-signed
1351 # cert, let's check if it really is one
1352 ### subject key id : $cert->get_subject_key_id()
1353 ### authority key id : $cert->get_authority_key_id()
1354 if (defined $cert->get_subject_key_id()
1355 && defined $cert->get_authority_key_id()
1356 && ref $cert->get_authority_key_id() eq '' # TODO: check if hash
1357 && ($cert->get_subject_key_id()
1358 ne $cert->get_authority_key_id())) {
1359 if (! defined $params{'force-really-self-signed'}) {
1360 print STDERR
"ERROR: This is not a self-signed "
1362 . "(subject key id and authority key id do not match) "
1363 . "please specify --realm if you want to import a "
1364 . "normal certificate.\n";
1368 if ( $cert->{PARSED
}->{BODY
}->{SUBJECT
}
1369 ne $cert->{PARSED
}->{BODY
}->{ISSUER
}) {
1370 if (! defined $params{'force-really-self-signed'}) {
1371 print STDERR
"ERROR: This is not a self-signed "
1373 . "(subject and issuer do not match) "
1374 . "please specify --realm if you want to import a "
1375 . "normal certificate.\n";
1379 # we are our own issuer
1380 $issuer_identifier = $cert->get_identifier();
1382 else { # we have a "normal" certificate
1383 if (defined $params{'issuer-realm'}) {
1384 $realm = $params{'issuer-realm'};
1387 $realm = $params{realm
};
1389 # maybe the issuer name is an alias, try to resolve it
1390 $issuer_identifier = __resolve_alias
({
1392 NAME
=> $params{issuer
},
1395 ### issuer_identifier : $issuer_identifier
1396 # check whether the certificate is in the DB
1397 my $issuer = $dbi->first(
1398 TABLE
=> 'CERTIFICATE',
1400 IDENTIFIER
=> $issuer_identifier,
1403 if (! defined $issuer &&
1404 ! defined $params{'force-issuer-not-found'}) {
1405 print STDERR
"ERROR: Issuer '$params{issuer}' not found in "
1406 . "the database.\n";
1411 # make sure the self-signed realm is specified as 'undef'
1412 if (defined $realm && ($realm eq '')) {
1416 # compile all relevant data for the database
1417 # TODO: use $cert->to_db_hash();
1419 $insert_hash{STATUS
} = 'ISSUED';
1420 $insert_hash{PKI_REALM
} = $realm;
1421 $insert_hash{CERTIFICATE_SERIAL
} = $cert->get_serial();
1422 $insert_hash{IDENTIFIER
} = $cert->get_identifier();
1423 $insert_hash{DATA
} = $extracted_certdata;
1424 $insert_hash{SUBJECT
} = $cert->{PARSED
}->{BODY
}->{SUBJECT
};
1425 $insert_hash{ISSUER_DN
} = $cert->{PARSED
}->{BODY
}->{ISSUER
};
1426 $insert_hash{ISSUER_IDENTIFIER
} = $issuer_identifier;
1427 # combine email addresses
1428 if (exists $cert->{PARSED
}->{BODY
}->{EMAILADDRESSES
}) {
1429 $insert_hash{EMAIL
} = '';
1430 foreach my $email (@
{$cert->{PARSED
}->{BODY
}->{EMAILADDRESSES
}}) {
1431 $insert_hash{EMAIL
} .= "," if ($insert_hash{EMAIL
} ne '');
1432 $insert_hash{EMAIL
} .= $email;
1435 $insert_hash{PUBKEY
} = $cert->{PARSED
}->{BODY
}->{PUBKEY
};
1436 # set subject key id and authority key id, if defined.
1437 if (defined $cert->get_subject_key_id()) {
1438 $insert_hash{SUBJECT_KEY_IDENTIFIER
}
1439 = $cert->get_subject_key_id();
1441 if (defined $cert->get_authority_key_id() &&
1442 ref $cert->get_authority_key_id() eq '') {
1443 # TODO: do we save if authority key id is hash, and if
1444 # yes, in which format?
1445 $insert_hash{AUTHORITY_KEY_IDENTIFIER
}
1446 = $cert->get_authority_key_id();
1448 if (defined $params{role
} && $params{role
} ne '') {
1449 # TODO: check if role is valid (from acl.xml)
1450 $insert_hash{ROLE
} = $params{role
}
1453 $insert_hash{NOTAFTER
}
1454 = OpenXPKI
::DateTime
::convert_date
({
1455 DATE
=> $cert->{PARSED
}->{BODY
}->{NOTAFTER
},
1456 OUTFORMAT
=> 'epoch',
1458 $insert_hash{NOTBEFORE
}
1459 = OpenXPKI
::DateTime
::convert_date
({
1460 DATE
=> $cert->{PARSED
}->{BODY
}->{NOTBEFORE
},
1461 OUTFORMAT
=> 'epoch',
1463 # fields which are explicitly NOT set:
1464 # LOA (we don't know it)
1465 # CSR_SERIAL ( " " )
1467 # check whether there is already a certificate with the given
1468 # identifier anywhere
1469 my $certificate = $dbi->first(
1470 TABLE
=> 'CERTIFICATE',
1472 IDENTIFIER
=> $insert_hash{IDENTIFIER
},
1475 if (defined $certificate &&
1476 ! defined $params{'force-certificate-already-exists'}) {
1477 if ($certificate->{PKI_REALM
} ne '') {
1478 print STDERR
"ERROR: The same certificate already exists "
1479 . "in the $certificate->{PKI_REALM} realm. Use openxpkiadm "
1480 . "certificate alias to reference it.\n";
1483 print STDERR
"ERROR: The same certificate already exists "
1484 . "as a global self-signed certificate. Use openxpkiadm "
1485 . "certificate alias to reference it.\n";
1490 TABLE
=> 'CERTIFICATE', # use hash method
1491 HASH
=> \
%insert_hash,
1495 print "Successfully imported certificate into database:\n";
1496 print " Subject: " . $insert_hash{SUBJECT
} . "\n";
1497 print " Issuer: " . $insert_hash{ISSUER_DN
} . "\n";
1498 print " Identifier: " . $insert_hash{IDENTIFIER
} . "\n";
1502 if ($subcmd eq 'alias') {
1503 my %insert_hash = ();
1505 $insert_hash{PKI_REALM
} = $params{realm
};
1507 if (! exists($params{alias
}) || $params{alias
} eq '') {
1508 print STDERR
"Please specify an alias with --alias\n";
1512 $insert_hash{ALIAS
} = $params{alias
};
1514 if (! exists($params{identifier
}) || $params{identifier
} eq '') {
1515 print STDERR
"Please specify an identifier with --identifier\n";
1519 $insert_hash{IDENTIFIER
} = $params{identifier
};
1521 # TODO: check for --realm?
1523 # query certificate table to check whether --identifer actually exists
1524 my $certificate = $dbi->first(
1525 TABLE
=> 'CERTIFICATE',
1527 IDENTIFIER
=> $insert_hash{IDENTIFIER
},
1531 if (! defined $certificate &&
1532 ! defined $params{'force-certificate-not-found'}) { # there is no cert with given identifier
1533 print STDERR
"ERROR: Could not find a certificate with "
1534 . "identifier '$insert_hash{IDENTIFIER}', "
1535 . "are you sure it is correct?\n";
1538 #### insert_hash : Dumper(\%insert_hash)
1541 HASH
=> \
%insert_hash,
1544 print "Successfully created alias in realm $params{realm}:\n";
1545 print " Alias : $insert_hash{ALIAS}\n";
1546 print " Identifier: $insert_hash{IDENTIFIER}\n";
1550 if ($subcmd eq 'chain') {
1553 if (! exists($params{name
}) || $params{name
} eq '') {
1554 print STDERR
"Please specify a certificate name with --name\n";
1558 $cert_name = $params{name
};
1560 if (! exists($params{issuer
}) || $params{issuer
} eq '') {
1561 print STDERR
"Please specify an issuer name with --issuer\n";
1565 $issuer_name = $params{issuer
};
1568 # maybe the certificate name is an alias, try to resolve it
1569 my $cert_identifier = __resolve_alias
({
1572 REALM
=> $params{realm
},
1574 # check whether the certificate is in the DB
1575 my $certificate = $dbi->first(
1576 TABLE
=> 'CERTIFICATE',
1578 IDENTIFIER
=> $cert_identifier,
1579 PKI_REALM
=> $params{realm
}
1582 if (! defined $certificate &&
1583 ! defined $params{'force-certificate-not-found'}) {
1584 print STDERR
"ERROR: Certificate '$cert_name' not found in realm "
1585 . "$params{realm}.\n";
1589 my $issuer_identifier;
1590 # maybe the issuer name is an alias, try resolve it
1592 if (defined $params{'issuer-realm'}) {
1593 $realm = $params{'issuer-realm'};
1596 $realm = $params{realm
};
1598 $issuer_identifier = __resolve_alias
({
1600 NAME
=> $issuer_name,
1603 # check whether the issuer is in the DB
1604 my $issuer = $dbi->first(
1605 TABLE
=> 'CERTIFICATE',
1607 IDENTIFIER
=> $issuer_identifier,
1610 if (! defined $issuer &&
1611 ! defined $params{'force-issuer-certificate-not-found'}) {
1612 print STDERR
"ERROR: Issuer certificate '$issuer_name' "
1613 . "(identifier: $issuer_identifier) not found in database.\n";
1617 # set the issuer_identifier for the given certificate
1619 TABLE
=> 'CERTIFICATE',
1621 ISSUER_IDENTIFIER
=> $issuer_identifier,
1624 CERTIFICATE_SERIAL
=> $certificate->{CERTIFICATE_SERIAL
},
1625 IDENTIFIER
=> $cert_identifier,
1626 PKI_REALM
=> $certificate->{PKI_REALM
},
1630 print "Successfully set $issuer_name (identifier: $issuer_identifier) "
1631 . "as issuer of certificate $cert_name (identifier: "
1632 . "$cert_identifier).\n";
1633 # TODO: maybe don't warn only, but let the user use --force to
1634 # specify that he knows what he is doing ...?
1635 if ($issuer->{SUBJECT_KEY_IDENTIFIER
}
1636 ne $certificate->{AUTHORITY_KEY_IDENTIFIER
}) {
1637 print STDERR
"WARNING: The issuer's subject key identifier "
1638 . "extension ($issuer->{SUBJECT_KEY_IDENTIFIER}) does not "
1639 . "match the authority key identifier extension contained "
1640 . "in the certificate "
1641 . "($certificate->{AUTHORITY_KEY_IDENTIFIER}). Are you sure "
1642 . "your chain is correct?\n";
1644 if ($issuer->{SUBJECT
} ne $certificate->{ISSUER_DN
}) {
1645 print STDERR
"WARNING: The issuer's subject ($issuer->{SUBJECT}) "
1646 . "does not match the issuer DN contained in the certificate "
1647 . "($certificate->{ISSUER_DN}). Are you sure your chain is "
1653 print STDERR
"Unknown certificate subcommand '$subcmd'.\n";
1657 if ($cmd eq 'key') {
1658 if (! get_config
($configfile, qw( dbi_backend xml_config crypto_layer))) {
1659 print STDERR
"Could not obtain OpenXPKI instance configuration\n";
1663 my @pki_realms = list_realms
();
1664 if (! defined $params{realm
} ||
1665 ! grep {$params{realm
} eq $_} @pki_realms) {
1666 print STDERR
"Please specify one of the following PKI realms via --realm:\n";
1667 foreach my $realm (@pki_realms) {
1675 my $defaulttoken = CTX
('crypto_layer')->get_token(
1678 PKI_REALM
=> $params{realm
},
1681 if (! defined $defaulttoken) {
1682 print STDERR
"ERROR: Could not get default token for specified realm\n";
1686 if ($subcmd eq 'list') {
1689 REALM
=> $params{realm
},
1694 if ($subcmd eq 'use') {
1695 my $key_id = $params{id
};
1696 my $purpose = lc $params{purpose
};
1697 my $realm = $params{realm
};
1698 my $command = $params{command
};
1699 if (! defined $command) {
1700 $command = '/bin/sh';
1702 my $default_token = CTX
('crypto_layer')->get_token(
1704 PKI_REALM
=> $realm,
1707 if ($key_id eq '') {
1708 print STDERR
"Please specify a token ID with --id (see output of key list for a list of possible values).\n";
1711 if ($purpose eq '') {
1712 print STDERR
"Please specify a purpose (CA, SCEP, ...) with --purpose (see output of key list for a list of possible values).\n";
1714 my $config = OpenXPKI
::XML
::Config
->new(
1715 CONFIG
=> $configfile,
1718 my $nr_of_realms = $config->get_xpath_count(
1719 XPATH
=> [ 'pki_realm' ],
1724 for (my $i=0; $i < $nr_of_realms; $i++) {
1725 next if ($realm ne $config->get_xpath(
1726 XPATH
=> [ 'pki_realm', 'name' ],
1727 COUNTER
=> [ $i , 0 ]));
1731 my $nr_of_purpose_items = $config->get_xpath_count(
1732 XPATH
=> [ 'pki_realm', $purpose ],
1733 COUNTER
=> [ $realm_index ],
1736 for (my $i=0; $i < $nr_of_purpose_items; $i++) { # look for matching id
1737 next if ($key_id ne $config->get_xpath(
1738 XPATH
=> [ 'pki_realm' , $purpose, 'token', 'id' ],
1739 COUNTER
=> [ $realm_index, $i , 0 , 0 ],
1741 $purpose_index = $i;
1744 if (! defined($purpose_index)) {
1745 print STDERR
"Could not find configuration for this ID and purpose.\n";
1748 my @token_path = ( 'pki_realm' , $purpose , 'token' );
1749 my @token_counter = ( $realm_index, $purpose_index, 0 );
1752 $key_filename = $config->get_xpath(
1753 XPATH
=> [ @token_path , 'key' ],
1754 COUNTER
=> [ @token_counter, 0 ],
1758 print STDERR
"Could not read key filename from config file, "
1759 . "configuration error?\n";
1760 print STDERR
"$EVAL_ERROR\n";
1763 if (! -s
$key_filename) {
1764 print STDERR
"Key file does not exist or is empty\n";
1767 my $secret_group_name;
1769 $secret_group_name = $config->get_xpath(
1770 XPATH
=> [ @token_path , 'secret' ],
1771 COUNTER
=> [ @token_counter, 0 ],
1775 print STDERR
"Could not read secret group from config file, "
1776 . "configuration error?\n";
1777 print STDERR
"$EVAL_ERROR\n";
1781 my @secret_path = ( 'pki_realm' , 'common', 'secret' );
1782 my @secret_counter = ( $realm_index, 0 , 0 );
1783 my $secret_group_count;
1785 $secret_group_count = $config->get_xpath_count(
1786 XPATH
=> [ @secret_path , 'group' ],
1787 COUNTER
=> [ @secret_counter ],
1792 for (my $i = 0; $i < $secret_group_count; $i++) {
1793 my $group_id = $config->get_xpath(
1794 XPATH
=> [ @secret_path , 'group', 'id' ],
1795 COUNTER
=> [ @secret_counter, $i , 0 ],
1797 if ($group_id eq $secret_group_name) {
1799 last SEARCH_GROUP_ID
;
1802 if (! defined $group_index) {
1803 print STDERR
"Could not find configuration for secret group '$secret_group_name'.\n";
1806 my $secret_method = $config->get_xpath(
1807 XPATH
=> [ @secret_path , 'group' , 'method', 'id' ],
1808 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ],
1811 if ($secret_method ne 'literal') { # all others have total_shares
1812 my $quorum_n = $config->get_xpath(
1813 XPATH
=> [ @secret_path , 'group' , 'method', 'total_shares' ],
1814 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ],
1817 my $quorum_k = __get_required_shares
({
1819 TOKEN_PATH
=> [ @secret_path, 'group' ],
1820 TOKEN_COUNTER
=> [ @secret_counter, $group_index ],
1822 if (! defined $quorum_k) { # if it is not defined, n is
1824 $quorum_k = $quorum_n;
1826 my $display_secret_method = $secret_method;
1827 $display_secret_method .= ' (n = ' . $quorum_n . ', k = '
1829 print 'Secret method: ' . $display_secret_method . "\n";
1830 my $secret_method = ucfirst($secret_method);
1831 my $secret = OpenXPKI
::Crypto
::Secret
->new({
1832 TYPE
=> $secret_method,
1837 TOKEN
=> $default_token,
1840 while (! $secret->is_complete()) {
1841 print "Secret not yet complete, please enter a share.\n";
1843 if ($secret_method eq 'Plain') {
1844 $part = prompt
"Share number: ";
1846 my $share = prompt
('Share: ');
1847 my $share2 = prompt
('Share (again): ');
1848 if ($share ne $share2) {
1849 print "Shares input do not match, please try again!\n";
1853 if ($secret_method eq 'Plain') {
1854 $secret->set_secret({
1859 elsif ($secret_method eq 'Split') {
1860 $secret->set_secret("$share");
1863 for (my $j = 0; $j < 500; $j++) {
1864 print "\n"; # pseudo clearscreen
1867 $passphrase = $secret->get_secret();
1870 $passphrase = $config->get_xpath(
1871 XPATH
=> [ @secret_path , 'group' , 'method' ],
1872 COUNTER
=> [ @secret_counter, $group_index, 0 ],
1875 $ENV{'passphrase'} = $passphrase;
1876 $ENV{'keyfile'} = $key_filename;
1877 delete $ENV{'OPENSSL_CONF'};
1881 if ($subcmd eq 'import') {
1882 my $key_id = $params{id
};
1883 my $purpose = lc $params{purpose
};
1884 my $realm = $params{realm
};
1885 my $import_filename = $params{file
};
1887 if ($key_id eq '') {
1888 print STDERR
"Please specify a token ID with --id (see output of key list for a list of possible values).\n";
1891 if ($purpose eq '') {
1892 print STDERR
"Please specify a purpose (CA, SCEP, ...) with --purpose (see output of key list for a list of possible values).\n";
1894 my $config = OpenXPKI
::XML
::Config
->new(
1895 CONFIG
=> $configfile,
1898 my $nr_of_realms = $config->get_xpath_count(
1899 XPATH
=> [ 'pki_realm' ],
1904 for (my $i=0; $i < $nr_of_realms; $i++) {
1905 next if ($realm ne $config->get_xpath(
1906 XPATH
=> [ 'pki_realm', 'name' ],
1907 COUNTER
=> [ $i , 0 ]));
1911 my $nr_of_purpose_items = $config->get_xpath_count(
1912 XPATH
=> [ 'pki_realm', $purpose ],
1913 COUNTER
=> [ $realm_index ],
1916 for (my $i=0; $i < $nr_of_purpose_items; $i++) { # look for matching id
1917 next if ($key_id ne $config->get_xpath(
1918 XPATH
=> [ 'pki_realm' , $purpose, 'token', 'id' ],
1919 COUNTER
=> [ $realm_index, $i , 0 , 0 ],
1921 $purpose_index = $i;
1924 if (! defined($purpose_index)) {
1925 print STDERR
"Could not find configuration for this ID and purpose.\n";
1928 my @token_path = ( 'pki_realm' , $purpose , 'token' );
1929 my @token_counter = ( $realm_index, $purpose_index, 0 );
1932 $key_filename = $config->get_xpath(
1933 XPATH
=> [ @token_path , 'key' ],
1934 COUNTER
=> [ @token_counter, 0 ],
1938 print STDERR
"Could not read key filename from config file, "
1939 . "configuration error?\n";
1940 print STDERR
"$EVAL_ERROR\n";
1943 if (-s
$key_filename) {
1944 print STDERR
"Key file is non-empty, cowardly refusing to create "
1949 my $fu = OpenXPKI
::FileUtils
->new();
1950 $key = $fu->read_file($import_filename);
1953 my $key_path = $key_filename;
1954 $key_path =~ s/(.*)\/.*/$1/; # perl is greedy, so this is the path
1955 if (! -d
$key_path) { # key path does not yet exist, create it
1956 eval { # try to create
1960 print STDERR
"Could not create key directory: $key_path";
1965 FILENAME
=> $key_filename,
1968 if (-s
$key_filename) { # key file exists and is nonzero
1969 print "Key successfully written to $key_filename\n";
1973 print STDERR
"Key import failed.\n";
1979 if ($subcmd eq 'generate') {
1980 my $secret_group = $params{group
};
1981 if ($secret_group eq '') {
1982 print STDERR
"Please specify a secret group with --group (see output of key list for a list of possible values).\n";
1985 my $rc = generate_keys
({
1986 REALM
=> $params{realm
},
1987 GROUP
=> $secret_group,
1992 print STDERR
"Unknown key subcommand '$subcmd'.\n";
1996 print STDERR
"Unknown command '$cmd'.\n";
2003 openxpkiadm - tool for management operations of OpenXPKI instances
2007 openxpkiadm COMMAND [SUBCOMMAND] [OPTIONS]
2010 --config FILE use configuration from FILE
2013 help brief help message
2014 man full documentation
2015 version print program version and exit
2016 deploy Deploy a new OpenXPKI installation
2017 initdb Initialize database
2019 certificate Manage certificates
2029 --prefix DIR Use specified prefix during deployment
2030 --templatedir DIR Use specified directory as base directory for
2032 --template TEMPLATE Use specified template (defaults to 'default')
2033 --force Force operation (may be destructive)
2036 Creates a new OpenXPKI server configuration file set below the specified
2037 prefix directory (defaults to [% dir.prefix %]
2038 if no other directory is specified via --prefix).
2039 This command will not overwrite existing configuration files unless --force
2042 If the --templatedir argument is given the specified directory is used
2043 as template base directory.
2045 If --template is specified, its argument is used instead of 'default' for
2046 the source of the templates used.
2048 All options following -- are literally passed to openxpki-metaconf during
2056 --force Force operation (may be destructive)
2057 --dryrun Don't change anything, just print what would
2060 Initializes the OpenXPKI database schema. Will not destroy existing data
2061 unless called with --force.
2065 Key generation for OpenXPKI Tokens (including issuing CAs and subsystems).
2069 --realm PKI Realm to operate on
2071 =head3 key management subcommands
2077 Shows token key information for the specified realm, including
2078 key algorithm, key length and secret splitting information.
2080 Lists keys together with a status flag, which can be one of the
2083 + - key exists and file is non-empty
2084 0 - key exists but file is empty
2085 ! - key files does not exist (yet)
2089 openxpkiadm key list --realm 'Root CA'
2095 --realm PKI Realm to operate on
2096 --group The secret group to generate keys for
2098 Generates asymmetric key pairs for the given secret group. The command
2099 will use the secret splitting method specified in the token
2100 configuration. For valid secret groups, see the output of key list.
2102 The command will refuse to overwrite an existing key.
2104 If multiple secret password parts are configured for the specified key,
2105 the key generation routine will automatically create the configured
2106 number of password secret parts.
2108 This command only supports key generation in software.
2109 For HSM protected keys please refer to the HSM product documentation
2110 regarding key generation with the particular product.
2114 openxpkiadm key generate --realm 'Root CA' --group default
2120 --realm PKI Realm to operate on
2121 --purpose The purpose of the key (e.g. CA, SCEP)
2122 --id The name of the configured key
2123 --file The file to import the key from
2125 This command allows to import a key that has been generated using
2126 a different method then using openxpkiadm key generate. It effectively
2127 copies the key file to the location given in the config file. The options
2128 are the same as in key generate, except for --file, which gives the
2129 location of the externally generated key.
2135 --realm PKI Realm to operate on
2136 --purpose The purpose of the key (e.g. CA, SCEP)
2137 --id The name of the configured key
2138 --command The command to start (defaults to '/bin/sh')
2140 This command asks for the needed key passphrase shares and exports the
2141 recovered passphrase into the passphrase environment variable as well
2142 as the key file location into the keyfile environment variable. Typical
2143 usage is to create a certificate request for a secret-splitted key:
2145 openxpkiadm key use --realm 'Root CA' --purpose ca --id testdummyca1 \
2146 --command 'openssl req -new -passin env:passphrase -key $keyfile'
2152 Starts a certificate management command and allows to list, install,
2153 delete and connect certificates for the configured PKI Realms.
2155 openxpkiadm certificate <subcommand> <options>
2157 =head3 certificate management subcommands
2163 Subcommand options (optional):
2165 --realm PKI realm to operate on
2166 --all Show all certificates
2167 -v Show subject and issuer DN as well
2168 -v -v Show chain as well
2169 -v -v -v Show (nearly complete) database entry
2170 -v -v -v -v Show pubkey and certificate data, too
2172 Lists certificates present in the database for
2173 the specified realm. If --all is not specified, only certificates
2174 that have an alias defined for them are listed. --all lists all
2175 certificates, regardless of whether they have an alias or not.
2176 If --realm is left out, the certificates in all realms are listed
2177 The number of -v's increases the verbosity (see above for what is
2178 listed in which case).
2185 --realm PKI realm to import certificate to
2186 --file the PEM file to import from
2187 --issuer the issuer alias or identifier
2190 --issuer-realm the realm where the issuer alias
2192 --role the role of the certificate owner
2194 Force options (use only if you exactly now what you are doing!):
2195 --force-really-self-signed
2196 The certificate is really self-signed
2197 --force-issuer-not-found
2198 Don't care that the issuer is not in the database
2199 --force-certificate-already-exists
2200 Don't care that the certificate is already in database
2202 Once again, only use these options if you actually have to (the
2203 occasions where this happens should be really, really rare).
2205 Adds a certificate to the database. There are two different ways to
2206 call it, depending on whether you have a self-signed certificate
2207 or not. With a self-signed certificate, the --realm and --issuer options
2208 are left out, with a "normal" certificate, they are mandatory.
2210 The command outputs the subject's DN and the issuer's DN for you to
2211 verify that you imported the correct certificate as well as a unique
2212 identifier which can be used to globally reference the certificate
2213 (i.e. for configuration or as an issuer). If you don't want to remember
2214 the identifier, look into openxpkiadm certificate alias to find out
2215 how to create a symbolic name for an identifier.
2219 openxpkiadm certificate import --file cacert.pem
2221 Imports a self-signed CA certificate.
2223 openxpkiadm certificate import --realm 'Root CA' \
2224 --file subca1.pem --issuer 'Root CA 1'
2226 Imports a Sub CA certificate which is signed by Root CA 1.
2233 --name The alias or identifier of the certificate
2236 --realm The PKI realm in which the alias is defined
2238 Force options (use only if you now what you are doing!):
2239 --force-is-issuer Delete certificate even though it is the
2240 issuer of another certificate in the database
2242 Removes a certificate from the database.
2246 openxpkiadm certificate remove --realm 'Root CA' \
2254 --realm PKI realm to create the alias in
2255 --alias The symbolic name for the certificate
2256 --identifier The identifier of the certificate
2258 Force options (use only if you now what you are doing!):
2259 --force-certificate-not-found
2260 Ignore that the certificate for which to create an
2261 alias was not found in the DB
2263 Only use these options if you actually have to (the occasions where
2264 this happens should be really, really rare).
2266 Using openxpkiadm certificate alias, you can create a symbolic name
2267 for a certificate, which is associated with a specific PKI realm.
2268 This symbolic name can then be used in some of the openxpkiadm commands
2269 as well as in the configuration files.
2273 openxpkiadm certificate alias --realm 'Root CA' \
2274 --identifier FpzZptRsa/444Acs/Nrdmo1Fo1s --alias 'root1'
2281 --realm The PKI realm to operate in
2282 --name The alias or identifier of the child
2283 --issuer The alias or identifier of the parent
2286 --issuer-realm The realm in which the issuer alias
2289 Force options (use only if you now what you are doing!):
2290 --force-certificate-not-found
2291 Ignore that the certificate of the child was not found
2293 --force-issuer-certificate-not-found
2294 Ignore that the certificate of the parent was not found
2297 Once again, only use these options if you actually have to (the
2298 occasions where this happens should be really, really rare).
2300 Specifies subject/issuer relationship in order to set up certificate
2301 chains. The certificates to be connected must already be present in
2302 the database (see B<import>). As those connections are already set up
2303 during --import, this command exists for changing the issuer if you
2304 made an error. It also allows to specify an issuer that does not
2305 agree with the information contained in the certificate (but outputs
2310 openxpkiadm certificate chain --realm 'Root CA' \
2311 --name 'Subordinate CA 1' --issuer 'root1'
2317 =item B<--config FILE>
2319 Read configuration file FILE. Uses built-in default if not specified.
2323 Force execution of command.
2325 WARNING: This may destroy existing data!
2329 Prints effects of a command without actually modifying anything.
2333 Specify deployment prefix for deploy command.
2335 =item B<--templatedir>
2337 Specify template directory to use for configuration files.
2341 Specify template to use during deployment.
2347 B<openxpkiadm> is the administrative frontend for controlling the OpenXPKI
2352 NOTE: This script was customized to the paths specified during
2354 You will have to modify this script to reflect any changes to the
2355 installation directories.
2357 The openxpkiadm script returns a 0 exit value on success, and >0 if an