Workaround for bug in m2crypto-0.18 (on Fedora 8)
[sfa.git] / keyconvert / keyconvert.py
1 #!/usr/bin/env python
2
3 import sys
4 import base64
5 import struct
6 import binascii
7 from M2Crypto import RSA, DSA, m2
8
9
10 ###### Workaround for bug in m2crypto-0.18 (on Fedora 8)
11 class RSA_pub_fix(RSA.RSA_pub):
12     def save_key_bio(self, bio, *args, **kw):
13         return self.save_pub_key_bio(bio)
14
15 def rsa_new_pub_key((e, n)):
16     rsa = m2.rsa_new()
17     m2.rsa_set_e(rsa, e)
18     m2.rsa_set_n(rsa, n)
19     return RSA_pub_fix(rsa, 1)
20 ######
21 #rsa_new_pub_key = RSA.new_pub_key
22
23
24 def decode_key(fname):
25     """Convert base64 encoded openssh key to binary"""
26     contents = open(fname).read()
27     fields = contents.split()
28
29     in_key = False
30     for f in fields:
31         f = f.strip()
32         if f.startswith("ssh-"):
33             in_key = True
34             continue
35         elif in_key:
36             return base64.b64decode(f)
37         
38     return None
39
40
41 # openssh binary key format
42 #
43 # a section:
44 # length = 4 bytes (32-bit big-endian integer)
45 # data = length bytes of string 
46
47 # sections of the key ( for RSA )
48 # [key-type (in ASCII)] [public exponent (bignum)] [primes (bignum)]
49 #
50 # sections of the key ( for DSA )
51 # [key-type (in ASCII)] [p (bignum)] [q (bignum)] [g (bignum)] [y (bignum)]
52 #
53 # - baris
54 def read_key(key):
55     
56     def read_length(key):
57         length = key[0:4]
58         length = struct.unpack(">l", length)[0]
59         return length, key
60         
61     def read_values(key, count):
62         v = []
63         for i in range(count):
64             length, key = read_length(key)
65             size = 4 + length
66             v.append(key[:size])
67             key = key[size:]
68         return v
69
70     length, key = read_length(key)
71     key = key[4:]
72     key_type = key[:length]
73     key = key[length:]
74
75     if key_type == "ssh-rsa":
76         # prepare parameters for RSA.new_pub_key
77         v = read_values(key, 2)
78         e, n = v[0], v[1]
79         return key_type, e, n
80
81     elif key_type == "ssh-dss":
82         # prepare parameters for DSA.set_params
83         v = read_values(key, 4)
84         p, q, g, y = v[0], v[1], v[2], v[3]
85         return key_type, p, q, g, y
86
87
88 def convert(fin, fout):
89     key = decode_key(fin)
90     ret = read_key(key)
91     key_type = ret[0]
92
93     if key_type == "ssh-rsa":
94         e, n = ret[1:]
95         rsa = rsa_new_pub_key((e, n))
96         rsa.save_pem(fout)
97
98     elif key_type == "ssh-dss":
99         p, q, g, y = ret[1:]
100         dsa = DSA.set_params(p, q, g)
101         dsa.gen_key()
102         dsa.save_pub_key(fout)
103         # FIXME: This is wrong.
104         # M2Crypto doesn't allow us to set the public key parameter
105         raise(Exception, "DSA keys are not supported yet: M2Crypto doesn't allow us to set the public key parameter")
106
107
108 if __name__ == "__main__":
109     if len(sys.argv) != 3:
110         print "Usage: %s <input-file> <output-file>"
111         sys.exit(1)
112
113     fin = sys.argv[1]
114     fout = sys.argv[2]
115     convert(fin, fout)