source: fedd/federation/fedid.py @ 7321a43

version-1.30
Last change on this file since 7321a43 was 5d3f239, checked in by Ted Faber <faber@…>, 16 years ago

change package name to avoid conflicts with fedd on install

  • Property mode set to 100644
File size: 4.4 KB
Line 
1#!/usr/local/bin/python
2
3import os, sys
4import subprocess
5import tempfile
6import logging
7
8from M2Crypto import SSL, X509, EVP
9from pyasn1.codec.der import decoder
10
11# The version of M2Crypto on users is pretty old and doesn't have all the
12# features that are useful.  The legacy code is somewhat more brittle than the
13# main line, but will work.
14if "as_der" not in dir(EVP.PKey):
15    from asn1_raw import get_key_bits_from_file, get_key_bits_from_cert
16    legacy = True
17else:
18    legacy = False
19
20class fedid:
21    """
22    Wrapper around the federated ID from an X509 certificate.
23    """
24    HASHSIZE=20
25    def __init__(self, bits=None, hexstr=None, cert=None, file=None):
26        if bits != None:
27            self.set_bits(bits)
28        elif hexstr != None:
29            self.set_hexstr(hexstr)
30        elif cert != None:
31            self.set_cert(cert)
32        elif file != None:
33            self.set_file(file)
34        else:
35            self.buf = None
36
37    def __hash__(self):
38        return hash(self.buf)
39
40    def __eq__(self, other):
41        if isinstance(other, type(self)):
42            return self.buf == other.buf
43        elif isinstance(other, type(str())):
44            return self.buf == other;
45        else:
46            return False
47
48    def __ne__(self, other): return not self.__eq__(other)
49
50    def __str__(self):
51        if self.buf != None:
52            return str().join([ "%02x" % ord(x) for x in self.buf])
53        else: return ""
54
55    def __repr__(self):
56        return "fedid(hexstr='%s')" % self.__str__()
57
58    def pack_soap(self):
59        return self.buf
60
61    def pack_xmlrpc(self):
62        return self.buf
63
64    def digest_bits(self, bits):
65        """Internal function.  Compute the fedid from bits and store in buf"""
66        d = EVP.MessageDigest('sha1')
67        d.update(bits)
68        self.buf = d.final()
69
70
71    def set_hexstr(self, hexstr):
72        h = hexstr.replace(':','')
73        self.buf= str().join([chr(int(h[i:i+2],16)) \
74                for i in range(0,2*fedid.HASHSIZE,2)])
75
76    def get_hexstr(self):
77        """Return the hexstring representation of the fedid"""
78        return __str__(self)
79
80    def set_bits(self, bits):
81        """Set the fedid to bits(a 160 bit buffer)"""
82        self.buf = bits
83
84    def get_bits(self):
85        """Get the 160 bit buffer from the fedid"""
86        return self.buf
87
88    def set_file(self, file):
89        """Get the fedid from a certificate file
90
91        Calculate the SHA1 hash over the bit string of the public key as
92        defined in RFC3280.
93        """
94        self.buf = None
95        if legacy: self.digest_bits(get_key_bits_from_file(file))
96        else: self.set_cert(X509.load_cert(file))
97
98    def set_cert(self, cert):
99        """Get the fedid from a certificate.
100
101        Calculate the SHA1 hash over the bit string of the public key as
102        defined in RFC3280.
103        """
104
105        self.buf = None
106        if (cert != None):
107            if legacy:
108                self.digest_bits(get_key_bits_from_cert(cert))
109            else:
110                b = []
111                k = cert.get_pubkey()
112
113                # Getting the key was easy, but getting the bit string of the
114                # key requires a side trip through ASN.1
115                dec = decoder.decode(k.as_der())
116
117                # kv is a tuple of the bits in the key.  The loop below
118                # recombines these into bytes and then into a buffer for the
119                # SSL digest function.
120                kv =  dec[0].getComponentByPosition(1)
121                for i in range(0, len(kv), 8):
122                    v = 0
123                    for j in range(0, 8):
124                        v = (v << 1) + kv[i+j]
125                    b.append(v)
126                # The comprehension turns b from a list of bytes into a buffer
127                # (string) of bytes
128                self.digest_bits(str().join([chr(x) for x in b]))
129
130def generate_fedid(subj, bits=2048, log=None, dir=None, trace=sys.stderr,
131        ssl_prog="/usr/bin/openssl"):
132    """
133    Create a new certificate and derive a fedid from it.
134
135    The fedid and the certificate are returned as a tuple.
136    """
137
138    keypath = None
139    certpath = None
140    try:
141        try:
142            kd, keypath = tempfile.mkstemp(dir=dir, prefix="key",
143                    suffix=".pem")
144            cd, certpath = tempfile.mkstemp(dir=dir, prefix="cert",
145                    suffix=".pem")
146
147            cmd = [ssl_prog, "req", "-text", "-newkey", 
148                    "rsa:%d" % bits, "-keyout", keypath,  "-nodes", 
149                    "-subj", "/CN=%s" % subj, "-x509", "-days", "30", 
150                    "-out", certpath]
151
152            if log:
153                log.debug("[generate_fedid] %s" % " ".join(cmd))
154
155            if trace: call_out = trace
156            else: 
157                call_out = open("/dev/null", "w")
158
159            rv = subprocess.call(cmd, stdout=call_out, stderr=call_out)
160            log.debug("rv = %d" % rv)
161            if rv == 0:
162                cert = ""
163                for p in (certpath, keypath):
164                    f = open(p)
165                    for line in f:
166                        cert += line
167               
168                fid = fedid(file=certpath)
169                return (fid, cert)
170            else:
171                return (None, None)
172        except IOError, e:
173            raise e
174    finally:
175        if keypath: os.remove(keypath)
176        if certpath: os.remove(certpath)
Note: See TracBrowser for help on using the repository browser.