#!/usr/local/bin/perl

package Certificate;

BEGIN {
    $ENV{HOME} = (getpwuid($<))[7];
}

use Expect;

$Expect::Log_Stdout = 0;
#$Expect::Debug = 1;
$Certificate::CA_DIR='/root/CA';
$Certificate::tmpdir = '/tmp';
$Certificate::ca_key_password = '';

sub new {
    my $class = shift;
    my $self = {@_};
    bless ($self,$class);

    $self->{req_files} = { 'key'   => Certificate::rand_filename($tmpdir,'key'),
			   'csr'   => Certificate::rand_filename($tmpdir,'csr'),
			   'crt'   => Certificate::rand_filename($tmpdir,'crt'),
			   'p12'   => Certificate::rand_filename($tmpdir,'p12'),
			   'der'   => Certificate::rand_filename($tmpdir,'der')
			 };

    return $self;
}

sub touch {
    my $file = shift;
    open(FILE,">$file");
    close(FILE);
}

sub rand_filename {
    my $dir = shift;
    my $ext = shift;
    my $rand;

    $ext = ".$ext" if ($ext !~ /^\./);

    while (1) {
	$rand = join('', rand(time()+getppid()), rand(time()+$$));
	$rand =~ s/\.//g;
	if (! -f "$dir/$rand$ext") {
	    Certificate::touch ("$dir/$rand$ext");
	    return "$dir/$rand$ext";
	}
    }
}

sub info_line {
    my $txt = shift;
    $txt = ($txt eq '') ? ".\n" : "$txt\n";
    return $txt;
}

sub gencsr {
    my $self = shift;

    my $genrsa = Expect->spawn('/usr/bin/openssl genrsa -des3 -out',
	    $self->{req_files}->{key}, '1024');

    $genrsa->expect(20,':');
    print $genrsa $self->{pass}, "\n";
    $genrsa->expect(10,':');
    print $genrsa $self->{pass}, "\n";
    $genrsa->expect(undef);


    if ($genrsa->soft_close) {
	return undef;
    }

    my $req = Expect->spawn('/usr/bin/openssl req -new -days 365 -key ',
	    $self->{req_files}->{key}, ' -out ', $self->{req_files}->{csr});

    $req->expect(10,':');
    print $req $self->{pass}, "\n";
    $req->expect(10,':');
    print $req $self->{country}, "\n";
    $req->expect(10,':');
    print $req $self->{state}, "\n";
    $req->expect(10,':');
    print $req $self->{city}, "\n";
    $req->expect(10,':');
    print $req $self->{org}, "\n";
    $req->expect(10,':');
    print $req $self->{org_unit}, "\n";
    $req->expect(10,':');
    print $req $self->{name}, "\n";
    $req->expect(10,':');
    print $req $self->{email}, "\n";
    $req->expect(10,':');
    print $req "\n";
    $req->expect(10,':');
    print $req "\n";
    $req->expect(undef);

    if ($req->soft_close) {
	return undef;
    }

    return 1;
}


sub signcsr {
    my $self = shift;

    my $csr = Expect->spawn("/usr/bin/openssl ca -config $Certificate::CA_DIR/ca.config -out", $self->{req_files}->{crt}, '-infiles', $self->{req_files}->{csr});
    
    $csr->expect(10,':');
    print $csr "$Certificate::ca_key_password\n";
    $csr->expect(10,'y/n');
    print $csr "y\n";
    $csr->expect(10,'y/n');
    print $csr "y\n";
    $csr->expect(undef);

    if ($csr->soft_close) {
	return undef;
    }
    unlink("$Certificate::CA_DIR/ca.db.serial.old", "$Certificate::CA_DIR/ca.db.index.old");
    return 1;
}

sub genp12 {
    my $self = shift;

    my $fn = $self->{name} . "'s certificate at " . $self->{org};

    my $p12 = Expect->spawn('/usr/bin/openssl pkcs12 -export -in',
	    $self->{req_files}->{crt}, '-inkey', $self->{req_files}->{key},
	    "-name \"$fn\" -out", $self->{req_files}->{p12});

    $p12->expect(10,':');
    print $p12 $self->{pass}, "\n";
    $p12->expect(10,':');
    print $p12 $self->{pass}, "\n";
    $p12->expect(10,':');
    print $p12 $self->{pass}, "\n";
    $p12->expect(undef);

    return $p12->soft_close ? undef : 1;
}

sub retrp12 {
    my $self = shift;
    open(P12,$self->{req_files}->{p12});
    my $p12 = undef;
    while ( defined ($_ = <P12>) ) {
	$p12 .= $_;
    }
    close(P12);
    return $p12;
}

sub DESTROY {
    my $self = shift;
    foreach (keys %{ $self->{req_files} }) {
	unlink $self->{req_files}->{$_};
    }
}

# vim: sw=4 ts=8 cindent
