summaryrefslogtreecommitdiff
path: root/src/keytrans/pem2openpgp
blob: 59f9bb0db7f5f5bb14cff55aa56b05345b571383 (plain)
  1. #!/usr/bin/perl -w -T
  2. # pem2openpgp: take a PEM-encoded RSA private-key on standard input, a
  3. # User ID as the first argument, and generate an OpenPGP certificate
  4. # from it.
  5. # Authors:
  6. # Jameson Rollins <jrollins@finestructure.net>
  7. # Daniel Kahn Gillmor <dkg@fifthhorseman.net>
  8. # Started on: 2009-01-07 02:01:19-0500
  9. # License: GPL v3 or later (we may need to adjust this given that this
  10. # connects to OpenSSL via perl)
  11. use strict;
  12. use warnings;
  13. use Crypt::OpenSSL::RSA;
  14. use Crypt::OpenSSL::Bignum;
  15. use Digest::SHA1;
  16. use MIME::Base64;
  17. my $holdTerminator = $/;
  18. undef $/;
  19. my $buf = <STDIN>;
  20. my $rsa = Crypt::OpenSSL::RSA->new_private_key($buf);
  21. $rsa->use_sha1_hash();
  22. $rsa->use_no_padding();
  23. if (! $rsa->check_key()) {
  24. die "key does not check";
  25. }
  26. my $uid = 'fake key (do not use) <test@example.org>';
  27. my $version = pack('C', 4);
  28. # strong assertion of identity:
  29. my $sigtype = pack('C', 0x13);
  30. # RSA
  31. my $pubkey_algo = pack('C', 1);
  32. # SHA1
  33. my $hash_algo = pack('C', 2);
  34. my $timestamp = 1231003584;
  35. my $creation_time_packet = pack('CCN', 5, 2, $timestamp);
  36. # usage: signing and certification:
  37. my $flags = 0x03;
  38. my $usage_packet = pack('CCC', 2, 27, $flags);
  39. # expire in 2 days:
  40. my $expires_in = 86400*2;
  41. my $expiration_packet = pack('CCN', 5, 9, $expires_in);
  42. # prefer AES-256, AES-192, AES-128, CAST5, 3DES:
  43. my $pref_sym_algos = pack('CCCCCCC', 6, 11, 9, 8, 7, 3, 2);
  44. # prefer SHA-1, SHA-256, RIPE-MD/160
  45. my $pref_hash_algos = pack('CCCCC', 4, 21, 2, 8, 3);
  46. # prefer ZLIB, BZip2, ZIP
  47. my $pref_zip_algos = pack('CCCCC', 4, 22, 2, 3, 1);
  48. # we support the MDC feature:
  49. my $features = pack('CCC', 2, 30, 1);
  50. # keyserver preference: only owner modify (???):
  51. my $keyserver_pref = pack('CCC', 2, 23, 0x80);
  52. my $subpackets_to_be_hashed =
  53. $creation_time_packet.
  54. $usage_packet.
  55. $expiration_packet.
  56. $pref_sym_algos.
  57. $pref_hash_algos.
  58. $pref_zip_algos.
  59. $features.
  60. $keyserver_pref;
  61. #FIXME: what's the right way to get length()?
  62. my $subpacket_octets = pack('n', length($subpackets_to_be_hashed));
  63. my $sig_data_to_be_hashed =
  64. $version.
  65. $sigtype.
  66. $pubkey_algo.
  67. $hash_algo.
  68. $subpacket_octets.
  69. $subpackets_to_be_hashed;
  70. my ($n, $e, $d, $p, $q) = $rsa->get_key_parameters();
  71. open(KEYFILE, "</home/wt215/gpg-test/key-data");
  72. my $key_data = <KEYFILE>;
  73. # FIXME: $keyid should be generated from the public key instead of
  74. # hardcoded:
  75. my $keyid = '5616d7cb02e69446';
  76. # the v4 signature trailer is:
  77. # version number, literal 0xff, and then a 4-byte count of the
  78. # signature data itself.
  79. my $trailer = pack('CCN', 4, 0xff, length($sig_data_to_be_hashed));
  80. # FIXME: length() is probably not right here either in the event that
  81. # the uid uses unicode.
  82. my $uid_data =
  83. pack('CN', 0xb4, length($uid)).
  84. $uid;
  85. my $datatosign =
  86. $key_data.
  87. $uid_data.
  88. $sig_data_to_be_hashed.
  89. $trailer;
  90. my $data_hash = Digest::SHA1::sha1_hex($datatosign);
  91. my $issuer_packet = pack('CCH16', 9, 16, $keyid);
  92. my $sig = $rsa->sign($datatosign);
  93. my $bigsig = Crypt::OpenSSL::Bignum->new_from_bin($sig);
  94. my $hex = $bigsig->to_hex();
  95. my $mpilen = length($hex)*4;
  96. # this is a kludgy way to get the number of bits in the first byte:
  97. my $bitsinfirstbyte = length(sprintf("%b", hex(substr $hex, 0, 2)));
  98. $mpilen -= (8 - $bitsinfirstbyte);
  99. # emit two octets representing $mpilen, followed by the signature itself:
  100. my $sig_body =
  101. $sig_data_to_be_hashed.
  102. # FIXME: another dubious length() call.
  103. pack('n', length($issuer_packet)).
  104. $issuer_packet.
  105. pack('n', hex(substr($data_hash, 0, 4))).
  106. pack("n" , $mpilen).
  107. $sig;
  108. # FIXME: yet another length():
  109. my $len = length($sig_body);
  110. my $header;
  111. if ($len < 2**8) {
  112. $header = pack('CC', 0x88, $len);
  113. } elsif ($len < 2**16) {
  114. $header = pack('Cn', 0x89, $len);
  115. } elsif ($len < 2**31) {
  116. $header = pack('CN', 0x8a, $len);
  117. } else {
  118. # what the hell do we do here?
  119. $header = pack('C', 0x8b);
  120. }
  121. print $header.$sig_body;
  122. $/ = $holdTerminator;