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 +