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";
1340 = OpenXPKI
::Crypto
::X509
->new(TOKEN
=> $defaulttoken,
1342 #### cert : Dumper($cert)
1345 my $issuer_identifier;
1346 if (! defined $params{realm
} || $params{realm
} eq '') {
1347 # user wants to import a self-signed
1348 # cert, let's check if it really is one
1349 ### subject key id : $cert->get_subject_key_id()
1350 ### authority key id : $cert->get_authority_key_id()
1351 if (defined $cert->get_subject_key_id()
1352 && defined $cert->get_authority_key_id()
1353 && ref $cert->get_authority_key_id() eq '' # TODO: check if hash
1354 && ($cert->get_subject_key_id()
1355 ne $cert->get_authority_key_id())) {
1356 if (! defined $params{'force-really-self-signed'}) {
1357 print STDERR
"ERROR: This is not a self-signed "
1359 . "(subject key id and authority key id do not match) "
1360 . "please specify --realm if you want to import a "
1361 . "normal certificate.\n";
1365 if ( $cert->{PARSED
}->{BODY
}->{SUBJECT
}
1366 ne $cert->{PARSED
}->{BODY
}->{ISSUER
}) {
1367 if (! defined $params{'force-really-self-signed'}) {
1368 print STDERR
"ERROR: This is not a self-signed "
1370 . "(subject and issuer do not match) "
1371 . "please specify --realm if you want to import a "
1372 . "normal certificate.\n";
1376 # we are our own issuer
1377 $issuer_identifier = $cert->get_identifier();
1379 else { # we have a "normal" certificate
1380 if (defined $params{'issuer-realm'}) {
1381 $realm = $params{'issuer-realm'};
1384 $realm = $params{realm
};
1386 # maybe the issuer name is an alias, try to resolve it
1387 $issuer_identifier = __resolve_alias
({
1389 NAME
=> $params{issuer
},
1392 ### issuer_identifier : $issuer_identifier
1393 # check whether the certificate is in the DB
1394 my $issuer = $dbi->first(
1395 TABLE
=> 'CERTIFICATE',
1397 IDENTIFIER
=> $issuer_identifier,
1400 if (! defined $issuer &&
1401 ! defined $params{'force-issuer-not-found'}) {
1402 print STDERR
"ERROR: Issuer '$params{issuer}' not found in "
1403 . "the database.\n";
1408 # make sure the self-signed realm is specified as 'undef'
1409 if (defined $realm && ($realm eq '')) {
1413 # compile all relevant data for the database
1414 # TODO: use $cert->to_db_hash();
1416 $insert_hash{STATUS
} = 'ISSUED';
1417 $insert_hash{PKI_REALM
} = $realm;
1418 $insert_hash{CERTIFICATE_SERIAL
} = $cert->get_serial();
1419 $insert_hash{IDENTIFIER
} = $cert->get_identifier();
1420 $insert_hash{DATA
} = $certdata;
1421 $insert_hash{SUBJECT
} = $cert->{PARSED
}->{BODY
}->{SUBJECT
};
1422 $insert_hash{ISSUER_DN
} = $cert->{PARSED
}->{BODY
}->{ISSUER
};
1423 $insert_hash{ISSUER_IDENTIFIER
} = $issuer_identifier;
1424 # combine email addresses
1425 if (exists $cert->{PARSED
}->{BODY
}->{EMAILADDRESSES
}) {
1426 $insert_hash{EMAIL
} = '';
1427 foreach my $email (@
{$cert->{PARSED
}->{BODY
}->{EMAILADDRESSES
}}) {
1428 $insert_hash{EMAIL
} .= "," if ($insert_hash{EMAIL
} ne '');
1429 $insert_hash{EMAIL
} .= $email;
1432 $insert_hash{PUBKEY
} = $cert->{PARSED
}->{BODY
}->{PUBKEY
};
1433 # set subject key id and authority key id, if defined.
1434 if (defined $cert->get_subject_key_id()) {
1435 $insert_hash{SUBJECT_KEY_IDENTIFIER
}
1436 = $cert->get_subject_key_id();
1438 if (defined $cert->get_authority_key_id() &&
1439 ref $cert->get_authority_key_id() eq '') {
1440 # TODO: do we save if authority key id is hash, and if
1441 # yes, in which format?
1442 $insert_hash{AUTHORITY_KEY_IDENTIFIER
}
1443 = $cert->get_authority_key_id();
1445 if (defined $params{role
} && $params{role
} ne '') {
1446 # TODO: check if role is valid (from acl.xml)
1447 $insert_hash{ROLE
} = $params{role
}
1450 $insert_hash{NOTAFTER
}
1451 = OpenXPKI
::DateTime
::convert_date
({
1452 DATE
=> $cert->{PARSED
}->{BODY
}->{NOTAFTER
},
1453 OUTFORMAT
=> 'epoch',
1455 $insert_hash{NOTBEFORE
}
1456 = OpenXPKI
::DateTime
::convert_date
({
1457 DATE
=> $cert->{PARSED
}->{BODY
}->{NOTBEFORE
},
1458 OUTFORMAT
=> 'epoch',
1460 # fields which are explicitly NOT set:
1461 # LOA (we don't know it)
1462 # CSR_SERIAL ( " " )
1464 # check whether there is already a certificate with the given
1465 # identifier anywhere
1466 my $certificate = $dbi->first(
1467 TABLE
=> 'CERTIFICATE',
1469 IDENTIFIER
=> $insert_hash{IDENTIFIER
},
1472 if (defined $certificate &&
1473 ! defined $params{'force-certificate-already-exists'}) {
1474 if ($certificate->{PKI_REALM
} ne '') {
1475 print STDERR
"ERROR: The same certificate already exists "
1476 . "in the $certificate->{PKI_REALM} realm. Use openxpkiadm "
1477 . "certificate alias to reference it.\n";
1480 print STDERR
"ERROR: The same certificate already exists "
1481 . "as a global self-signed certificate. Use openxpkiadm "
1482 . "certificate alias to reference it.\n";
1487 TABLE
=> 'CERTIFICATE', # use hash method
1488 HASH
=> \
%insert_hash,
1492 print "Successfully imported certificate into database:\n";
1493 print " Subject: " . $insert_hash{SUBJECT
} . "\n";
1494 print " Issuer: " . $insert_hash{ISSUER_DN
} . "\n";
1495 print " Identifier: " . $insert_hash{IDENTIFIER
} . "\n";
1499 if ($subcmd eq 'alias') {
1500 my %insert_hash = ();
1502 $insert_hash{PKI_REALM
} = $params{realm
};
1504 if (! exists($params{alias
}) || $params{alias
} eq '') {
1505 print STDERR
"Please specify an alias with --alias\n";
1509 $insert_hash{ALIAS
} = $params{alias
};
1511 if (! exists($params{identifier
}) || $params{identifier
} eq '') {
1512 print STDERR
"Please specify an identifier with --identifier\n";
1516 $insert_hash{IDENTIFIER
} = $params{identifier
};
1518 # TODO: check for --realm?
1520 # query certificate table to check whether --identifer actually exists
1521 my $certificate = $dbi->first(
1522 TABLE
=> 'CERTIFICATE',
1524 IDENTIFIER
=> $insert_hash{IDENTIFIER
},
1528 if (! defined $certificate &&
1529 ! defined $params{'force-certificate-not-found'}) { # there is no cert with given identifier
1530 print STDERR
"ERROR: Could not find a certificate with "
1531 . "identifier '$insert_hash{IDENTIFIER}', "
1532 . "are you sure it is correct?\n";
1535 #### insert_hash : Dumper(\%insert_hash)
1538 HASH
=> \
%insert_hash,
1541 print "Successfully created alias in realm $params{realm}:\n";
1542 print " Alias : $insert_hash{ALIAS}\n";
1543 print " Identifier: $insert_hash{IDENTIFIER}\n";
1547 if ($subcmd eq 'chain') {
1550 if (! exists($params{name
}) || $params{name
} eq '') {
1551 print STDERR
"Please specify a certificate name with --name\n";
1555 $cert_name = $params{name
};
1557 if (! exists($params{issuer
}) || $params{issuer
} eq '') {
1558 print STDERR
"Please specify an issuer name with --issuer\n";
1562 $issuer_name = $params{issuer
};
1565 # maybe the certificate name is an alias, try to resolve it
1566 my $cert_identifier = __resolve_alias
({
1569 REALM
=> $params{realm
},
1571 # check whether the certificate is in the DB
1572 my $certificate = $dbi->first(
1573 TABLE
=> 'CERTIFICATE',
1575 IDENTIFIER
=> $cert_identifier,
1576 PKI_REALM
=> $params{realm
}
1579 if (! defined $certificate &&
1580 ! defined $params{'force-certificate-not-found'}) {
1581 print STDERR
"ERROR: Certificate '$cert_name' not found in realm "
1582 . "$params{realm}.\n";
1586 my $issuer_identifier;
1587 # maybe the issuer name is an alias, try resolve it
1589 if (defined $params{'issuer-realm'}) {
1590 $realm = $params{'issuer-realm'};
1593 $realm = $params{realm
};
1595 $issuer_identifier = __resolve_alias
({
1597 NAME
=> $issuer_name,
1600 # check whether the issuer is in the DB
1601 my $issuer = $dbi->first(
1602 TABLE
=> 'CERTIFICATE',
1604 IDENTIFIER
=> $issuer_identifier,
1607 if (! defined $issuer &&
1608 ! defined $params{'force-issuer-certificate-not-found'}) {
1609 print STDERR
"ERROR: Issuer certificate '$issuer_name' "
1610 . "(identifier: $issuer_identifier) not found in database.\n";
1614 # set the issuer_identifier for the given certificate
1616 TABLE
=> 'CERTIFICATE',
1618 ISSUER_IDENTIFIER
=> $issuer_identifier,
1621 CERTIFICATE_SERIAL
=> $certificate->{CERTIFICATE_SERIAL
},
1622 IDENTIFIER
=> $cert_identifier,
1623 PKI_REALM
=> $certificate->{PKI_REALM
},
1627 print "Successfully set $issuer_name (identifier: $issuer_identifier) "
1628 . "as issuer of certificate $cert_name (identifier: "
1629 . "$cert_identifier).\n";
1630 # TODO: maybe don't warn only, but let the user use --force to
1631 # specify that he knows what he is doing ...?
1632 if ($issuer->{SUBJECT_KEY_IDENTIFIER
}
1633 ne $certificate->{AUTHORITY_KEY_IDENTIFIER
}) {
1634 print STDERR
"WARNING: The issuer's subject key identifier "
1635 . "extension ($issuer->{SUBJECT_KEY_IDENTIFIER}) does not "
1636 . "match the authority key identifier extension contained "
1637 . "in the certificate "
1638 . "($certificate->{AUTHORITY_KEY_IDENTIFIER}). Are you sure "
1639 . "your chain is correct?\n";
1641 if ($issuer->{SUBJECT
} ne $certificate->{ISSUER_DN
}) {
1642 print STDERR
"WARNING: The issuer's subject ($issuer->{SUBJECT}) "
1643 . "does not match the issuer DN contained in the certificate "
1644 . "($certificate->{ISSUER_DN}). Are you sure your chain is "
1650 print STDERR
"Unknown certificate subcommand '$subcmd'.\n";
1654 if ($cmd eq 'key') {
1655 if (! get_config
($configfile, qw( dbi_backend xml_config crypto_layer))) {
1656 print STDERR
"Could not obtain OpenXPKI instance configuration\n";
1660 my @pki_realms = list_realms
();
1661 if (! defined $params{realm
} ||
1662 ! grep {$params{realm
} eq $_} @pki_realms) {
1663 print STDERR
"Please specify one of the following PKI realms via --realm:\n";
1664 foreach my $realm (@pki_realms) {
1672 my $defaulttoken = CTX
('crypto_layer')->get_token(
1675 PKI_REALM
=> $params{realm
},
1678 if (! defined $defaulttoken) {
1679 print STDERR
"ERROR: Could not get default token for specified realm\n";
1683 if ($subcmd eq 'list') {
1686 REALM
=> $params{realm
},
1691 if ($subcmd eq 'use') {
1692 my $key_id = $params{id
};
1693 my $purpose = lc $params{purpose
};
1694 my $realm = $params{realm
};
1695 my $command = $params{command
};
1696 if (! defined $command) {
1697 $command = '/bin/sh';
1699 my $default_token = CTX
('crypto_layer')->get_token(
1701 PKI_REALM
=> $realm,
1704 if ($key_id eq '') {
1705 print STDERR
"Please specify a token ID with --id (see output of key list for a list of possible values).\n";
1708 if ($purpose eq '') {
1709 print STDERR
"Please specify a purpose (CA, SCEP, ...) with --purpose (see output of key list for a list of possible values).\n";
1711 my $config = OpenXPKI
::XML
::Config
->new(
1712 CONFIG
=> $configfile,
1715 my $nr_of_realms = $config->get_xpath_count(
1716 XPATH
=> [ 'pki_realm' ],
1721 for (my $i=0; $i < $nr_of_realms; $i++) {
1722 next if ($realm ne $config->get_xpath(
1723 XPATH
=> [ 'pki_realm', 'name' ],
1724 COUNTER
=> [ $i , 0 ]));
1728 my $nr_of_purpose_items = $config->get_xpath_count(
1729 XPATH
=> [ 'pki_realm', $purpose ],
1730 COUNTER
=> [ $realm_index ],
1733 for (my $i=0; $i < $nr_of_purpose_items; $i++) { # look for matching id
1734 next if ($key_id ne $config->get_xpath(
1735 XPATH
=> [ 'pki_realm' , $purpose, 'token', 'id' ],
1736 COUNTER
=> [ $realm_index, $i , 0 , 0 ],
1738 $purpose_index = $i;
1741 if (! defined($purpose_index)) {
1742 print STDERR
"Could not find configuration for this ID and purpose.\n";
1745 my @token_path = ( 'pki_realm' , $purpose , 'token' );
1746 my @token_counter = ( $realm_index, $purpose_index, 0 );
1749 $key_filename = $config->get_xpath(
1750 XPATH
=> [ @token_path , 'key' ],
1751 COUNTER
=> [ @token_counter, 0 ],
1755 print STDERR
"Could not read key filename from config file, "
1756 . "configuration error?\n";
1757 print STDERR
"$EVAL_ERROR\n";
1760 if (! -s
$key_filename) {
1761 print STDERR
"Key file does not exist or is empty\n";
1764 my $secret_group_name;
1766 $secret_group_name = $config->get_xpath(
1767 XPATH
=> [ @token_path , 'secret' ],
1768 COUNTER
=> [ @token_counter, 0 ],
1772 print STDERR
"Could not read secret group from config file, "
1773 . "configuration error?\n";
1774 print STDERR
"$EVAL_ERROR\n";
1778 my @secret_path = ( 'pki_realm' , 'common', 'secret' );
1779 my @secret_counter = ( $realm_index, 0 , 0 );
1780 my $secret_group_count;
1782 $secret_group_count = $config->get_xpath_count(
1783 XPATH
=> [ @secret_path , 'group' ],
1784 COUNTER
=> [ @secret_counter ],
1789 for (my $i = 0; $i < $secret_group_count; $i++) {
1790 my $group_id = $config->get_xpath(
1791 XPATH
=> [ @secret_path , 'group', 'id' ],
1792 COUNTER
=> [ @secret_counter, $i , 0 ],
1794 if ($group_id eq $secret_group_name) {
1796 last SEARCH_GROUP_ID
;
1799 if (! defined $group_index) {
1800 print STDERR
"Could not find configuration for secret group '$secret_group_name'.\n";
1803 my $secret_method = $config->get_xpath(
1804 XPATH
=> [ @secret_path , 'group' , 'method', 'id' ],
1805 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ],
1808 if ($secret_method ne 'literal') { # all others have total_shares
1809 my $quorum_n = $config->get_xpath(
1810 XPATH
=> [ @secret_path , 'group' , 'method', 'total_shares' ],
1811 COUNTER
=> [ @secret_counter, $group_index, 0 , 0 ],
1814 my $quorum_k = __get_required_shares
({
1816 TOKEN_PATH
=> [ @secret_path, 'group' ],
1817 TOKEN_COUNTER
=> [ @secret_counter, $group_index ],
1819 if (! defined $quorum_k) { # if it is not defined, n is
1821 $quorum_k = $quorum_n;
1823 my $display_secret_method = $secret_method;
1824 $display_secret_method .= ' (n = ' . $quorum_n . ', k = '
1826 print 'Secret method: ' . $display_secret_method . "\n";
1827 my $secret_method = ucfirst($secret_method);
1828 my $secret = OpenXPKI
::Crypto
::Secret
->new({
1829 TYPE
=> $secret_method,
1834 TOKEN
=> $default_token,
1837 while (! $secret->is_complete()) {
1838 print "Secret not yet complete, please enter a share.\n";
1840 if ($secret_method eq 'Plain') {
1841 $part = prompt
"Share number: ";
1843 my $share = prompt
('Share: ');
1844 my $share2 = prompt
('Share (again): ');
1845 if ($share ne $share2) {
1846 print "Shares input do not match, please try again!\n";
1850 if ($secret_method eq 'Plain') {
1851 $secret->set_secret({
1856 elsif ($secret_method eq 'Split') {
1857 $secret->set_secret("$share");
1860 for (my $j = 0; $j < 500; $j++) {
1861 print "\n"; # pseudo clearscreen
1864 $passphrase = $secret->get_secret();
1867 $passphrase = $config->get_xpath(
1868 XPATH
=> [ @secret_path , 'group' , 'method' ],
1869 COUNTER
=> [ @secret_counter, $group_index, 0 ],
1872 $ENV{'passphrase'} = $passphrase;
1873 $ENV{'keyfile'} = $key_filename;
1874 delete $ENV{'OPENSSL_CONF'};
1878 if ($subcmd eq 'import') {
1879 my $key_id = $params{id
};
1880 my $purpose = lc $params{purpose
};
1881 my $realm = $params{realm
};
1882 my $import_filename = $params{file
};
1884 if ($key_id eq '') {
1885 print STDERR
"Please specify a token ID with --id (see output of key list for a list of possible values).\n";
1888 if ($purpose eq '') {
1889 print STDERR
"Please specify a purpose (CA, SCEP, ...) with --purpose (see output of key list for a list of possible values).\n";
1891 my $config = OpenXPKI
::XML
::Config
->new(
1892 CONFIG
=> $configfile,
1895 my $nr_of_realms = $config->get_xpath_count(
1896 XPATH
=> [ 'pki_realm' ],
1901 for (my $i=0; $i < $nr_of_realms; $i++) {
1902 next if ($realm ne $config->get_xpath(
1903 XPATH
=> [ 'pki_realm', 'name' ],
1904 COUNTER
=> [ $i , 0 ]));
1908 my $nr_of_purpose_items = $config->get_xpath_count(
1909 XPATH
=> [ 'pki_realm', $purpose ],
1910 COUNTER
=> [ $realm_index ],
1913 for (my $i=0; $i < $nr_of_purpose_items; $i++) { # look for matching id
1914 next if ($key_id ne $config->get_xpath(
1915 XPATH
=> [ 'pki_realm' , $purpose, 'token', 'id' ],
1916 COUNTER
=> [ $realm_index, $i , 0 , 0 ],
1918 $purpose_index = $i;
1921 if (! defined($purpose_index)) {
1922 print STDERR
"Could not find configuration for this ID and purpose.\n";
1925 my @token_path = ( 'pki_realm' , $purpose , 'token' );
1926 my @token_counter = ( $realm_index, $purpose_index, 0 );
1929 $key_filename = $config->get_xpath(
1930 XPATH
=> [ @token_path , 'key' ],
1931 COUNTER
=> [ @token_counter, 0 ],
1935 print STDERR
"Could not read key filename from config file, "
1936 . "configuration error?\n";
1937 print STDERR
"$EVAL_ERROR\n";
1940 if (-s
$key_filename) {
1941 print STDERR
"Key file is non-empty, cowardly refusing to create "
1946 my $fu = OpenXPKI
::FileUtils
->new();
1947 $key = $fu->read_file($import_filename);
1950 my $key_path = $key_filename;
1951 $key_path =~ s/(.*)\/.*/$1/; # perl is greedy, so this is the path
1952 if (! -d
$key_path) { # key path does not yet exist, create it
1953 eval { # try to create
1957 print STDERR
"Could not create key directory: $key_path";
1962 FILENAME
=> $key_filename,
1965 if (-s
$key_filename) { # key file exists and is nonzero
1966 print "Key successfully written to $key_filename\n";
1970 print STDERR
"Key import failed.\n";
1976 if ($subcmd eq 'generate') {
1977 my $secret_group = $params{group
};
1978 if ($secret_group eq '') {
1979 print STDERR
"Please specify a secret group with --group (see output of key list for a list of possible values).\n";
1982 my $rc = generate_keys
({
1983 REALM
=> $params{realm
},
1984 GROUP
=> $secret_group,
1989 print STDERR
"Unknown key subcommand '$subcmd'.\n";
1993 print STDERR
"Unknown command '$cmd'.\n";
2000 openxpkiadm - tool for management operations of OpenXPKI instances
2004 openxpkiadm COMMAND [SUBCOMMAND] [OPTIONS]
2007 --config FILE use configuration from FILE
2010 help brief help message
2011 man full documentation
2012 version print program version and exit
2013 deploy Deploy a new OpenXPKI installation
2014 initdb Initialize database
2016 certificate Manage certificates
2026 --prefix DIR Use specified prefix during deployment
2027 --templatedir DIR Use specified directory as base directory for
2029 --template TEMPLATE Use specified template (defaults to 'default')
2030 --force Force operation (may be destructive)
2033 Creates a new OpenXPKI server configuration file set below the specified
2034 prefix directory (defaults to [% dir.prefix %]
2035 if no other directory is specified via --prefix).
2036 This command will not overwrite existing configuration files unless --force
2039 If the --templatedir argument is given the specified directory is used
2040 as template base directory.
2042 If --template is specified, its argument is used instead of 'default' for
2043 the source of the templates used.
2045 All options following -- are literally passed to openxpki-metaconf during
2053 --force Force operation (may be destructive)
2054 --dryrun Don't change anything, just print what would
2057 Initializes the OpenXPKI database schema. Will not destroy existing data
2058 unless called with --force.
2062 Key generation for OpenXPKI Tokens (including issuing CAs and subsystems).
2066 --realm PKI Realm to operate on
2068 =head3 key management subcommands
2074 Shows token key information for the specified realm, including
2075 key algorithm, key length and secret splitting information.
2077 Lists keys together with a status flag, which can be one of the
2080 + - key exists and file is non-empty
2081 0 - key exists but file is empty
2082 ! - key files does not exist (yet)
2086 openxpkiadm key list --realm 'Root CA'
2092 --realm PKI Realm to operate on
2093 --group The secret group to generate keys for
2095 Generates asymmetric key pairs for the given secret group. The command
2096 will use the secret splitting method specified in the token
2097 configuration. For valid secret groups, see the output of key list.
2099 The command will refuse to overwrite an existing key.
2101 If multiple secret password parts are configured for the specified key,
2102 the key generation routine will automatically create the configured
2103 number of password secret parts.
2105 This command only supports key generation in software.
2106 For HSM protected keys please refer to the HSM product documentation
2107 regarding key generation with the particular product.
2111 openxpkiadm key generate --realm 'Root CA' --group default
2117 --realm PKI Realm to operate on
2118 --purpose The purpose of the key (e.g. CA, SCEP)
2119 --id The name of the configured key
2120 --file The file to import the key from
2122 This command allows to import a key that has been generated using
2123 a different method then using openxpkiadm key generate. It effectively
2124 copies the key file to the location given in the config file. The options
2125 are the same as in key generate, except for --file, which gives the
2126 location of the externally generated key.
2132 --realm PKI Realm to operate on
2133 --purpose The purpose of the key (e.g. CA, SCEP)
2134 --id The name of the configured key
2135 --command The command to start (defaults to '/bin/sh')
2137 This command asks for the needed key passphrase shares and exports the
2138 recovered passphrase into the passphrase environment variable as well
2139 as the key file location into the keyfile environment variable. Typical
2140 usage is to create a certificate request for a secret-splitted key:
2142 openxpkiadm key use --realm 'Root CA' --purpose ca --id testdummyca1 \
2143 --command 'openssl req -new -passin env:passphrase -key $keyfile'
2149 Starts a certificate management command and allows to list, install,
2150 delete and connect certificates for the configured PKI Realms.
2152 openxpkiadm certificate <subcommand> <options>
2154 =head3 certificate management subcommands
2160 Subcommand options (optional):
2162 --realm PKI realm to operate on
2163 --all Show all certificates
2164 -v Show subject and issuer DN as well
2165 -v -v Show chain as well
2166 -v -v -v Show (nearly complete) database entry
2167 -v -v -v -v Show pubkey and certificate data, too
2169 Lists certificates present in the database for
2170 the specified realm. If --all is not specified, only certificates
2171 that have an alias defined for them are listed. --all lists all
2172 certificates, regardless of whether they have an alias or not.
2173 If --realm is left out, the certificates in all realms are listed
2174 The number of -v's increases the verbosity (see above for what is
2175 listed in which case).
2182 --realm PKI realm to import certificate to
2183 --file the PEM file to import from
2184 --issuer the issuer alias or identifier
2187 --issuer-realm the realm where the issuer alias
2189 --role the role of the certificate owner
2191 Force options (use only if you exactly now what you are doing!):
2192 --force-really-self-signed
2193 The certificate is really self-signed
2194 --force-issuer-not-found
2195 Don't care that the issuer is not in the database
2196 --force-certificate-already-exists
2197 Don't care that the certificate is already in database
2199 Once again, only use these options if you actually have to (the
2200 occasions where this happens should be really, really rare).
2202 Adds a certificate to the database. There are two different ways to
2203 call it, depending on whether you have a self-signed certificate
2204 or not. With a self-signed certificate, the --realm and --issuer options
2205 are left out, with a "normal" certificate, they are mandatory.
2207 The command outputs the subject's DN and the issuer's DN for you to
2208 verify that you imported the correct certificate as well as a unique
2209 identifier which can be used to globally reference the certificate
2210 (i.e. for configuration or as an issuer). If you don't want to remember
2211 the identifier, look into openxpkiadm certificate alias to find out
2212 how to create a symbolic name for an identifier.
2216 openxpkiadm certificate import --file cacert.pem
2218 Imports a self-signed CA certificate.
2220 openxpkiadm certificate import --realm 'Root CA' \
2221 --file subca1.pem --issuer 'Root CA 1'
2223 Imports a Sub CA certificate which is signed by Root CA 1.
2230 --name The alias or identifier of the certificate
2233 --realm The PKI realm in which the alias is defined
2235 Force options (use only if you now what you are doing!):
2236 --force-is-issuer Delete certificate even though it is the
2237 issuer of another certificate in the database
2239 Removes a certificate from the database.
2243 openxpkiadm certificate remove --realm 'Root CA' \
2251 --realm PKI realm to create the alias in
2252 --alias The symbolic name for the certificate
2253 --identifier The identifier of the certificate
2255 Force options (use only if you now what you are doing!):
2256 --force-certificate-not-found
2257 Ignore that the certificate for which to create an
2258 alias was not found in the DB
2260 Only use these options if you actually have to (the occasions where
2261 this happens should be really, really rare).
2263 Using openxpkiadm certificate alias, you can create a symbolic name
2264 for a certificate, which is associated with a specific PKI realm.
2265 This symbolic name can then be used in some of the openxpkiadm commands
2266 as well as in the configuration files.
2270 openxpkiadm certificate alias --realm 'Root CA' \
2271 --identifier FpzZptRsa/444Acs/Nrdmo1Fo1s --alias 'root1'
2278 --realm The PKI realm to operate in
2279 --name The alias or identifier of the child
2280 --issuer The alias or identifier of the parent
2283 --issuer-realm The realm in which the issuer alias
2286 Force options (use only if you now what you are doing!):
2287 --force-certificate-not-found
2288 Ignore that the certificate of the child was not found
2290 --force-issuer-certificate-not-found
2291 Ignore that the certificate of the parent was not found
2294 Once again, only use these options if you actually have to (the
2295 occasions where this happens should be really, really rare).
2297 Specifies subject/issuer relationship in order to set up certificate
2298 chains. The certificates to be connected must already be present in
2299 the database (see B<import>). As those connections are already set up
2300 during --import, this command exists for changing the issuer if you
2301 made an error. It also allows to specify an issuer that does not
2302 agree with the information contained in the certificate (but outputs
2307 openxpkiadm certificate chain --realm 'Root CA' \
2308 --name 'Subordinate CA 1' --issuer 'root1'
2314 =item B<--config FILE>
2316 Read configuration file FILE. Uses built-in default if not specified.
2320 Force execution of command.
2322 WARNING: This may destroy existing data!
2326 Prints effects of a command without actually modifying anything.
2330 Specify deployment prefix for deploy command.
2332 =item B<--templatedir>
2334 Specify template directory to use for configuration files.
2338 Specify template to use during deployment.
2344 B<openxpkiadm> is the administrative frontend for controlling the OpenXPKI
2349 NOTE: This script was customized to the paths specified during
2351 You will have to modify this script to reflect any changes to the
2352 installation directories.
2354 The openxpkiadm script returns a 0 exit value on success, and >0 if an