| #!/usr/bin/env python3 | |
| """Detach CMS encrypted data. | |
| Detach encrypted data from a CMS envelopedData or authEnvelopedData | |
| message into a separate file. | |
| """ | |
| import argparse | |
| from pathlib import Path | |
| from asn1crypto.cms import ContentInfo, AuthEnvelopedData, EnvelopedData | |
| # Parse command-line arguments | |
| # | |
| parser = argparse.ArgumentParser( | |
| description=__doc__, | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| ) | |
| parser.add_argument("-d", "--data", metavar="FILE", type=Path, | |
| help="Write detached data (without envelope) to FILE") | |
| parser.add_argument("-e", "--envelope", metavar="FILE", type=Path, | |
| help="Write envelope (without data) to FILE") | |
| parser.add_argument("-o", "--overwrite", action="store_true", | |
| help="Overwrite output files") | |
| parser.add_argument("file", type=Path, help="Input envelope file") | |
| args = parser.parse_args() | |
| if args.data is None and args.envelope is None: | |
| parser.error("at least one of --data and --envelope is required") | |
| outmode = "wb" if args.overwrite else "xb" | |
| # Read input envelope | |
| # | |
| envelope = ContentInfo.load(args.file.read_bytes()) | |
| # Locate encrypted content info | |
| # | |
| content = envelope["content"] | |
| if type(content) is AuthEnvelopedData: | |
| encinfo = content["auth_encrypted_content_info"] | |
| elif type(content) is EnvelopedData: | |
| encinfo = content["encrypted_content_info"] | |
| else: | |
| parser.error("Input file does not contain any encrypted data") | |
| # Detach encrypted content data | |
| # | |
| data = encinfo["encrypted_content"] | |
| del encinfo["encrypted_content"] | |
| # Write envelope (without data), if applicable | |
| # | |
| if args.envelope: | |
| with args.envelope.open(mode=outmode) as fh: | |
| fh.write(envelope.dump()) | |
| # Write data (without envelope), if applicable | |
| # | |
| if args.data: | |
| with args.data.open(mode=outmode) as fh: | |
| fh.write(data.contents) |