tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pypkcs12.py (3459B)


      1 #!/usr/bin/env python
      2 #
      3 # This Source Code Form is subject to the terms of the Mozilla Public
      4 # License, v. 2.0. If a copy of the MPL was not distributed with this
      5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      6 
      7 """
      8 Reads a specification from stdin or a file and outputs a PKCS12
      9 file with the desired properties.
     10 
     11 The input format currently consists of a pycert certificate
     12 specification (see pycert.py).
     13 Currently, keys other than the default key are not supported.
     14 The password that is used to encrypt and authenticate the file
     15 is "password".
     16 """
     17 
     18 import base64
     19 import os
     20 import shutil
     21 import subprocess
     22 import sys
     23 
     24 import mozinfo
     25 import pycert
     26 import pykey
     27 from mozfile import NamedTemporaryFile
     28 
     29 
     30 class Error(Exception):
     31    """Base class for exceptions in this module."""
     32 
     33    pass
     34 
     35 
     36 class OpenSSLError(Error):
     37    """Class for handling errors when calling OpenSSL."""
     38 
     39    def __init__(self, status):
     40        super().__init__()
     41        self.status = status
     42 
     43    def __str__(self):
     44        return "Error running openssl: %s " % self.status
     45 
     46 
     47 def runUtil(util, args):
     48    env = os.environ.copy()
     49    if mozinfo.os == "linux":
     50        pathvar = "LD_LIBRARY_PATH"
     51        app_path = os.path.dirname(util)
     52        if pathvar in env:
     53            env[pathvar] = "%s%s%s" % (app_path, os.pathsep, env[pathvar])
     54        else:
     55            env[pathvar] = app_path
     56    proc = subprocess.run(
     57        [util] + args,
     58        check=False,
     59        env=env,
     60        text=True,
     61    )
     62    return proc.returncode
     63 
     64 
     65 class PKCS12:
     66    """Utility class for reading a specification and generating
     67    a PKCS12 file"""
     68 
     69    def __init__(self, paramStream):
     70        self.cert = pycert.Certificate(paramStream)
     71        self.key = pykey.keyFromSpecification("default")
     72 
     73    def toDER(self):
     74        with NamedTemporaryFile(mode="wt+") as certTmp, NamedTemporaryFile(
     75            mode="wt+"
     76        ) as keyTmp, NamedTemporaryFile(mode="rb+") as pkcs12Tmp:
     77            certTmp.write(self.cert.toPEM())
     78            certTmp.flush()
     79            keyTmp.write(self.key.toPEM())
     80            keyTmp.flush()
     81            openssl = shutil.which("openssl")
     82            status = runUtil(
     83                openssl,
     84                [
     85                    "pkcs12",
     86                    "-export",
     87                    "-inkey",
     88                    keyTmp.name,
     89                    "-in",
     90                    certTmp.name,
     91                    "-out",
     92                    pkcs12Tmp.name,
     93                    "-passout",
     94                    "pass:password",
     95                ],
     96            )
     97            if status != 0:
     98                raise OpenSSLError(status)
     99            return pkcs12Tmp.read()
    100 
    101    def toPEM(self):
    102        output = "-----BEGIN PKCS12-----"
    103        der = self.toDER()
    104        b64 = base64.b64encode(der).decode()
    105        while b64:
    106            output += "\n" + b64[:64]
    107            b64 = b64[64:]
    108        output += "\n-----END PKCS12-----"
    109        return output
    110 
    111 
    112 # The build harness will call this function with an output
    113 # file-like object and a path to a file containing a
    114 # specification. This will read the specification and output
    115 # the PKCS12 file.
    116 def main(output, inputPath):
    117    with open(inputPath) as configStream:
    118        output.write(PKCS12(configStream).toDER())
    119 
    120 
    121 # When run as a standalone program, this will read a specification from
    122 # stdin and output the PKCS12 file as PEM to stdout.
    123 if __name__ == "__main__":
    124    print(PKCS12(sys.stdin).toPEM())