PGP Keysigning

The past couple of years, I have organized a PGP Keysigning Session at ApacheCon. With another ApacheCon coming up, I’d like to chronicle how I sign the keys after having verified them at a keysigning party.

After the keysigning party, I am left with a list with all participating keys, and two checkboxes per key. I have checked these boxes during the party, or refrained from doing so based on the circumstances. The first one tells me whether the owner of that key spoke up and asserted that this was in fact their key. The second checkbox tells me whether I have actually met that person and verified their identity.

I put the key ID for every key that got two check marks in a file, one per line. That file will be the base of my further activities. Now every key identified in this file has to be:

  1. Signed by my private key
  2. Uploaded to two keyservers
  3. A notification sent to the owner that I signed their key

For the last step, the notification, I have a Perl script that came to me through Apache folks drbacchus and sgala. But first, I have to sign the keys. I could incorporate that into the script and will possibly do so in the future, but for now I use a single line of shell will suffice:

$ LANG=C for k in `cat keystosign.txt` ; do gpg --display-charset utf8 --edit-key $k ; done

This will land you in the interactive key editor for each of the keys on the list, where you will manually perform the following steps:

  1. Verify the name and e-mail address on the key
  2. Sign the key with your private key
  3. Raise the key trust level according to your identification impression during the keysigning party

Then, upload all the keys you signed to a two keyservers using another one line shellscript:

$ for k in `cat keystosign.txt ` ; do gpg --verbose --keyserver pgpkeys.mit.edu --send-keys $k ; sleep 5 ; done
for k in `cat keystosign.txt ` ; do gpg --verbose --keyserver minsky.surfnet.nl --send-keys $k ; sleep 5 ; done

We’re sleeping five seconds between keys to prevent the keyserver from thinking it’s being flooded: I don’t know that these servers are sensitive to a particular connection rate from a particular host but it just seems the nice thing to do. Why these two keyservers? Well, mainly because in the Perl script below I tell the receipient that she can find her signed key there. So, the script:


#!/usr/bin/perl
use Mail::Sendmail;

use IO::Handle ;
use GnuPG::Interface;

my $sig_marker = 'Replace this with some text that defines your signature in a list';
my $mail_from = 'Replace this with your own e-mail address';
my $gpg_params = '--display-charset utf8 --with-colons';

#
# Be careful to use it in safe
# environment, the passphrase is either type in clear, as here,
# or entered in the program as a literal string.
# If the passphrase line is commented, it will prompt
# (quite a few times)
#
print "Enter your passphrase:";
my $passphrase = ;

#
# A file called keys in the current directory
# contains one key ID per line to be checked.
#

open KEYS, "<$ARGV[0]";
while () {
my $fp = $_;
chomp $fp;
print "Key ID is [$fp]\n";
my @sigs = `LANG=C gpg $gpg_params --list-sigs $fp 2> /dev/null`;
#print @sigs;
my $keyid;
my $uid;
my $signed = 0;
foreach $sig (@sigs) {
my @f = split /:/, $sig; # Fields
if ($f[0] eq 'pub') { # First field: type of record
$keyid = $f[4]; # Fifth field: keyid
$uid = $f[9]; # Tenth field: uid
print "Working with key [$keyid] of [$uid]\n";
}
if (($f[0] eq 'sig') && ($f[9] eq $sig_marker)) {
# My signature is on this key
$signed = 1;
}
}
if ($signed) {
sendit($keyid, $uid);
} else {
print "Key $keyid not signed. Skipping...\n\n";
}

}

sub sendit {
my ($keyid, $email) = @_;

my @body = qq"
Hi there,

Thanks for participating in the KeySigning at ApacheCon.
I finally took some time to hack up and run a script to notify
the owners of all the keys I signed after that session.

In the instructions I sent out before the keysigning, I recommended to
export the signed keys mail them back to their owners. However, after
some valid criticism (most notably from RoUS), I have decided not to do
that.

I have uploaded your key(s) to two popular keyservers:

pgpkeys.mit.edu
minsky.surfnet.nl

You should be able to find it/them there and download accordingly. Of
course, if you prefer I can still mail you your signed key. Please let
me know if you would like me to do that. I'm just not going to do a mass
e-mail. This is also why I'm only sending this to the primary uid of
your key.

Thank you for being part of the ApacheCon PGP Keysigning.

Regards,

<>

";

# Colons are escaped by gpg --with-colons because they are
# field delimiters. Put them back.
$email =~ s/\\x3a/:/;
print "Sending message to [$email]...\n";

# setting up the situation
my $gnupg = GnuPG::Interface->new();

$gnupg->options->hash_init( armor => 1,
homedir => "$ENV{HOME}/.gnupg"
);

$gnupg->options->push_recipients( $email );
$gnupg->options->meta_interactive( 0 );
#comment the next line, and the script will prompt each time
$gnupg->passphrase( $passphrase );

# how we create some handles to interact with GnuPG
my $input = IO::Handle->new();
my $output = IO::Handle->new();

my $handles = GnuPG::Handles->new(
stdin => $input,
stdout => $output
);

# Now we'll go about encrypting with the options already set
my @plaintext = @body;

#print "Text:", @plaintext, $gnupg->options->get_args(), "\n";

my $pid;
my @cyphertext;

$pid = $gnupg->sign_and_encrypt( handles => $handles );
# $pid = $gnupg->clearsign( handles => $handles );

print "Writing...($key)";
print $input @plaintext;
print "...and closing\n";
close $input;

print "Reading...($key)";
@cyphertext = <$output>;
close $output;
print " ...and closing\n";

close $error;

#kill('TERM', $pid);
waitpid $pid, 0;

my $cypherbody = join "", @cyphertext;

#print "Attempting to send signed key to $email\n, Body: \n$cypherbody\n";

# For testing purposes, send all mail to self
# $email = $mail_from;

my %mail = ( To => $email,
Subject => 'Your signed key from ApacheCon',
From => $mail_from,
smtp => 'your.smtp.mail.server',
Message => $cypherbody
);
sendmail(%mail) or die $Mail::Sendmail::error;

# Sleep for a while to keep the mailserver from choking.
# It may think I'm spamming...
sleep 30;
}

This script will undoubtedly undergo refinements as I do more of these sessions.

Be Sociable, Share!