The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/local/bin/perl
# cli-cert.pl
# 8.6.1998, originally written as stdio_bulk.pl Sampo Kellomaki <sampo@iki.fi>
# 8.12.2001, adapted to test client certificates
#
# Contact server using client side certificate. Demonstrates how to
# set up the client and how to make the server request the certificate.
# This also demonstrates how you can communicate via arbitrary stream, not
# just a TCP one.
# $Id: cli-cert.pl,v 1.2 2003/06/13 21:14:41 sampo Exp $

use Socket;
use Net::SSLeay::OO;

$ENV{RND_SEED} = '1234567890123456789012345678901234567890';

($cert_pem, $key_pem, $cert_dir) = @ARGV;      # Read command line
$how_much = 10000;

### Note: the following initialization is common for both client
### and the server. In particular, it is important that VERIFY_PEER
### is sent on the server as well, because otherwise the client
### certificate will never be requested.

use Net::SSLeay::Constants qw(VERIFY_PEER FILETYPE_PEM);

$ctx = Net::SSLeay::Context->new;
$ctx->set_default_passwd_cb(sub{"secr1t"});
$ctx->use_certificate_chain_file($cert_pem);
$ctx->use_PrivateKey_file($key_pem, FILETYPE_PEM);
$ctx->load_verify_locations('', $cert_dir);
$ctx->set_verify(VERIFY_PEER, \&verify);


pipe RS, WC or die "pipe 1 ($!)";
pipe RC, WS or die "pipe 2 ($!)";
select WC; $| = 1;
select WS; $| = 1;
select STDOUT;
$| = 1;

if ($child_pid = fork) {
    print "$$: I'm the server for child $child_pid\n";
    $ssl = Net::SSLeay::SSL->new(ctx => $ctx);

    $ssl->set_rfd(fileno(RS));
    $ssl->set_wfd(fileno(WS));

    print "$$: accept\n";
    $ssl->accept;
    print "$$: Cipher `" . $ssl->get_cipher . "'\n";
    #print "$$: client cert: " . $ssl->dump_peer_certificate;

    $got = $ssl->ssl_read_all($how_much);
    print "$$: got " . length($got) . " bytes\n";
    $ssl->ssl_write_all(\$got);
    $got = '';

    print "$$: close SSL\n";
    undef($ssl);# Tear down connection
    print "$$: close CTX\n";
    undef($ctx);

    print "$$: wait\n";
    wait;  # wait for child to read the stuff

    close WS;
    close RS;
    print "$$: server done ($?).\n"
	. (($? >> 8) ? "ERROR\n" : "OK\n"); 
    exit;
}

print "$$: I'm the child.\n";
sleep 1;  # Give server time to get its act together

$ssl = Net::SSLeay::SSL->new(ctx => $ctx);
$ssl->set_rfd(fileno(RC));
$ssl->set_wfd(fileno(WC));
print "$$: connect\n";
$ssl->connect;

print "$$: Cipher `" . $ssl->get_cipher . "'\n";
#print "$$: server cert: " . $ssl->dump_peer_certificate;

# Exchange data

$data = 'B' x $how_much;
$ssl->ssl_write_all(\$data);
$got = $ssl->ssl_read_all($how_much);

print "$$: close SSL\n";
undef($ssl);               # Tear down connection
print "$$: close CTX\n";
undef($ctx);
print "$$: close pipes\n";
close WC;
close RC;
print "$$: exiting\n";
exit ($data ne $got);

use Net::SSLeay::X509::Context;
use Net::SSLeay::X509;

our $PRINTED;
sub verify {
    my ($ok, $x509_cert) = @_;
    print "$$: **** Verify 2 called ($ok)\n";
    if ($x509_cert) {
	print "$$: Certificate:\n";
	    print "  Subject Name: "
		. $x509_cert->get_subject_name->oneline
		    . "\n";
	    print "  Issuer Name:  "
                . $x509_cert->get_issuer_name->oneline
                  . "\n";
    }
    $callback_called++;
    return 1;
}

__END__