security/manager/ssl/tests/unit/psm_common_py/CertUtils.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/manager/ssl/tests/unit/psm_common_py/CertUtils.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,311 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +# This file requires openssl 1.0.0 at least
     1.9 +
    1.10 +import os
    1.11 +import random
    1.12 +import pexpect
    1.13 +import time
    1.14 +import sys
    1.15 +
    1.16 +def init_dsa(db_dir):
    1.17 +    """
    1.18 +    Initialize dsa parameters
    1.19 +
    1.20 +    Sets up a set of params to be reused for DSA key generation
    1.21 +
    1.22 +    Arguments:
    1.23 +      db_dir     -- location of the temporary params for the certificate
    1.24 +    """
    1.25 +    dsa_key_params = db_dir + "/dsa_param.pem"
    1.26 +    os.system("openssl dsaparam -out " + dsa_key_params + " 2048")
    1.27 +
    1.28 +
    1.29 +def generate_cert_generic(db_dir, dest_dir, serial_num,  key_type, name,
    1.30 +                          ext_text, signer_key_filename = "",
    1.31 +                          signer_cert_filename = "",
    1.32 +                          subject_string = ""):
    1.33 +    """
    1.34 +    Generate an x509 certificate with a sha256 signature
    1.35 +
    1.36 +    Preconditions:
    1.37 +      if dsa keys are to be generated init_dsa must have been called before.
    1.38 +
    1.39 +
    1.40 +    Arguments:
    1.41 +      db_dir     -- location of the temporary params for the certificate
    1.42 +      dest_dir   -- location of the x509 cert
    1.43 +      serial_num -- serial number for the cert (must be unique for each signer
    1.44 +                    key)
    1.45 +      key_type   -- the type of key generated: potential values: 'rsa', 'dsa',
    1.46 +                    or any of the curves found by 'openssl ecparam -list_curves'
    1.47 +      name       -- the common name for the cert, will match the prefix of the
    1.48 +                    output cert
    1.49 +      ext_text   -- the text for the x509 extensions to be added to the
    1.50 +                    certificate
    1.51 +      signer_key_filename -- the filename of the key from which the cert will
    1.52 +                    be signed if null the cert will be self signed (think CA
    1.53 +                    roots).
    1.54 +      signer_cert_filename -- the certificate that will sign the certificate
    1.55 +                    (used to extract signer info) it must be in DER format.
    1.56 +
    1.57 +    output:
    1.58 +      key_name   -- the filename of the key file (PEM format)
    1.59 +      cert_name  -- the filename of the output certificate (DER format)
    1.60 +    """
    1.61 +    key_name = db_dir + "/"+ name + ".key"
    1.62 +    if key_type == 'rsa':
    1.63 +      os.system ("openssl genpkey -algorithm RSA -out " + key_name +
    1.64 +                 " -pkeyopt rsa_keygen_bits:2048")
    1.65 +    elif key_type == 'dsa':
    1.66 +      dsa_key_params = db_dir + "/dsa_param.pem"
    1.67 +      os.system("openssl gendsa -out " + key_name + "  " + dsa_key_params)
    1.68 +    else:
    1.69 +      #assume is ec
    1.70 +      os.system("openssl ecparam -out " + key_name + " -name "+ key_type +
    1.71 +                " -genkey");
    1.72 +    csr_name =  db_dir + "/"+ name + ".csr"
    1.73 +    if not subject_string:
    1.74 +      subject_string = '/CN=' + name
    1.75 +    os.system ("openssl req -new -key " + key_name + " -days 3650" +
    1.76 +               " -extensions v3_ca -batch -out " + csr_name +
    1.77 +               " -utf8 -subj '" + subject_string + "'")
    1.78 +
    1.79 +    extensions_filename = db_dir + "/openssl-exts"
    1.80 +    f = open(extensions_filename,'w')
    1.81 +    f.write(ext_text)
    1.82 +    f.close()
    1.83 +
    1.84 +    cert_name =  dest_dir + "/"+ name + ".der"
    1.85 +    if not signer_key_filename:
    1.86 +        signer_key_filename = key_name;
    1.87 +        os.system ("openssl x509 -req -sha256 -days 3650 -in " + csr_name +
    1.88 +                   " -signkey " + signer_key_filename +
    1.89 +                   " -set_serial " + str(serial_num) +
    1.90 +                   " -extfile " + extensions_filename +
    1.91 +                   " -outform DER -out "+ cert_name)
    1.92 +    else:
    1.93 +        os.system ("openssl x509 -req -sha256 -days 3650 -in " + csr_name +
    1.94 +                   " -CAkey " + signer_key_filename +
    1.95 +                   " -CA " + signer_cert_filename + " -CAform DER " +
    1.96 +                   " -set_serial " + str(serial_num) + " -out " + cert_name +
    1.97 +                   " -outform DER  -extfile " + extensions_filename)
    1.98 +    return key_name, cert_name
    1.99 +
   1.100 +
   1.101 +
   1.102 +def generate_int_and_ee(db_dir, dest_dir, ca_key, ca_cert, name, int_ext_text,
   1.103 +                        ee_ext_text, key_type = 'rsa'):
   1.104 +    """
   1.105 +    Generate an intermediate and ee signed by the generated intermediate. The
   1.106 +    name of the intermediate files will be the name '.der' or '.key'. The name
   1.107 +    of the end entity files with be "ee-"+ name plus the appropiate prefixes.
   1.108 +    The serial number will be generated radomly so it is potentially possible
   1.109 +    to have problem (but very unlikely).
   1.110 +
   1.111 +    Arguments:
   1.112 +      db_dir     -- location of the temporary params for the certificate
   1.113 +      dest_dir   -- location of the x509 cert
   1.114 +      ca_key     -- The filename of the key that will be used to sign the
   1.115 +                    intermediate (PEM FORMAT)
   1.116 +      ca_cert    -- The filename of the cert that will be used to sign the
   1.117 +                    intermediate, it MUST be the private key for the ca_key.
   1.118 +                    The file must be in DER format.
   1.119 +      name       -- the common name for the intermediate, will match the prefix
   1.120 +                    of the output intermediate. The ee will have the name
   1.121 +                    prefixed with "ee-"
   1.122 +      int_ext_text  -- the text for the x509 extensions to be added to the
   1.123 +                    intermediate certificate
   1.124 +      ee_ext_text  -- the text for the x509 extensions to be added to the
   1.125 +                    end entity certificate
   1.126 +      key_type   -- the type of key generated: potential values: 'rsa', 'dsa',
   1.127 +                    or any of the curves found by 'openssl ecparam -list_curves'
   1.128 +
   1.129 +    output:
   1.130 +      int_key   -- the filename of the intermeidate key file (PEM format)
   1.131 +      int_cert  -- the filename of the intermediate certificate (DER format)
   1.132 +      ee_key    -- the filename of the end entity key file (PEM format)
   1.133 +      ee_cert   -- the filename of the end entity certficate (DER format)
   1.134 +
   1.135 +    """
   1.136 +    [int_key, int_cert] = generate_cert_generic(db_dir, dest_dir,
   1.137 +                                                random.randint(100,40000000),
   1.138 +                                                key_type, "int-" + name,
   1.139 +                                                int_ext_text,
   1.140 +                                                ca_key, ca_cert)
   1.141 +    [ee_key, ee_cert] = generate_cert_generic(db_dir, dest_dir,
   1.142 +                                              random.randint(100,40000000),
   1.143 +                                              key_type,  name,
   1.144 +                                              ee_ext_text, int_key, int_cert)
   1.145 +
   1.146 +    return int_key, int_cert, ee_key, ee_cert
   1.147 +
   1.148 +def generate_pkcs12(db_dir, dest_dir, der_cert_filename, key_pem_filename,
   1.149 +                    prefix):
   1.150 +    """
   1.151 +    Generate a pkcs12 file for a given certificate  name (in der format) and
   1.152 +    a key filename (key in pem format). The output file will have an empty
   1.153 +    password.
   1.154 +
   1.155 +    Arguments:
   1.156 +    input:
   1.157 +      db_dir     -- location of the temporary params for the certificate
   1.158 +      dest_dir   -- location of the x509 cert
   1.159 +      der_cert_filename -- the filename of the certificate to be included in the
   1.160 +                           output file (DER format)
   1.161 +      key_pem_filename  -- the filename of the private key of the certificate to
   1.162 +                           (PEM format)
   1.163 +      prefix     -- the name to be prepended to the output pkcs12 file.
   1.164 +    output:
   1.165 +      pk12_filename -- the filename of the outgoing pkcs12 output file
   1.166 +    """
   1.167 +    #make pem cert file  from der filename
   1.168 +    pem_cert_filename = db_dir + "/" + prefix + ".pem"
   1.169 +    pk12_filename = dest_dir + "/" + prefix + ".p12"
   1.170 +    os.system("openssl x509 -inform der -in " + der_cert_filename + " -out " +
   1.171 +                pem_cert_filename )
   1.172 +    #now make pkcs12 file
   1.173 +    child = pexpect.spawn("openssl pkcs12 -export -in " + pem_cert_filename +
   1.174 +                          " -inkey " + key_pem_filename + " -out " +
   1.175 +                          pk12_filename)
   1.176 +    child.expect('Enter Export Password:')
   1.177 +    child.sendline('')
   1.178 +    child.expect('Verifying - Enter Export Password:')
   1.179 +    child.sendline('')
   1.180 +    child.expect(pexpect.EOF)
   1.181 +    return pk12_filename
   1.182 +
   1.183 +def init_nss_db(db_dir):
   1.184 +    """
   1.185 +    Remove the current nss database in the specified directory and create a new
   1.186 +    nss database with the cert8 format.
   1.187 +    Arguments
   1.188 +      db_dir -- the desired location of the new database
   1.189 +    output
   1.190 +     noise_file -- the path to a noise file suitable to generate TEST
   1.191 +                   certificates. This does not have enough entropy for a real
   1.192 +                   secret
   1.193 +     pwd_file   -- the patch to the secret file used for the database.
   1.194 +                   this file should be empty.
   1.195 +    """
   1.196 +    nss_db_files = ["cert8.db", "key3.db", "secmod.db", "noise", "pwdfile"]
   1.197 +    for file in nss_db_files:
   1.198 +        if os.path.isfile(file):
   1.199 +            os.remove(file)
   1.200 +    # create noise file
   1.201 +    noise_file = db_dir + "/noise"
   1.202 +    nf = open(noise_file, 'w')
   1.203 +    nf.write(str(time.time()))
   1.204 +    nf.close()
   1.205 +    # create pwd file
   1.206 +    pwd_file = db_dir + "/pwfile"
   1.207 +    pf = open(pwd_file, 'w')
   1.208 +    pf.write("\n")
   1.209 +    pf.close()
   1.210 +    # create nss db
   1.211 +    os.system("certutil -d " + db_dir + " -N -f " + pwd_file);
   1.212 +    return [noise_file, pwd_file]
   1.213 +
   1.214 +def generate_ca_cert(db_dir, dest_dir, noise_file, name, version, do_bc):
   1.215 +    """
   1.216 +    Creates a new CA certificate in an sql NSS database and as a der file
   1.217 +    Arguments:
   1.218 +      db_dir     -- the location of the nss database (in sql format)
   1.219 +      dest_dir   -- the location of for the output file
   1.220 +      noise_file -- the location of a noise file.
   1.221 +      name       -- the nickname of the new certificate in the database and the
   1.222 +                    common name of the certificate
   1.223 +      version    -- the version number of the certificate (valid certs must use
   1.224 +                    3)
   1.225 +      do_bc      -- if the certificate should include the basic constraints
   1.226 +                    (valid ca's should be true)
   1.227 +    output:
   1.228 +      outname    -- the location of the der file.
   1.229 +    """
   1.230 +    out_name = dest_dir + "/" + name + ".der"
   1.231 +    base_exec_string = ("certutil -S -z " + noise_file + " -g 2048 -d " +
   1.232 +                        db_dir + "/ -n " + name + " -v 120 -s 'CN=" + name +
   1.233 +                        ",O=PSM Testing,L=Mountain View,ST=California,C=US'" +
   1.234 +                        " -t C,C,C -x --certVersion=" + str(int(version)))
   1.235 +    if (do_bc):
   1.236 +        child = pexpect.spawn(base_exec_string + " -2")
   1.237 +        child.logfile = sys.stdout
   1.238 +        child.expect('Is this a CA certificate \[y/N\]?')
   1.239 +        child.sendline('y')
   1.240 +        child.expect('Enter the path length constraint, enter to skip \[<0 for unlimited path\]: >')
   1.241 +        child.sendline('')
   1.242 +        child.expect('Is this a critical extension \[y/N\]?')
   1.243 +        child.sendline('')
   1.244 +        child.expect(pexpect.EOF)
   1.245 +    else:
   1.246 +        os.system(base_exec_string)
   1.247 +    os.system("certutil -d " + db_dir + "/ -L -n " + name + " -r > " +
   1.248 +              out_name)
   1.249 +    return out_name
   1.250 +
   1.251 +def generate_child_cert(db_dir, dest_dir, noise_file, name, ca_nick, version,
   1.252 +                        do_bc, is_ee, ocsp_url):
   1.253 +    """
   1.254 +    Creates a new child certificate in an sql NSS database and as a der file
   1.255 +    Arguments:
   1.256 +      db_dir     -- the location of the nss database (in sql format)
   1.257 +      dest_dir   -- the location of for the output file
   1.258 +      noise_file -- the location of a noise file.
   1.259 +      name       -- the nickname of the new certificate in the database and the
   1.260 +                    common name of the certificate
   1.261 +      ca_nick    -- the nickname of the isser of the new certificate
   1.262 +      version    -- the version number of the certificate (valid certs must use
   1.263 +                    3)
   1.264 +      do_bc      -- if the certificate should include the basic constraints
   1.265 +                    (valid ca's should be true)
   1.266 +      is_ee      -- is this and End Entity cert? false means intermediate
   1.267 +      ocsp_url   -- optional location of the ocsp responder for this certificate
   1.268 +                    this is included only if do_bc is set to True
   1.269 +    output:
   1.270 +      outname    -- the location of the der file.
   1.271 +    """
   1.272 +
   1.273 +    out_name = dest_dir + "/" + name + ".der"
   1.274 +    base_exec_string = ("certutil -S -z " + noise_file + " -g 2048 -d " +
   1.275 +                        db_dir + "/ -n " + name + " -v 120 -m " +
   1.276 +                        str(random.randint(100, 40000000)) + " -s 'CN=" + name +
   1.277 +                        ",O=PSM Testing,L=Mountain View,ST=California,C=US'" +
   1.278 +                        " -t C,C,C -c " + ca_nick + " --certVersion=" +
   1.279 +                        str(int(version)))
   1.280 +    if (do_bc):
   1.281 +        extra_arguments = " -2"
   1.282 +        if (ocsp_url):
   1.283 +            extra_arguments += " --extAIA"
   1.284 +        child = pexpect.spawn(base_exec_string + extra_arguments)
   1.285 +        child.logfile = sys.stdout
   1.286 +        child.expect('Is this a CA certificate \[y/N\]?')
   1.287 +        if (is_ee):
   1.288 +            child.sendline('N')
   1.289 +        else:
   1.290 +            child.sendline('y')
   1.291 +        child.expect('Enter the path length constraint, enter to skip \[<0 for unlimited path\]: >')
   1.292 +        child.sendline('')
   1.293 +        child.expect('Is this a critical extension \[y/N\]?')
   1.294 +        child.sendline('')
   1.295 +        if (ocsp_url):
   1.296 +            child.expect('\s+Choice >')
   1.297 +            child.sendline('2')
   1.298 +            child.expect('\s+Choice: >')
   1.299 +            child.sendline('7')
   1.300 +            child.expect('Enter data:')
   1.301 +            child.sendline(ocsp_url)
   1.302 +            child.expect('\s+Choice: >')
   1.303 +            child.sendline('0')
   1.304 +            child.expect('Add another location to the Authority Information Access extension \[y/N\]')
   1.305 +            child.sendline('')
   1.306 +            child.expect('Is this a critical extension \[y/N\]?')
   1.307 +            child.sendline('')
   1.308 +        child.expect(pexpect.EOF)
   1.309 +    else:
   1.310 +        os.system(base_exec_string)
   1.311 +    os.system("certutil -d " + db_dir + "/ -L -n " + name + " -r > " +
   1.312 +              out_name)
   1.313 +    return out_name
   1.314 +

mercurial