Fork of TinyCA2 because of discontinued upstream
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
tinyca/lib/CA.pm

1431 lines
40 KiB

# Copyright (c) Stephan Martin <sm@sm-zone.net>
#
# $Id: CA.pm,v 1.9 2006/06/28 21:50:41 sm Exp $
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
use strict;
package CA;
use POSIX;
sub new {
my $that = shift;
my $self = {};
my $class = ref($that) || $that;
$self->{'init'} = shift;
if(not -d $self->{'init'}->{'basedir'}) {
print "create basedir: $self->{'init'}->{'basedir'}\n";
mkdir($self->{'init'}->{'basedir'}, 0700);
}
if(not -d $self->{'init'}->{'tmpdir'}) {
print "create temp dir: $self->{'init'}->{'tmpdir'}\n";
mkdir($self->{'init'}->{'tmpdir'}, 0700);
}
opendir(DIR, $self->{'init'}->{'basedir'}) || do {
print _("error: can't open basedir: ").$!;
exit(1);
};
$self->{'calist'} = [];
while(my $ca = readdir(DIR)) {
chomp($ca);
next if $ca eq ".";
next if $ca eq "..";
next if $ca eq "tmp";
my $dir = $self->{'init'}->{'basedir'}."/".$ca;
next unless -d $dir;
next unless -s $dir."/cacert.pem";
next unless -s $dir."/cacert.key";
push(@{$self->{'calist'}}, $ca);
@{$self->{'calist'}} = sort(@{$self->{'calist'}});
$self->{$ca}->{'dir'} = $dir;
$self->{$ca}->{'cnf'} = $dir."/openssl.cnf";
}
closedir(DIR);
bless($self, $class);
}
#
# see if the ca can be opened without asking the user
# or show the open dialog
#
sub get_open_name {
my ($self, $main, $opts) = @_;
my ($ind);
if((not defined($opts->{'name'})) || ($opts->{'name'} eq "")) {
# if only one CA is defined, open it without prompting
if($#{$self->{'calist'}} == 0) {
$opts->{'name'} = $self->{'calist'}->[0];
$self->open_ca($main, $opts);
} else {
$main->show_select_ca_dialog('open');
}
}
}
#
# open the ca with the given name
#
sub open_ca {
my ($self, $main, $opts, $box) = @_;
$box->destroy() if(defined($box));
GUI::HELPERS::set_cursor($main, 1);
my ($i, $cnf, @lines, $oldca, $index, $bak, $t);
main::printd("Opening ca $opts->{'name'}");
GUI::HELPERS::set_status($main, _(" Opening CA: ").$opts->{'name'});
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
if(!exists($self->{$opts->{'name'}})) {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Invalid CA selected"));
return;
}
# selected CA is already open
if ((defined($self->{'actca'})) &&
($opts->{'name'} eq $self->{'actca'})) {
GUI::HELPERS::set_cursor($main, 0);
print STDERR "DEBUG: ca $opts->{'name'} already opened\n";
return;
}
$self->{'actca'} = $opts->{'name'};
$self->{'cadir'} = $self->{$opts->{'name'}}->{'dir'};
$main->{'cadir'} = $self->{'cadir'};
if(my $dir = HELPERS::get_export_dir($main)) {
$main->{'exportdir'} = $dir;
}
# read per CA configuration
$self->{'cfg'} = HELPERS::read_cfg($self->{'cadir'});
main::printd("Request-Type: " . ($self->{'cfg'}->{global}{default_req_type} // 'none'));
main::printd("Default bits server: " . ($self->{'cfg'}->{server}{default_bits} // 'none'));
main::printd("Default bits user: " . ($self->{'cfg'}->{user}{default_bits} // 'none'));
main::printd("Default key cipher: " . ($self->{'cfg'}->{user}{default_cipher} // 'none'));
# update config (necessary for update from old tinyca)
$cnf = $self->{$opts->{'name'}}->{'cnf'};
open(IN, "<$cnf");
@lines = <IN>;
close(IN);
for($i = 0; $lines[$i]; $i++) {
$lines[$i] =~ s/private\/cakey.pem/cacert.key/;
}
open(OUT, ">$cnf");
print OUT @lines;
close(OUT);
$main->{'mw'}->set_title( "Tiny CA Management $main->{'version'}".
" - $self->{'actca'}"
);
$main->{'CERT'}->{'lastread'} = 0;
$main->{'REQ'}->{'lastread'} = 0;
$main->{'KEY'}->{'lastread'} = 0;
delete($main->{'OpenSSL'}->{'CACHE'});
delete($main->{'CERT'}->{'OpenSSL'}->{'CACHE'});
delete($main->{'REQ'}->{'OpenSSL'}->{'CACHE'});
delete($main->{'OpenSSL'});
GUI::HELPERS::set_status($main, _(" Initializing OpenSSL"));
$main->{'OpenSSL'} = OpenSSL->new(
$main->{'init'}->{'opensslbin'}, $main->{'tmpdir'});
$index = $self->{'cadir'}."/index.txt";
GUI::HELPERS::set_status($main, _(" Check for CA Version"));
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
open(INDEX, "+<$index") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_error(_("Can't open index file: ".$!));
return;
};
while(<INDEX>) {
if(/Email=/) {
$oldca = 1;
last;
}
}
close(INDEX);
# offer CA conversion for old CAs and openssl >= 0.9.7
if($oldca && ($main->{'OpenSSL'}->{'version'} eq "0.9.7") &&
!$opts->{'noconv'} && !$opts->{'doconv'}) {
GUI::HELPERS::set_status($main, _(" Convert CA"));
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
$self->{'actca'} = undef;
GUI::HELPERS::set_cursor($main, 0);
$main->show_ca_convert_dialog($opts);
return;
}
if($opts->{'doconv'}) {
open(INDEX, "+<$index") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_error(_("Can't open index file: ".$!));
return;
};
$bak = $index.".bak";
open(BAK, "+>$bak") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_error(_("Can't open index backup: ").$!);
return;
};
seek(INDEX, 0, 0);
while(<INDEX>) {
print BAK;
}
seek(INDEX, 0, 0);
truncate(INDEX, 0);
seek(BAK, 0, 0);
while(<BAK>) {
$_ =~ s/Email=/emailAddress=/;
print INDEX;
}
close(INDEX);
close(BAK);
$t = _("This CA is converted for openssl 0.9.7x now.");
$t .= "\n";
$t .= _("You will find a backup copy of the index file at: ");
$t .= $bak;
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_info($t);
}
GUI::HELPERS::set_cursor($main, 1);
GUI::HELPERS::set_status($main, _(" Read Configuration"));
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
$main->{'TCONFIG'}->init_config($main, $opts->{'name'});
GUI::HELPERS::set_status($main, _(" Create GUI"));
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
$main->create_mframe(1);
GUI::HELPERS::set_status($main, _(" Create Toolbar"));
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
$main->create_toolbar('ca');
GUI::HELPERS::set_status($main, _(" Actual CA: ").$self->{'actca'});
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
GUI::HELPERS::set_cursor($main, 0);
$main->{'nb'}->set_current_page(0);
return;
}
#
# get name for deleting a CA
#
sub get_ca_delete {
my ($self, $main, $name) = @_;
if(!defined($name)) {
$main->show_select_ca_dialog('delete');
return;
}elsif(!exists($self->{$name})) {
$main->show_select_ca_dialog('delete');
GUI::HELPERS::print_warning(_("Invalid CA selected"));
return;
}else {
$self->delete_ca($main, $name);
}
return;
}
#
# delete given CA
#
sub delete_ca {
my ($self, $main, $name, $box) = @_;
my ($ind, @tmp, $t);
$box->destroy() if(defined($box));
GUI::HELPERS::set_cursor($main, 1);
_rm_dir($self->{$name}->{'dir'});
if((defined($self->{'actca'})) &&
($name eq $self->{'actca'})) {
$self->{'actca'} = undef;
}
$main->{'cabox'}->destroy() if(defined($main->{'cabox'}));
delete($main->{'cabox'});
$main->{'reqbox'}->destroy() if(defined($main->{'reqbox'}));
delete($main->{'reqbox'});
$main->{'keybox'}->destroy() if(defined($main->{'keybox'}));
delete($main->{'keybox'});
$main->{'certbox'}->destroy() if(defined($main->{'certbox'}));
delete($main->{'certbox'});
for(my $i = 0; $i < 4; $i++) {
$main->{'nb'}->remove_page($i);
}
delete($main->{'reqbrowser'});
delete($main->{'certbrowser'});
delete($main->{'REQ'}->{'reqlist'});
delete($main->{'CERT'}->{'certlist'});
foreach(@{$self->{'calist'}}) {
next if $_ eq $name;
push(@tmp, $_);
}
$self->{'calist'} = \@tmp;
delete($self->{$name});
$main->create_mframe();
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("CA: %s deleted"), $name);
GUI::HELPERS::print_info($t);
return;
}
#
# check if all data for creating a ca is available
#
sub get_ca_create {
my ($self, $main, $opts, $box, $mode) = @_;
$box->destroy() if(defined($box));
my ($action, $index, $serial, $t, $parsed);
if(not(defined($opts))) {
$opts = {};
$opts->{'days'} = 3650; # set default to 10 years
$opts->{'bits'} = 4096;
$opts->{'digest'} = 'sha256';
if(defined($mode) && $mode eq "sub") { # create SubCA, use defaults
$opts->{'parentca'} = $main->{'CA'}->{'actca'};
$parsed = $main->{'CERT'}->parse_cert($main, 'CA');
if(defined $parsed->{'C'}) {
$opts->{'C'} = $parsed->{'C'};
}
if(defined $parsed->{'ST'}) {
$opts->{'ST'} = $parsed->{'ST'};
}
if(defined $parsed->{'L'}) {
$opts->{'L'} = $parsed->{'L'};
}
if(defined $parsed->{'O'}) {
$opts->{'O'} = $parsed->{'O'};
}
if(defined $parsed->{'OU'}) {
my $cc = 0;
foreach my $ou (@{$parsed->{'OU'}}) {
$opts->{'OU'}->[$cc++] = $ou;
}
}
}
$main->show_ca_dialog($opts, $mode);
return;
}
if(defined($mode) && $mode eq "sub") {
if(not defined($opts->{'parentpw'})) {
$main->show_ca_dialog($opts, $mode);
GUI::HELPERS::print_warning(
_("Password of parent CA is needed for creating a Sub CA"));
return;
}
}
if((not defined($opts->{'name'})) ||
($opts->{'name'} eq "") ||
($opts->{'name'} =~ /\s/)) {
$main->show_ca_dialog($opts, $mode);
GUI::HELPERS::print_warning(_("Name must be filled in and must")
._(" not contain Spaces"));
return;
}
if((not defined($opts->{'C'})) ||
($opts->{'C'} eq "") ||
(not defined($opts->{'CN'})) ||
($opts->{'CN'} eq "") ||
(not defined($opts->{'passwd'})) ||
($opts->{'passwd'} eq "")) {
$main->show_ca_dialog($opts, $mode);
GUI::HELPERS::print_warning(
_("Please specify at least Common Name, ")
._("Country and Password"));
return;
}
if((not defined($opts->{'passwd2'})) ||
$opts->{'passwd'} ne $opts->{'passwd2'}) {
$main->show_ca_dialog($opts, $mode);
GUI::HELPERS::print_warning(_("Passwords don't match"));
return;
}
$opts->{'C'} = uc($opts->{'C'});
if(length($opts->{'C'}) != 2) {
$main->show_ca_dialog($opts, $mode);
GUI::HELPERS::print_warning(_("Country must be exact 2 letter code"));
return;
}
$t = sprintf(_("CA: %s already exists"), $opts->{'name'});
if(defined($self->{$opts->{'name'}})) {
$main->show_ca_dialog($opts, $mode);
GUI::HELPERS::print_warning($t);
return;
}
# warn "call create_ca_env with bits: $opts->{'bits'}\n";
$self->create_ca_env($main, $opts, $mode);
return;
}
#
# check if all data for importing a CA is available
#
sub get_ca_import {
my ($self, $main, $opts, $box) = @_;
$box->destroy() if(defined($box));
my ($name, $t, $parsed, $constr);
if(!(defined($opts))) {
$opts = {};
$opts->{'days'} = 3650; # set default to 10 years
$opts->{'bits'} = 4096;
$opts->{'digest'} = 'sha256';
$main->show_ca_import_dialog($opts);
return;
}
# check options given in dialog
if((not defined($opts->{'name'})) ||
($opts->{'name'} eq "") ||
($opts->{'name'} =~ /\s/)) {
$main->show_ca_import_dialog($opts);
GUI::HELPERS::print_warning(
_("Name for storage must be filled in and must not contain spaces"));
return;
}
if(((not defined($opts->{'passwd'})) ||
($opts->{'passwd'} eq '')) &&
(!$opts->{'pwwarning'})) {
$main->show_ca_import_dialog($opts);
GUI::HELPERS::print_warning(
_("You didn't give a password for the private CA key.").
"\n".
_("The import will fail, if the key is encrypted."));
$opts->{'pwwarning'} = 1;
return;
}
if((not defined($opts->{'newpasswd'})) ||
($opts->{'newpasswd'} eq '')) {
$main->show_ca_import_dialog($opts);
GUI::HELPERS::print_warning(
_("Please give a new password for the CA"));
return;
}
if((not defined($opts->{'newpasswd2'})) ||
$opts->{'newpasswd'} ne $opts->{'newpasswd2'}) {
$main->show_ca_import_dialog($opts);
GUI::HELPERS::print_warning(_("New passwords don't match"));
return;
}
if((not defined($opts->{'cacertfile'})) ||
($opts->{'cacertfile'} eq '')) {
$main->show_ca_import_dialog($opts);
GUI::HELPERS::print_warning(
_("Please give a CA certificate to import"));
return;
}
if(not -r $opts->{'cacertfile'}) {
$main->show_ca_import_dialog($opts);
$t = sprintf(_("Can't read CA certificate file:\n%s"),
$opts->{'cacertfile'});
GUI::HELPERS::print_warning($t);
return;
}
if((not defined($opts->{'cakeyfile'})) ||
($opts->{'cakeyfile'} eq '')) {
$main->show_ca_import_dialog($opts);
GUI::HELPERS::print_warning(
_("Please give a CA keyfile to import"));
return;
}
if(not -r $opts->{'cakeyfile'}) {
$main->show_ca_import_dialog($opts);
$t = sprintf(_("Can't read CA key file:\n%s"),
$opts->{'cakeyfile'});
GUI::HELPERS::print_warning($t);
return;
}
if(((not defined($opts->{'indexfile'})) ||
($opts->{'indexfile'} eq '')) &&
(not defined($opts->{'indexwarning'}))) {
$main->show_ca_import_dialog($opts);
$t = _("Please give an Index file to import.\n");
$t .= _("If you don't have an Index file, i'll try to generate one.\n");
$t .= _("Attention: This will cause all Certificates to show up as valid.\n");
$t .= _("Attention: Revoked Certificates will not be determined.");
$opts->{'indexwarning'} = 1;
GUI::HELPERS::print_warning($t);
return;
}
if(defined($opts->{'indexfile'}) &&
$opts->{'indexfile'} ne '' &&
not -r $opts->{'indexfile'}) {
$main->show_ca_import_dialog($opts);
$t = sprintf(_("Can't read Index file:\n%s"),
$opts->{'indexfile'});
GUI::HELPERS::print_warning($t);
return;
} elsif(defined($opts->{'indexfile'}) &&
$opts->{'indexfile'} ne '') {
$opts->{'gotindex'} = 1;
}
if((not defined($opts->{'certdir'})) ||
($opts->{'certdir'} eq '')) {
$main->show_ca_import_dialog($opts);
GUI::HELPERS::print_warning(
_("Please give a directory containing the certificates to import"));
return;
}
if(not -d $opts->{'certdir'}) {
$main->show_ca_import_dialog($opts);
$t = sprintf(_("Can't find certificate directory:\n%s"),
$opts->{'certdir'});
GUI::HELPERS::print_warning($t);
return;
}
$name = $opts->{'name'};
if(defined($self->{$name})) {
$main->show_ca_import_dialog($opts);
$t = sprintf(
_("CA: %s already exists. Please choose another name"),
$name);
GUI::HELPERS::print_warning($t);
return;
}
# check ca certificate and key
$parsed = $main->{'OpenSSL'}->parsecert(
undef, undef, $opts->{'cacertfile'}, 1);
# check if it's really a CA certificate
if(defined($parsed->{'EXT'}->{'X509v3 Basic Constraints: critical'})) {
$constr = $parsed->{'EXT'}->{'X509v3 Basic Constraints: critical'}->[0];
} elsif(defined($parsed->{'EXT'}->{'X509v3 Basic Constraints'})) {
$constr = $parsed->{'EXT'}->{'X509v3 Basic Constraints'}->[0];
} else {
$t = _("Can't find X509v3 Basic Constraints in CA Certificate\n");
$t .= _("Import canceled");
GUI::HELPERS::print_warning($t);
return;
}
if($constr !~ /CA:TRUE/i) {
$t = _("The selected CA Certificate is no valid CA certificate\n");
$t .= sprintf(_("X509v3 Basic Constraint is set to: %s"), $constr);
GUI::HELPERS::print_warning($t);
return;
}
$opts->{'cacertdata'} = $parsed->{'PEM'};
# now read the data from the files
if(defined($opts->{'gotindex'})) {
open(INDEX, "<$opts->{'indexfile'}") || do {
$t = sprintf(_("Can't open Index file:\n%s"),
$opts->{'indexfile'});
GUI::HELPERS::print_warning($t);
return;
};
while(<INDEX>) {
$opts->{'serial'} = hex((split(/\t/, $_))[3]);
$opts->{'indexdata'} .= $_;
}
close(INDEX);
$opts->{'serial'} +=1;
$opts->{'serial'} = sprintf("%x", $opts->{'serial'});
}
$main->show_import_verification("cacert", $opts, $parsed);
return;
}
#
# do the real import
#
sub import_ca {
my ($self, $main, $opts, $box) = @_;
my ($t, $f, $cacertfile, $cakeyfile, $certfile, $c, $p, @files, $ext, $ret,
@d, $timestring, $indexline, $index, $serial, $subjects, $serials,
$timestrings);
my $format = "DER";
my $data = {};
my $ca = $opts->{'name'};
if (hex($opts->{'serial'}) < 1) {
$opts->{'serial'} = "01";
}
if(defined($box)) {
$box->destroy();
}
$opts->{'cakeydata'} = $main->{'KEY'}->key_change_passwd(
$main, $opts->{'cakeyfile'}, $opts->{'passwd'},
$opts->{'newpasswd'}, 'ca');
if($opts->{'cakeydata'} eq 1) {
return;
}
$self->create_ca_env($main, $opts, 'import');
# now read all certificates
opendir(DIR, $opts->{'certdir'}) || do {
$t = sprintf(_("Can't open Certificate directory: %s"),
$opts->{'certdir'});
GUI::HELPERS::print_warning($t);
return;
};
# just count the files
while($f = readdir(DIR)) {
next if($f =~ /^\./);
$certfile = $opts->{'certdir'}."/".$f;
push (@files, $certfile);
$c++;
}
GUI::HELPERS::set_cursor($main, 1);
# import all the certificate files and gather information if necessary
$main->{'barbox'}->pack_start($main->{'progress'}, 0, 0, 0);
foreach $certfile (@files) {
$t = sprintf(_(" Read Certificate: %s"), $certfile);
GUI::HELPERS::set_status($main, $t);
$p += 100/$c;
$main->{'progress'}->set_fraction($p/100);
while(Gtk2->events_pending) {
Gtk2->main_iteration;
}
open(IN, "<$certfile") || do {
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("Can't read Certificate file: %s"), $certfile);
return;
};
$data->{'raw'} = '';
$data->{'raw'} .= $_ while(<IN>);
close(IN);
$format = "PEM" if($data->{'raw'} =~ /BEGIN CERTIFICATE/);
if($format eq "PEM") {
$data->{'PEM'} = $data->{'raw'};
}
$data->{'parsed'} = $main->{'OpenSSL'}->parsecert(
undef, undef, $certfile, 1
);
$data->{'name'} = HELPERS::gen_name($data->{'parsed'});
$data->{'name'} = HELPERS::enc_base64($data->{'name'});
$data->{'name'} .= ".pem";
$data->{'file'} = $self->{$ca}->{'dir'}."/certs/".$data->{'name'};
open(OUT, ">$data->{'file'}") || do {
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("Can't write Certificate file: %s"),
$data->{'file'});
return;
};
print OUT $data->{'PEM'};
if(not defined($opts->{'gotindex'})) {
# get information for index.txt file
@d = localtime($data->{'parsed'}->{'EXPDATE'});
$timestring = sprintf("%02d%02d%02d%02d%02d%02dZ",
$d[5]%100, $d[4]+1, $d[3], $d[2], $d[1], $d[0]);
# try to detect index clashes FIXME: only the newer is kept
if(exists($subjects->{$data->{'parsed'}->{'SUBJECT'}})) {
if(hex($data->{'parsed'}->{'SERIAL'}) >=
hex($serials->{$data->{'parsed'}->{'SUBJECT'}})) {
$subjects->{$data->{'parsed'}->{'SUBJECT'}} = 1;
$serials->{$data->{'parsed'}->{'SUBJECT'}} =
$data->{'parsed'}->{'SERIAL'};
$timestrings->{$data->{'parsed'}->{'SUBJECT'}} =
$timestring;
}
} else {
$subjects->{$data->{'parsed'}->{'SUBJECT'}} = 1;
$serials->{$data->{'parsed'}->{'SUBJECT'}} =
$data->{'parsed'}->{'SERIAL'};
$timestrings->{$data->{'parsed'}->{'SUBJECT'}} =
$timestring;
}
# get information for serial file
if(hex($data->{'parsed'}->{'SERIAL'}) >= hex($opts->{'serial'})) {
$opts->{'serial'} = sprintf("%x", hex($data->{'parsed'}->{'SERIAL'}));
}
$opts->{'serial'} = hex($opts->{'serial'}) + 1;
$opts->{'serial'} = sprintf("%x", $opts->{'serial'});
}
close(OUT);
}
# now build the indexdata
foreach my $s (keys(%$subjects)) {
$indexline = "V\t$timestrings->{$s}\t\t$serials->{$s}\tunknown\t$s\n";
$opts->{'indexdata'} .= $indexline;
}
# create index file
$index = $self->{$ca}->{'dir'}."/index.txt";
open(OUT, ">$index") || do {
GUI::HELPERS::print_error(_("Can't open Index file: ").$!);
return;
};
print OUT $opts->{'indexdata'};
close OUT;
$cacertfile = $self->{$ca}->{'dir'}."/cacert.pem";
$cakeyfile = $self->{$ca}->{'dir'}."/cacert.key";
# write cacertfile
open(OUT, ">$cacertfile") || do {
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("Can't write CA Certificate file: %s"),
$cacertfile);
return;
};
print OUT $opts->{'cacertdata'};
close(OUT);
# check serial number of CA file
$data->{'parsed'} = $main->{'OpenSSL'}->parsecert(
undef, undef, $cacertfile, 1
);
if(hex($data->{'parsed'}->{'SERIAL'}) >= hex($opts->{'serial'})) {
$opts->{'serial'} = sprintf("%x", hex($opts->{'serial'}));
}
$opts->{'serial'} = hex($opts->{'serial'}) + 1;
$opts->{'serial'} = sprintf("%x", $opts->{'serial'});
# create serial file
$serial = $self->{$ca}->{'dir'}."/serial";
open(OUT, ">$serial") || do {
GUI::HELPERS::print_error(_("Can't write Serial file: ").$!);
return;
};
if($opts->{'serial'} ne "") {
print OUT uc($opts->{'serial'});
}else{
print OUT "01";
}
close OUT;
# write keyfile
open(OUT, ">$cakeyfile") || do {
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("Can't write CA Key file: %s"),
$cakeyfile);
return;
};
print OUT $opts->{'cakeydata'};
close(OUT);
($ret, $ext) = $main->{'OpenSSL'}->newcrl(
config => $self->{$ca}->{'cnf'},
pass => $opts->{'newpasswd'},
crldays => 30,
outfile => $self->{$ca}->{'dir'}."/crl/crl.pem",
format => 'PEM'
);
if ((not -s $self->{$ca}->{'dir'}."/crl/crl.pem") || $ret) {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_error(_("Generating CRL failed"), $ext);
print STDERR "DEBUG: newcrl returned $ext\n";
die;
return;
}
GUI::HELPERS::set_cursor($main, 0);
$main->{'barbox'}->remove($main->{'progress'});
push(@{$self->{'calist'}}, $ca);
@{$self->{'calist'}} = sort(@{$self->{'calist'}});
$t = sprintf(_("Succesfully imported %d certificates\n"), $c);
$t.= _("Check the configuration of your imported CA.");
GUI::HELPERS::print_info($t);
$self->open_ca($main, $opts);
return;
}
#
# create a new CA, environment: dirs, etc.
#
sub create_ca_env {
my ($self, $main, $opts, $mode) = @_;
my ($t, $index, $serial);
if((!defined($opts->{'name'})) || $opts->{'name'} eq '') {
GUI::HELPERS::print_error(_("No CA name given"));
return;
}
# create directories
$self->{$opts->{'name'}}->{'dir'} =
$self->{'init'}->{'basedir'}."/".$opts->{'name'};
mkdir($self->{$opts->{'name'}}->{'dir'}, 0700) || do {
GUI::HELPERS::print_warning(_("Can't create directory: ").$!);
return;
};
mkdir($self->{$opts->{'name'}}->{'dir'}."/req", 0700) || do {
GUI::HELPERS::print_warning(_("Can't create directory: ").$!);
return;
};
mkdir($self->{$opts->{'name'}}->{'dir'}."/keys", 0700) || do {
GUI::HELPERS::print_warning(_("Can't create directory: ").$!);
return;
};
mkdir($self->{$opts->{'name'}}->{'dir'}."/certs", 0700) || do {
GUI::HELPERS::print_warning(_("Can't create directory: ").$!);
return;
};
mkdir($self->{$opts->{'name'}}->{'dir'}."/crl", 0700) || do {
GUI::HELPERS::print_warning(_("Can't create directory: ").$!);
return;
};
mkdir($self->{$opts->{'name'}}->{'dir'}."/newcerts", 0700) || do {
GUI::HELPERS::print_warning(_("Can't create directory: ").$!);
return;
};
# create configuration file
my $in = $self->{'init'}->{'templatedir'}."/openssl.cnf";
my $out = $self->{$opts->{'name'}}->{'dir'}."/openssl.cnf";
open(IN, "<$in") || do {
$t = sprintf(_("Can't open template file %s %s"), $in, $!);
GUI::HELPERS::print_error($t);
return;
};
open(OUT, ">$out") || do {
$t = sprintf(_("Can't open output file: %s: %s"),$out, $!);
GUI::HELPERS::print_error($t);
return;
};
while(<IN>) {
s/\%dir\%/$self->{$opts->{'name'}}->{'dir'}/;
print OUT;
}
close IN;
close OUT;
$self->{$opts->{'name'}}->{'cnf'} = $out;
$main->{'TCONFIG'}->init_config($main, $opts->{'name'});
# create some more files
$index = $self->{$opts->{'name'}}->{'dir'}."/index.txt";
open(OUT, ">$index") || do {
GUI::HELPERS::print_error(_("Can't open Index file: ").$!);
return;
};
close(OUT);
$serial = $self->{$opts->{'name'}}->{'dir'}."/serial";
open(OUT, ">$serial") || do {
GUI::HELPERS::print_error(_("Can't write Serial file: ").$!);
return;
};
if(defined($opts->{'serial'}) && $opts->{'serial'} ne "") {
print OUT uc($opts->{'serial'});
}else{
print OUT "01";
}
close(OUT);
# create ca config file based on template
$in = $self->{'init'}->{'templatedir'}."/tinyca.cnf";
$out = $self->{$opts->{'name'}}->{'dir'}."/tinyca.cnf";
open(IN, "<$in") || do {
$t = sprintf(_("Can't open config template file %s %s"), $in, $!);
GUI::HELPERS::print_error($t);
return;
};
open(OUT, ">$out") || do {
$t = sprintf(_("Can't open config output file: %s: %s"),$out, $!);
GUI::HELPERS::print_error($t);
return;
};
while(<IN>) {
print OUT;
}
close(IN);
close(OUT);
if(defined($mode) && $mode eq "sub") {
$self->create_ca($main, $opts, undef, $mode);
} elsif(defined($mode) && $mode eq "import") {
} else {
GUI::TCONFIG::show_config_ca($main, $opts, $mode);
}
return;
}
#
# now create the CA certificate and CRL
#
sub create_ca {
my ($self, $main, $opts, $box, $mode) = @_;
my ($fname, $t, $index, $serial, $ca, $ret, $ext);
$ca = $self->{'actca'};
$box->destroy() if(defined($box));
GUI::HELPERS::set_cursor($main, 1);
if((!defined($opts->{'name'})) || $opts->{'name'} eq '') {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_error(_("No CA name given"));
return;
}
# create CA certifikate
($ret, $ext) = $main->{'OpenSSL'}->newkey(
'bits' => $opts->{'bits'},
'outfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.key",
'pass' => $opts->{'passwd'}
);
if (not -s $self->{$opts->{'name'}}->{'dir'}."/cacert.key" || $ret) {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Generating key failed"), $ext);
_rm_dir($self->{$opts->{'name'}}->{'dir'});
delete($self->{$opts->{'name'}});
return;
}
my @dn = (
$opts->{'C'},
$opts->{'ST'},
$opts->{'L'},
$opts->{'O'},
$opts->{'OU'}->[0],
$opts->{'CN'},
$opts->{'EMAIL'},
'',
'');
($ret, $ext) = $main->{'OpenSSL'}->newreq(
'config' => $self->{$opts->{'name'}}->{'cnf'},
'outfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.req",
'digest' => $opts->{'digest'},
'pass' => $opts->{'passwd'},
'dn' => \@dn,
'keyfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.key"
);
$fname = HELPERS::gen_name($opts);
$opts->{'reqname'} = HELPERS::enc_base64($fname);
if (not -s $self->{$opts->{'name'}}->{'dir'}."/cacert.req" || $ret) {
unlink($self->{$opts->{'name'}}->{'dir'}."/cacert.key");
unlink($self->{$opts->{'name'}}->{'dir'}."/cacert.req");
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Generating Request failed"), $ext);
_rm_dir($self->{$opts->{'name'}}->{'dir'});
delete($self->{$opts->{'name'}});
return;
} else {
if(defined($mode) && $mode eq "sub") {
# for SubCAs: copy the request to the signing CA
open(IN, "<$self->{$opts->{'name'}}->{'dir'}"."/cacert.req") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Can't read Certificate"));
return;
};
open(OUT, ">$self->{$ca}->{'dir'}"."/req/".$opts->{'reqname'}.".pem") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Can't write Certificate"));
return;
};
print OUT while(<IN>);
close IN; close OUT;
# for SubCAs: copy the key to the signing CA
open(IN, "<$self->{$opts->{'name'}}->{'dir'}"."/cacert.key") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Can't read Certificate"));
return;
};
open(OUT, ">$self->{$ca}->{'dir'}"."/keys/".$opts->{'reqname'}.".pem") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Can't write Certificate"));
return;
};
print OUT while(<IN>);
close IN; close OUT;
}
}
if(defined($mode) && $mode eq "sub") {
($ret, $ext) = $main->{'REQ'}->sign_req(
$main,
{
'mode' => "sub",
'config' => $self->{$opts->{'name'}}->{'cnf'},
'outfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.pem",
'reqfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.req",
'outdir' => $self->{$ca}->{'dir'}."/newcerts/",
'keyfile' => $self->{$ca}->{'dir'}."/cacert.key",
'cacertfile' => $self->{$ca}->{'dir'}."/cacert.pem",
'digest' => $opts->{'digest'},
'pass' => $opts->{'passwd'},
'days' => $opts->{'days'},
'parentpw' => $opts->{'parentpw'},
'reqname' => $opts->{'reqname'}
}
);
} else {
($ret, $ext) = $main->{'OpenSSL'}->newcert(
'config' => $self->{$opts->{'name'}}->{'cnf'},
'outfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.pem",
'keyfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.key",
'reqfile' => $self->{$opts->{'name'}}->{'dir'}."/cacert.req",
'digest' => $opts->{'digest'},
'pass' => $opts->{'passwd'},
'days' => $opts->{'days'}
);
}
if (not -s $self->{$opts->{'name'}}->{'dir'}."/cacert.pem" || $ret) {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(
_("Generating certificate failed"), $ext);
_rm_dir($self->{$opts->{'name'}}->{'dir'});
delete($self->{$opts->{'name'}});
return;
}
unlink($self->{$opts->{'name'}}->{'dir'}."/cacert.req");
if(defined($mode) && $mode eq "sub") {
# create file containing chain of ca certificates
my $in;
if (-f $self->{$ca}->{'dir'}."/cachain.pem") {
$in = $self->{$ca}->{'dir'}."/cachain.pem";
} else {
$in = $self->{$ca}->{'dir'}."/cacert.pem";
}
my $out = $self->{$opts->{'name'}}->{'dir'}."/cachain.pem";
open(IN, "<$in") || do {
$t = sprintf(
_("Can't open ca certificate file %s %s"), $in, $!);
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning($t);
_rm_dir($self->{$opts->{'name'}}->{'dir'});
delete($self->{$opts->{'name'}});
return;
};
open(OUT, ">$out") || do {
$t = sprintf(
_("Can't create certificate chain file: %s: %s"),$out, $!);
GUI::HELPERS::set_cursor($main, 0);
$main->print_warning($t);
_rm_dir($self->{$opts->{'name'}}->{'dir'});
delete($self->{$opts->{'name'}});
return;
};
while(<IN>) {
print OUT;
}
close IN;
# now append the certificate of the created SubCA
$in = $self->{$opts->{'name'}}->{'dir'}."/cacert.pem";
open(IN, "<$in") || do {
$t = sprintf(
_("Can't open ca certificate file %s %s"), $in, $!);
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning($t);
_rm_dir($self->{$opts->{'name'}}->{'dir'});
delete($self->{$opts->{'name'}});
return;
};
while(<IN>) {
print OUT;
}
close OUT;
}
($ret, $ext) = $main->{'OpenSSL'}->newcrl(
config => $self->{$opts->{'name'}}->{'cnf'},
pass => $opts->{'passwd'},
crldays => $main->{'TCONFIG'}->{'server_ca'}->{'default_crl_days'},
outfile => $self->{$opts->{'name'}}->{'dir'}."/crl/crl.pem",
format => 'PEM'
);
if (not -s $self->{$opts->{'name'}}->{'dir'}."/crl/crl.pem" || $ret) {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(_("Generating CRL failed"), $ext);
_rm_dir($self->{$opts->{'name'}}->{'dir'});
delete($self->{$opts->{'name'}});
return;
}
# seems to be done
push(@{$self->{'calist'}}, $opts->{'name'});
@{$self->{'calist'}} = sort(@{$self->{'calist'}});
$t = sprintf(_("CA: %s created"), $opts->{'name'});
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_info($t);
$self->open_ca($main, $opts);
return;
}
#
# export ca certificate chain
#
sub export_ca_chain {
my ($self, $main, $opts, $box) = @_;
my($ca, $chainfile, $parsed, $out, $t);
$box->destroy() if(defined($box));
$ca = $self->{'actca'};
if(not defined($opts)) {
$opts->{'format'} = 'PEM';
$opts->{'outfile'} = "$main->{'exportdir'}/$ca-cachain.pem";
$main->show_ca_chain_export_dialog($opts);
return;
}
GUI::HELPERS::set_cursor($main, 1);
$chainfile = $self->{$ca}->{'dir'}."/cachain.pem";
open(IN, "<$self->{$ca}->{'dir'}"."/cachain.pem") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(
_("Can't open certificate chain file: %s: %s"),
$self->{$ca}->{'dir'}."/cachain.pem", $!);
return;
};
open(OUT, ">$opts->{'outfile'}") || do {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning(
_("Can't open output file: %s: %s"),
$opts->{'outfile'}, $!);
return;
};
print OUT while(<IN>);
close OUT;
$main->{'exportdir'} = HELPERS::write_export_dir($main,
$opts->{'outfile'});
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("Certificate Chain succesfully exported to: %s"),
$opts->{'outfile'});
GUI::HELPERS::print_info($t);
return;
}
#
# export ca certificate
#
sub export_ca_cert {
my ($self, $main, $opts, $box) = @_;
my($ca, $certfile, $parsed, $out, $t);
$box->destroy() if(defined($box));
GUI::HELPERS::set_cursor($main, 1);
$ca = $self->{'actca'};
$certfile = $self->{$ca}->{'dir'}."/cacert.pem";
if(not defined($opts)) {
$opts->{'format'} = 'PEM';
$opts->{'outfile'} = "$main->{'exportdir'}/$ca-cacert.pem";
GUI::HELPERS::set_cursor($main, 0);
$main->show_ca_export_dialog($opts);
return;
}
$parsed = $main->{'CERT'}->parse_cert($main, 'CA');
if(not defined $parsed) {
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_error(_("Can't read CA certificate"));
}
if($opts->{'format'} eq "PEM") {
$out = $parsed->{'PEM'};
} elsif ($opts->{'format'} eq "DER") {
$out = $parsed->{'DER'};
} elsif ($opts->{'format'} eq "TXT") {
$out = $parsed->{'TEXT'};
} else {
$t = sprintf(_("Invalid Format for export_ca_cert(): %s"),
$opts->{'format'});
GUI::HELPERS::set_cursor($main, 0);
GUI::HELPERS::print_warning($t);
return;
}
open(OUT, ">$opts->{'outfile'}") || do {
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("Can't open output file: %s: %s"),
$opts->{'outfile'}, $!);
GUI::HELPERS::print_warning($t);
return;
};
print OUT $out;
close OUT;
$main->{'exportdir'} = HELPERS::write_export_dir($main,
$opts->{'outfile'});
GUI::HELPERS::set_cursor($main, 0);
$t = sprintf(_("Certificate succesfully exported to: %s"),
$opts->{'outfile'});
GUI::HELPERS::print_info($t);
return;
}
#
# export crl
#
sub export_crl {
my ($self, $main, $opts, $box) = @_;
my($ca, $t, $ret, $ext);
$box->destroy() if(defined($box));
GUI::HELPERS::set_cursor($main, 1);
$ca = $self->{'actca'};
if(not defined($opts)) {
$opts->{'outfile'} = "$main->{'exportdir'}/$ca-crl.pem";
$opts->{'format'} = 'PEM';
$opts->{'days'} = $main->{'TCONFIG'}->{'server_ca'}->{'default_crl_days'};
GUI::HELPERS::set_cursor($main, 0);
$main->show_crl_export_dialog($opts);
return;
}
if((not defined($opts->{'outfile'})) || ($opts->{'outfile'} eq '')) {
GUI::HELPERS::set_cursor($main, 0);
$t = _("Please give the output file");
$main->show_crl_export_dialog($opts);
GUI::HELPERS::print_warning($t);
return;
};
if((not defined($opts->{'passwd'})) || ($opts->{'passwd'} eq '')) {
GUI::HELPERS::set_cursor($main, 0);
$t = _("Please give the CA password to create the Revocation List");
$main->show_crl_export_dialog($opts);
GUI::HELPERS::print_warning($t);
return;
}
if(not defined($main->{'OpenSSL'})) {
$main->init_openssl($ca);
}
($ret, $ext) = $main->{'OpenSSL'}->newcrl(
config => $self->{$ca}->{'cnf'},
pass => $opts->{'passwd'},
crldays => $opts->{'days'},
outfile => $opts->{'outfile'},
format => $opts->{'format'}
);
GUI::HELPERS::set_cursor($main, 0);
if($ret eq 1) {
$t = _("Wrong CA password given\nGenerating Revocation List failed");
GUI::HELPERS::print_warning($t, $ext);
return;
} elsif($ret eq 2) {
$t = _("CA Key not found\nGenerating Revocation List failed");
GUI::HELPERS::print_warning($t, $ext);
return;
} elsif($ret) {
$t = _("Generating Revocation List failed");
GUI::HELPERS::print_warning($t, $ext);
return;
}
if (not -s $opts->{'outfile'}) {
$t = _("Generating Revocation List failed");
GUI::HELPERS::print_warning($t);
return;
}
$main->{'exportdir'} = HELPERS::write_export_dir($main,
$opts->{'outfile'});
$t = sprintf(_("CRL successfully exported to: %s"),
$opts->{'outfile'});
GUI::HELPERS::print_info($t, $ext);
return;
}
sub _rm_dir {
my $dir = shift;
my ($dirh, $path);
opendir($dirh, $dir);
while(my $f = readdir($dirh)) {
next if $f eq '.';
next if $f eq '..';
$path = $dir."/".$f;
if(-d $path) {
_rm_dir($path);
} else {
unlink($path);
}
}
closedir($dirh);
rmdir($dir);
return(0);
}
1