diff options
Diffstat (limited to '')
-rw-r--r-- | security/manager/tools/pypkcs12.py | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/security/manager/tools/pypkcs12.py b/security/manager/tools/pypkcs12.py new file mode 100644 index 0000000000..e21c52076f --- /dev/null +++ b/security/manager/tools/pypkcs12.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +""" +Reads a specification from stdin or a file and outputs a PKCS12 +file with the desired properties. + +The input format currently consists of a pycert certificate +specification (see pycert.py). +Currently, keys other than the default key are not supported. +The password that is used to encrypt and authenticate the file +is "password". +""" + +import base64 +import os +import subprocess +import sys +from distutils.spawn import find_executable + +import mozinfo +import pycert +import pykey +import six +from mozfile import NamedTemporaryFile + + +class Error(Exception): + """Base class for exceptions in this module.""" + + pass + + +class OpenSSLError(Error): + """Class for handling errors when calling OpenSSL.""" + + def __init__(self, status): + super(OpenSSLError, self).__init__() + self.status = status + + def __str__(self): + return "Error running openssl: %s " % self.status + + +def runUtil(util, args): + env = os.environ.copy() + if mozinfo.os == "linux": + pathvar = "LD_LIBRARY_PATH" + app_path = os.path.dirname(util) + if pathvar in env: + env[pathvar] = "%s%s%s" % (app_path, os.pathsep, env[pathvar]) + else: + env[pathvar] = app_path + proc = subprocess.run( + [util] + args, + env=env, + universal_newlines=True, + ) + return proc.returncode + + +class PKCS12(object): + """Utility class for reading a specification and generating + a PKCS12 file""" + + def __init__(self, paramStream): + self.cert = pycert.Certificate(paramStream) + self.key = pykey.keyFromSpecification("default") + + def toDER(self): + with NamedTemporaryFile(mode="wt+") as certTmp, NamedTemporaryFile( + mode="wt+" + ) as keyTmp, NamedTemporaryFile(mode="rb+") as pkcs12Tmp: + certTmp.write(self.cert.toPEM()) + certTmp.flush() + keyTmp.write(self.key.toPEM()) + keyTmp.flush() + openssl = find_executable("openssl") + status = runUtil( + openssl, + [ + "pkcs12", + "-export", + "-inkey", + keyTmp.name, + "-in", + certTmp.name, + "-out", + pkcs12Tmp.name, + "-passout", + "pass:password", + ], + ) + if status != 0: + raise OpenSSLError(status) + return pkcs12Tmp.read() + + def toPEM(self): + output = "-----BEGIN PKCS12-----" + der = self.toDER() + b64 = six.ensure_text(base64.b64encode(der)) + while b64: + output += "\n" + b64[:64] + b64 = b64[64:] + output += "\n-----END PKCS12-----" + return output + + +# The build harness will call this function with an output +# file-like object and a path to a file containing a +# specification. This will read the specification and output +# the PKCS12 file. +def main(output, inputPath): + with open(inputPath) as configStream: + output.write(PKCS12(configStream).toDER()) + + +# When run as a standalone program, this will read a specification from +# stdin and output the PKCS12 file as PEM to stdout. +if __name__ == "__main__": + print(PKCS12(sys.stdin).toPEM()) |