Well that took a little while, but it is now all done 🙂
Et voila :
#!/usr/bin/perl -w #use strict; use Getopt::Long; use Storable; use Net::Twitter::Lite; use Crypt::OpenSSL::Random; use Crypt::OpenSSL::RSA; use MIME::Base64; #----------------------------------------------# # This is : # $clientname = "twitter crypt"; # # Version : # $clientver = "0.1"; # #----------------------------------------------# # By : Simon Biles # $clienturl="http://computersecurityonline.com";# #----------------------------------------------# # Saved Public Keys File $Public_Keys_File = "twc_public_keys"; GetOptions("genkey=s" => \$genkey, "publishkey=s" => \$publishkey, "getkeys" => \$getkeys, "showkeys" => \$showkeys, "encrypt=s" => \$encrypt, "getmessages=s" => \$getmessages, "user=s" => \$user, "pass=s" => \$password ); if (defined $genkey){ $good_entropy = "10010101001010101"; Crypt::OpenSSL::Random::random_seed($good_entropy); Crypt::OpenSSL::RSA->import_random_seed(); $rsa = Crypt::OpenSSL::RSA->generate_key(640); $private = $rsa->get_private_key_string(); $public = $rsa->get_public_key_string(); print "\nprivate key is:\n $private\n"; print "\npublic key (in PKCS1 format) is:\n $public\n"; open(KEYFILE, "> $genkey") or die $!; print KEYFILE "$private\n$public"; close KEYFILE; print "\nKeys written to file $genkey ...\n\n"; exit 0; } if (defined $publishkey && defined $user && defined $password){ my $nt = Net::Twitter::Lite->new( username => $user, password => $password, clientname => $clientname, clientver => $clientver, clienturl => $clienturl ); open (KEYFILE, "< $publishkey"); $flag = 0; $public_key = ""; while (<KEYFILE>){ if ($_ =~ /-----END RSA PUBLIC KEY-----/) { last; }; if ($flag == 1) { $public_key = $public_key.$_; }; if ($_ =~ /-----BEGIN RSA PUBLIC KEY-----/) { $flag = 1; }; } $public_key = "twc-public -".$public_key; $key_length = length $public_key; print "Length: $key_length \n$public_key"; if($key_length > 140){ print "\nFor some reason we've over shot our 140 chars for Twitter, complain ...\n"; exit 0; } my $result = eval { $nt->update($public_key) }; exit 0; } if($getkeys && defined $user && defined $password){ if(-e $Public_Keys_File){ %keys_hash = %{retrieve($Public_Keys_File)}; } else { %keys_hash = (); } my $nt = Net::Twitter::Lite->new( username => $user, password => $password, clientname => $clientname, clientver => $clientver, clienturl => $clienturl ); eval { my $statuses = $nt->friends_timeline(); for my $status ( @$statuses ) { if ($status->{text} =~ /twc-public -/){ ($public_key_string = $status->{text}) =~ s/twc-public -//; $keys_hash{ $status->{user}{screen_name} } = $public_key_string; } } }; warn "$@\n" if $@; store(\%keys_hash, $Public_Keys_File) or die "\nCan't save public keys to $Public_Keys_File.\n\n"; exit 0; } if($showkeys){ if(-e $Public_Keys_File){ %keys_hash = %{retrieve($Public_Keys_File)}; while (($key, $value) = each(%keys_hash)){ print "$key - $value\n"; } } else { print "\n\n Public Keys file $Public_Keys_File does not exist, create it using the --getkeys option."; } exit 0; } if(defined $encrypt && defined $user && defined $password){ if(-e $Public_Keys_File){ %keys_hash = %{retrieve($Public_Keys_File)}; $encryption_key = "-----BEGIN RSA PUBLIC KEY-----\n"."$keys_hash{$encrypt}\n"."-----END RSA PUBLIC KEY-----\n"; print "$encryption_key"; $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($encryption_key); print " |------------------------------------|\n"; print "Enter your message : (between the lines !) "; $plaintext = <STDIN>; if ((length $plaintext) > 38){ print "Message too long I'm afraid, I did say to stay between the lines ...\n"; exit 0; } $ciphertext = $rsa_pub->encrypt($plaintext); $encoded_ciphertext = "twc-msg-$encrypt-".encode_base64($ciphertext); $encoded_length = length $encoded_ciphertext; if ($encoded_length > 140){ print "Uh-oh, we've had an over Twitter length moment there !\n"; exit 0; } my $nt = Net::Twitter::Lite->new( username => $user, password => $password, clientname => $clientname, clientver => $clientver, clienturl => $clienturl ); my $result = eval { $nt->update($encoded_ciphertext) }; } else { print "\n\n Public Keys file $Public_Keys_File does not exist, so there is no key for $encrypt create it using the --getkeys option."; exit 0; } exit 0; } if(defined $getmessages && defined $user && defined $password){ $private_key = ""; if (-e $getmessages){ open (FILEHANDLE, "< $getmessages"); while(<FILEHANDLE>){ if ($_ =~ /-----BEGIN RSA PRIVATE KEY-----/){ $flag = 1; } if ($flag == 1){ $private_key = "$private_key"."$_"; } if($_ =~ /-----END RSA PRIVATE KEY-----/){ last; } } $rsa_priv = Crypt::OpenSSL::RSA->new_private_key($private_key); my $nt = Net::Twitter::Lite->new( username => $user, password => $password, clientname => $clientname, clientver => $clientver, clienturl => $clienturl ); eval { my $statuses = $nt->friends_timeline(); for my $status ( @$statuses ) { if ($status->{text} =~ /twc-msg-$user-/){ $from = $status->{user}{screen_name}; ($encoded_ciphertext = $status->{text}) =~ s/twc-msg-$user-//; $ciphertext = decode_base64($encoded_ciphertext); $decrypted = $rsa_priv->decrypt($ciphertext); print "Message from $from : $decrypted\n"; } } }; warn "$@\n" if $@; exit 0; } else { print "That key file doesn't appear to exist ...\n"; exit 0; } exit 0; } #Useage information ... should get here if all else fails ! print "\nSorry the correct useage of twcrypt.pl is :\n\n"; print "twcrypt.pl --genkey keyfile\n\t- generate a key file of name keyfile.\n\n"; print "twcrypt.pl --publishkey keyfile --user username --pass password\n\t- publish the keyfile to twitter for specified account.\n\n"; print "twcrypt.pl --getkeys --user username --pass password\n\t- collect keys published by people you are following.\n\n"; print "twcrypt.pl --showkeys\n\t- show cached public keys.\n\n"; print "twcrypt.pl --encrypt to --user username --pass password\n\t- encrypt a message to a given user.\n\n"; print "twcrypt.pl --getmessages --user username --pass password\n\t- get messages sent to you.\n\n\n"; print "I do realise this isn't the best interface, but hell, live with it ...\n\n"; exit 0;
It works too ! Creating, uploading, encrypting and decrypting through Twitter – Twitter PKI – you saw it here first 😛
I only made one small modification to the planed implementation, and that was to include the name of the recipient – I found that the attempt to decrypt a message with the wrong private key for the encryption caused a crash out, so I though that it would be best to keep it to only decrypting what it is supposed to.
All comments and critique of my code welcome … Please remember though that it’s only a proof of concept – I would be interested in any major flaws though … ( A low key length isn’t a flaw, it’s a practicality by the way – 640 bits should keep most people at bay for a fairly reasonable amount of time 🙂 and is all I could fit into Twitter. ) I hope that the code is self explanatory, if you do have any questions, please drop me a line at si at thinking-security.com
I’m going to go and have a break from the computer now … I might fiddle with this some more later to make it a little more useable, maybe a GUI or iPhone version 😉