summaryrefslogtreecommitdiffstats
path: root/nova/crypto.py
diff options
context:
space:
mode:
authorJesse Andrews <anotherjesse@gmail.com>2010-05-27 23:05:26 -0700
committerJesse Andrews <anotherjesse@gmail.com>2010-05-27 23:05:26 -0700
commitbf6e6e718cdc7488e2da87b21e258ccc065fe499 (patch)
tree51cf4f72047eb6b16079c7fe21e9822895541801 /nova/crypto.py
downloadnova-bf6e6e718cdc7488e2da87b21e258ccc065fe499.tar.gz
nova-bf6e6e718cdc7488e2da87b21e258ccc065fe499.tar.xz
nova-bf6e6e718cdc7488e2da87b21e258ccc065fe499.zip
initial commit
Diffstat (limited to 'nova/crypto.py')
-rw-r--r--nova/crypto.py224
1 files changed, 224 insertions, 0 deletions
diff --git a/nova/crypto.py b/nova/crypto.py
new file mode 100644
index 000000000..6add55ee5
--- /dev/null
+++ b/nova/crypto.py
@@ -0,0 +1,224 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Wrappers around standard crypto, including root and intermediate CAs,
+SSH keypairs and x509 certificates.
+"""
+
+import hashlib
+import logging
+import os
+import shutil
+import tempfile
+import time
+import utils
+
+from nova import vendor
+import M2Crypto
+
+from nova import exception
+from nova import flags
+
+
+FLAGS = flags.FLAGS
+flags.DEFINE_string('ca_file', 'cacert.pem', 'Filename of root CA')
+flags.DEFINE_string('keys_path', utils.abspath('../keys'), 'Where we keep our keys')
+flags.DEFINE_string('ca_path', utils.abspath('../CA'), 'Where we keep our root CA')
+flags.DEFINE_boolean('use_intermediate_ca', False, 'Should we use intermediate CAs for each project?')
+
+
+def ca_path(username):
+ if username:
+ return "%s/INTER/%s/cacert.pem" % (FLAGS.ca_path, username)
+ return "%s/cacert.pem" % (FLAGS.ca_path)
+
+def fetch_ca(username=None, chain=True):
+ if not FLAGS.use_intermediate_ca:
+ username = None
+ buffer = ""
+ if username:
+ with open(ca_path(username),"r") as cafile:
+ buffer += cafile.read()
+ if username and not chain:
+ return buffer
+ with open(ca_path(None),"r") as cafile:
+ buffer += cafile.read()
+ return buffer
+
+def generate_key_pair(bits=1024):
+ # what is the magic 65537?
+
+ tmpdir = tempfile.mkdtemp()
+ keyfile = os.path.join(tmpdir, 'temp')
+ utils.execute('ssh-keygen -q -b %d -N "" -f %s' % (bits, keyfile))
+ (out, err) = utils.execute('ssh-keygen -q -l -f %s.pub' % (keyfile))
+ fingerprint = out.split(' ')[1]
+ private_key = open(keyfile).read()
+ public_key = open(keyfile + '.pub').read()
+
+ shutil.rmtree(tmpdir)
+ # code below returns public key in pem format
+ # key = M2Crypto.RSA.gen_key(bits, 65537, callback=lambda: None)
+ # private_key = key.as_pem(cipher=None)
+ # bio = M2Crypto.BIO.MemoryBuffer()
+ # key.save_pub_key_bio(bio)
+ # public_key = bio.read()
+ # public_key, err = execute('ssh-keygen -y -f /dev/stdin', private_key)
+
+ return (private_key, public_key, fingerprint)
+
+
+def ssl_pub_to_ssh_pub(ssl_public_key, name='root', suffix='nova'):
+ """requires lsh-utils"""
+ convert="sed -e'1d' -e'$d' | pkcs1-conv --public-key-info --base-64 |" \
+ + " sexp-conv | sed -e'1s/(rsa-pkcs1/(rsa-pkcs1-sha1/' | sexp-conv -s" \
+ + " transport | lsh-export-key --openssh"
+ (out, err) = utils.execute(convert, ssl_public_key)
+ if err:
+ raise exception.Error("Failed to generate key: %s", err)
+ return '%s %s@%s\n' %(out.strip(), name, suffix)
+
+
+def generate_x509_cert(subject="/C=US/ST=California/L=The Mission/O=CloudFed/OU=NOVA/CN=foo", bits=1024):
+ tmpdir = tempfile.mkdtemp()
+ keyfile = os.path.abspath(os.path.join(tmpdir, 'temp.key'))
+ csrfile = os.path.join(tmpdir, 'temp.csr')
+ logging.debug("openssl genrsa -out %s %s" % (keyfile, bits))
+ utils.runthis("Generating private key: %s", "openssl genrsa -out %s %s" % (keyfile, bits))
+ utils.runthis("Generating CSR: %s", "openssl req -new -key %s -out %s -batch -subj %s" % (keyfile, csrfile, subject))
+ private_key = open(keyfile).read()
+ csr = open(csrfile).read()
+ shutil.rmtree(tmpdir)
+ return (private_key, csr)
+
+
+def sign_csr(csr_text, intermediate=None):
+ if not FLAGS.use_intermediate_ca:
+ intermediate = None
+ if not intermediate:
+ return _sign_csr(csr_text, FLAGS.ca_path)
+ user_ca = "%s/INTER/%s" % (FLAGS.ca_path, intermediate)
+ if not os.path.exists(user_ca):
+ start = os.getcwd()
+ os.chdir(FLAGS.ca_path)
+ utils.runthis("Generating intermediate CA: %s", "sh geninter.sh %s" % (intermediate))
+ os.chdir(start)
+ return _sign_csr(csr_text, user_ca)
+
+
+def _sign_csr(csr_text, ca_folder):
+ tmpfolder = tempfile.mkdtemp()
+ csrfile = open("%s/inbound.csr" % (tmpfolder), "w")
+ csrfile.write(csr_text)
+ csrfile.close()
+ logging.debug("Flags path: %s" % ca_folder)
+ start = os.getcwd()
+ # Change working dir to CA
+ os.chdir(ca_folder)
+ utils.runthis("Signing cert: %s", "openssl ca -batch -out %s/outbound.crt -config ./openssl.cnf -infiles %s/inbound.csr" % (tmpfolder, tmpfolder))
+ os.chdir(start)
+ with open("%s/outbound.crt" % (tmpfolder), "r") as crtfile:
+ return crtfile.read()
+
+
+def mkreq(bits, subject="foo", ca=0):
+ pk = M2Crypto.EVP.PKey()
+ req = M2Crypto.X509.Request()
+ rsa = M2Crypto.RSA.gen_key(bits, 65537, callback=lambda: None)
+ pk.assign_rsa(rsa)
+ rsa = None # should not be freed here
+ req.set_pubkey(pk)
+ req.set_subject(subject)
+ req.sign(pk,'sha512')
+ assert req.verify(pk)
+ pk2 = req.get_pubkey()
+ assert req.verify(pk2)
+ return req, pk
+
+
+def mkcacert(subject='nova', years=1):
+ req, pk = mkreq(2048, subject, ca=1)
+ pkey = req.get_pubkey()
+ sub = req.get_subject()
+ cert = M2Crypto.X509.X509()
+ cert.set_serial_number(1)
+ cert.set_version(2)
+ cert.set_subject(sub) # FIXME subject is not set in mkreq yet
+ t = long(time.time()) + time.timezone
+ now = M2Crypto.ASN1.ASN1_UTCTIME()
+ now.set_time(t)
+ nowPlusYear = M2Crypto.ASN1.ASN1_UTCTIME()
+ nowPlusYear.set_time(t + (years * 60 * 60 * 24 * 365))
+ cert.set_not_before(now)
+ cert.set_not_after(nowPlusYear)
+ issuer = M2Crypto.X509.X509_Name()
+ issuer.C = "US"
+ issuer.CN = subject
+ cert.set_issuer(issuer)
+ cert.set_pubkey(pkey)
+ ext = M2Crypto.X509.new_extension('basicConstraints', 'CA:TRUE')
+ cert.add_ext(ext)
+ cert.sign(pk, 'sha512')
+
+ # print 'cert', dir(cert)
+ print cert.as_pem()
+ print pk.get_rsa().as_pem()
+
+ return cert, pk, pkey
+
+
+
+# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish, dis-
+# tribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to the fol-
+# lowing conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+# http://code.google.com/p/boto
+
+def compute_md5(fp):
+ """
+ @type fp: file
+ @param fp: File pointer to the file to MD5 hash. The file pointer will be
+ reset to the beginning of the file before the method returns.
+
+ @rtype: tuple
+ @return: the hex digest version of the MD5 hash
+ """
+ m = hashlib.md5()
+ fp.seek(0)
+ s = fp.read(8192)
+ while s:
+ m.update(s)
+ s = fp.read(8192)
+ hex_md5 = m.hexdigest()
+ # size = fp.tell()
+ fp.seek(0)
+ return hex_md5