-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contrib: Add a helper script for generating keys
Co-authored-by: lquidfire <[email protected]>
- Loading branch information
1 parent
a0d310d
commit 1987780
Showing
10 changed files
with
392 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
include = ["contrib/openarc-keygen"] | ||
line-length = 160 | ||
|
||
[lint] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ | |
# All rights reserved. | ||
|
||
SUBDIRS = init spec systemd | ||
|
||
dist_bin_SCRIPTS = openarc-keygen | ||
man_MANS = openarc-keygen.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# Copyright 2024 OpenARC contributors. | ||
# See LICENSE. | ||
|
||
import argparse | ||
import os | ||
import subprocess | ||
import sys | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
# fmt: off | ||
parser.add_argument( | ||
'-b', '--bits', | ||
type=int, | ||
default=2048, | ||
help='Size of RSA key to generate.', | ||
) | ||
parser.add_argument( | ||
'-d', '--domain', | ||
required=True, | ||
help='The domain which will use this key for signing.', | ||
) | ||
parser.add_argument( | ||
'-D', '--directory', | ||
help='Directory to store the keys in.', | ||
) | ||
parser.add_argument( | ||
'-f', '--format', | ||
default='zone', | ||
choices=['bare', 'testkey', 'text', 'zone'], | ||
help='output format for the public key', | ||
) | ||
parser.add_argument( | ||
'--fqdn', | ||
action='store_true', | ||
help='Use the fully qualified domain name when outputting a DNS zone entry', | ||
) | ||
parser.add_argument( | ||
'--hash-algorithms', | ||
help='Tag the generated DNS record for use with this colon-separated list of algorithms.', | ||
) | ||
parser.add_argument( | ||
'-n', '--note', | ||
help='Free-form text to include in the public key.' | ||
) | ||
parser.add_argument( | ||
'--no-subdomains', | ||
action='store_true', | ||
help='Tag the public key to indicate that identities in a signature are required to be from this exact domain, not subdomains.', | ||
) | ||
parser.add_argument( | ||
'-r', '--restrict', | ||
action='store_true', | ||
help='Tag the public key to indicate that it should only be used for email.', | ||
) | ||
parser.add_argument( | ||
'-s', '--selector', | ||
required=True, | ||
help='A name for the key.', | ||
) | ||
parser.add_argument( | ||
'-t', '--type', | ||
default='rsa', | ||
choices=['rsa', 'ed25519'], | ||
help='Type of key to generate.', | ||
) | ||
parser.add_argument( | ||
'--testing', | ||
action='store_true', | ||
help='Tag the public key to indicate that this domain is testing its deployment of the protocol this key is used for.', | ||
) | ||
# fmt: on | ||
|
||
args = parser.parse_args() | ||
|
||
fname_base = f'{args.selector}._domainkey.{args.domain}' | ||
if args.directory: | ||
if not os.path.exists(args.directory): | ||
print(f'{args.directory} does not exist', file=sys.stderr) | ||
sys.exit(1) | ||
fname_base = os.path.join(args.directory, fname_base) | ||
|
||
binargs = [ | ||
'openssl', | ||
'genpkey', | ||
'-algorithm', | ||
args.type, | ||
'-outform', | ||
'PEM', | ||
'-out', | ||
f'{fname_base}.key', | ||
] | ||
|
||
if args.type == 'rsa': | ||
binargs.extend( | ||
[ | ||
'-pkeyopt', | ||
f'rsa_keygen_bits:{args.bits}', | ||
] | ||
) | ||
|
||
res = subprocess.run(binargs, capture_output=True, text=True) | ||
if res.returncode != 0: | ||
print(f'openssl returned error code {res.returncode} while generating the private key: {res.stderr}', file=sys.stderr) | ||
sys.exit(1) | ||
|
||
binargs = [ | ||
'openssl', | ||
'pkey', | ||
'-in', | ||
f'{fname_base}.key', | ||
'-inform', | ||
'PEM', | ||
'-outform', | ||
'PEM', | ||
'-pubout', | ||
] | ||
|
||
res = subprocess.run(binargs, capture_output=True, text=True) | ||
if res.returncode != 0: | ||
print(f'openssl returned error code {res.returncode} while extracting the public key: {res.stderr}', file=sys.stderr) | ||
sys.exit(1) | ||
|
||
pkey = ''.join(res.stdout.splitlines()[1:-1]) | ||
if args.type == 'ed25519': | ||
# This key type is published without the ASN1 prefix. Conveniently, | ||
# this prefix is 12 bytes so we can strip it off without decoding the | ||
# base64. | ||
pkey = pkey[16:] | ||
|
||
# Format the DNS record contents | ||
txt = f'v=DKIM1; k={args.type}' | ||
|
||
if args.hash_algorithms: | ||
txt += f'; h={args.hash_algorithms}' | ||
|
||
if args.note: | ||
txt += f'; n=\\"{args.note}\\"' | ||
|
||
if args.restrict: | ||
txt += '; s=email' | ||
|
||
flags = [] | ||
if args.testing: | ||
flags.append('y') | ||
if args.no_subdomains: | ||
flags.append('s') | ||
if flags: | ||
txt += f'; t={":".join(flags)}' | ||
|
||
txt += f'; p={pkey}' | ||
|
||
# Write it out | ||
with open(f'{fname_base}.txt', 'w') as f: | ||
if args.format == 'bare': | ||
f.write(pkey) | ||
elif args.format in ('testkey', 'text'): | ||
if args.format == 'testkey': | ||
f.write(f'{args.selector}._domainkey.{args.domain} ') | ||
f.write(txt.replace('\\"', '"')) | ||
else: | ||
f.write(f'{args.selector}._domainkey') | ||
if args.fqdn: | ||
f.write(f'.{args.domain}.') | ||
f.write('\tIN\tTXT\t( "') | ||
# Individual strings within a TXT record are limited to 255 bytes | ||
f.write('"\n\t"'.join(txt[i : i + 255] for i in range(0, len(txt), 255))) | ||
f.write('" )') | ||
f.write('\n') | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
.\" Copyright 2024 OpenARC contributors. | ||
.\" See LICENSE. | ||
.Dd 2024-10-30 | ||
.Dt OPENARC-KEYGEN 1 | ||
.Os OpenARC @VERSION@ | ||
|
||
.Sh NAME | ||
.Nm openarc-keygen | ||
.Nd DKIM (and ARC) key generation tool | ||
|
||
.Sh SYNOPSIS | ||
.Nm openarc-keygen | ||
.Fl d Ar domain | ||
.Fl s Ar selector | ||
.Op options | ||
|
||
.Sh DESCRIPTION | ||
.Nm openarc-keygen | ||
outputs a private key suitable for signing messages using | ||
.Xr openarc 8 | ||
and also outputs one of several representations of the associated | ||
public key, which can be used in various ways. | ||
|
||
The output filenames are based on the | ||
.Ar selector | ||
and | ||
.Ar domain ; | ||
the private key will end in ".key" and the public key will end in ".txt". | ||
|
||
.Sh OPTIONS | ||
|
||
.Bl -tag -width Ds | ||
.It Fl b , Fl \-bits Ar bits | ||
Size of RSA key to generate. | ||
The default is 2048, which is also the recommended minimum size. | ||
Keys smaller than 1024 bits will almost certainly be rejected by | ||
downstream evaluators. | ||
|
||
.It Fl d , Fl \-domain Ar domain | ||
The domain which will use this key for signing. | ||
|
||
.It Fl D , Fl -directory Ar directory | ||
Directory to store the keys in. | ||
If this is not specified the keys will be stored in the current | ||
working directory. | ||
|
||
.It Fl f , Fl \-format Brq Cm bare | Cm testkey | Cm text | Cm zone | ||
Output format for the public key. | ||
.Cm bare | ||
outputs just the key itself, rendering many flags that this program accepts | ||
irrelevant. | ||
.Cm testkey | ||
outputs a line suitable for use in a file pointed to by | ||
the | ||
.Cm TestKeys | ||
option in | ||
.Xr openarc.conf 5 . | ||
.Cm text | ||
outputs a standard textual representation of the key as specified in RFC 6376. | ||
.Cm zone | ||
is the default, and outputs a DNS record formatted for use in a zone file. | ||
|
||
.It Fl \-fqdn | ||
When outputting a DNS zone file entry, use the fully qualified domain name | ||
instead of a relative one. | ||
|
||
.It Fl \-hash-algorithms Ar algorithms | ||
Tag the public key to indicate that it should only be used with | ||
this colon-separated list of algorithms. | ||
|
||
.It Fl h , Fl \-help | ||
Show a help message and exit. | ||
|
||
.It Fl \-no\-subdomains | ||
Tag the public key to indicate that identities in a signature are | ||
required to be from this exact domain, not subdomains. | ||
|
||
.It Fl n , Fl \-note Ar note | ||
Free-form text to include in the public key. | ||
This is intended for humans who are reading the record, and should be | ||
kept brief if it is used at all. | ||
|
||
.It Fl r , Fl \-restrict | ||
Tag the public key to indicate that it should only be used for email. | ||
There are not currently any other protocols that might use the key, so | ||
this does not have any practical effect. | ||
|
||
.It Fl s , Fl \-selector Ar selector | ||
A name for the key. | ||
|
||
.It Fl t , Fl \-type Brq Cm rsa | Cm ed25519 | ||
Type of key to generate, defaults to RSA. | ||
Note that Ed25519 keys are not currently useful for ARC, nor are | ||
they usable by OpenARC. | ||
This option is for people who are generating DKIM keys for use with | ||
other software. | ||
|
||
.It Fl \-testing | ||
Tag the public key to indicate that this domain is testing its | ||
deployment of the protocol this key is used with. | ||
This is a signal that you are more interested in receiving feedback, | ||
it does not affect the handling of messages or signatures. | ||
|
||
.El | ||
|
||
.Sh NOTES | ||
A suitable | ||
.Nm openssl | ||
executable must be available in the executing user's | ||
.Ev PATH . | ||
|
||
.Sh EXAMPLES | ||
You may want to use | ||
.Xr sudo 8 | ||
to run this command as the user that the | ||
.Xr openarc 8 | ||
daemon is configured to run as, so that the file permissions are correct. | ||
|
||
.Dl sudo -u openarc openarc-keygen -D /etc/openarc/keys -d example.com -s 20241004 | ||
|
||
.Sh SEE ALSO | ||
.Bl -item | ||
.It | ||
.Xr openarc 8 | ||
.It | ||
.Xr openssl 1 | ||
.It | ||
RFC6376 - DomainKeys Identified Mail | ||
.It | ||
RFC8617 - The Authenticated Received Chain (ARC) Protocol | ||
.El |
Oops, something went wrong.