|
|
@@ -0,0 +1,144 @@ |
|
|
|
--[[ |
|
|
|
Copyright (c) 2023, Vsevolod Stakhov <vsevolod@rspamd.com> |
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
you may not use this file except in compliance with the License. |
|
|
|
You may obtain a copy of the License at |
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software |
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, |
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
|
|
See the License for the specific language governing permissions and |
|
|
|
limitations under the License. |
|
|
|
]]-- |
|
|
|
|
|
|
|
local argparse = require "argparse" |
|
|
|
local rspamd_util = require "rspamd_util" |
|
|
|
|
|
|
|
local parser = argparse() |
|
|
|
:name 'rspamadm dkim_keygen' |
|
|
|
:description 'Create key pairs for dkim signing' |
|
|
|
:help_description_margin(30) |
|
|
|
parser:option '-d --domain' |
|
|
|
:description 'Create a key for a specific domain' |
|
|
|
:default "example.com" |
|
|
|
parser:option '-s --selector' |
|
|
|
:description 'Create a key for a specific DKIM selector' |
|
|
|
:default "mail" |
|
|
|
parser:option '-k --privkey' |
|
|
|
:description 'Save private key to file instead of printing it to stdout' |
|
|
|
parser:option '-b --bits' |
|
|
|
:convert(function(input) |
|
|
|
local n = tonumber(input) |
|
|
|
if not n or n < 512 or n > 4096 then |
|
|
|
return nil |
|
|
|
end |
|
|
|
return n |
|
|
|
end) |
|
|
|
:description 'Generate an RSA key with the specified number of bits (512 to 4096)' |
|
|
|
:default(1024) |
|
|
|
parser:option '-t --type' |
|
|
|
:description 'Key type: RSA, ED25519 or ED25119-seed' |
|
|
|
:convert { |
|
|
|
['rsa'] = 'rsa', |
|
|
|
['RSA'] = 'rsa', |
|
|
|
['ed25519'] = 'ed25519', |
|
|
|
['ED25519'] = 'ed25519', |
|
|
|
['ed25519-seed'] = 'ed25519-seed', |
|
|
|
['ED25519-seed'] = 'ed25519-seed', |
|
|
|
} |
|
|
|
:default 'rsa' |
|
|
|
parser:option '-o --output' |
|
|
|
:description 'Output public key in the following format: dns or plain' |
|
|
|
:convert { |
|
|
|
['dns'] = 'dns', |
|
|
|
['plain'] = 'plain', |
|
|
|
} |
|
|
|
:default 'dns' |
|
|
|
parser:option '--priv-output' |
|
|
|
:description 'Output private key in the following format: PEM or DER (for RSA)' |
|
|
|
:convert { |
|
|
|
['pem'] = 'pem', |
|
|
|
['der'] = 'der', |
|
|
|
} |
|
|
|
:default 'pem' |
|
|
|
|
|
|
|
local function split_string(input, max_length) |
|
|
|
max_length = max_length or 253 |
|
|
|
local pieces = {} |
|
|
|
local index = 1 |
|
|
|
|
|
|
|
while index <= #input do |
|
|
|
local piece = input:sub(index, index + max_length - 1) |
|
|
|
table.insert(pieces, piece) |
|
|
|
index = index + max_length |
|
|
|
end |
|
|
|
|
|
|
|
return pieces |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
local function print_public_key_dns(opts, base64_pk) |
|
|
|
if #base64_pk < 255 - 2 then |
|
|
|
io.write(string.format('%s._domainkey IN TXT ( "v=DKIM1; k=rsa;" \n\t"p=%s" ) ;\n', opts.selector, base64_pk)) |
|
|
|
else |
|
|
|
-- Split it by parts |
|
|
|
local parts = split_string(base64_pk) |
|
|
|
io.write(string.format('%s._domainkey IN TXT ( "v=DKIM1; k=rsa; "\n', opts.selector)) |
|
|
|
for i,part in ipairs(parts) do |
|
|
|
if i == 1 then |
|
|
|
io.write(string.format('\t"p=%s"\n', part)) |
|
|
|
else |
|
|
|
io.write(string.format('\t"%s"\n', part)) |
|
|
|
end |
|
|
|
end |
|
|
|
io.write(") ; \n") |
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
local function print_public_key(opts, pk) |
|
|
|
local base64_pk = tostring(rspamd_util.encode_base64(pk)) |
|
|
|
if opts.output == 'plain' then |
|
|
|
io.write(base64_pk) |
|
|
|
io.write("\n") |
|
|
|
elseif opts.output == 'dns' then |
|
|
|
print_public_key_dns(opts, base64_pk) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
local function gen_rsa_key(opts) |
|
|
|
local rsa = require "rspamd_rsa" |
|
|
|
|
|
|
|
local sk,pk = rsa.keypair(opts.bits or 1024) |
|
|
|
if opts.privkey then |
|
|
|
sk:save(opts.privkey, opts.priv_output) |
|
|
|
else |
|
|
|
sk:save("-", opts.priv_output) |
|
|
|
end |
|
|
|
|
|
|
|
print_public_key(opts, tostring(pk)) |
|
|
|
end |
|
|
|
|
|
|
|
local function handler(args) |
|
|
|
local opts = parser:parse(args) |
|
|
|
|
|
|
|
if not opts then os.exit(1) end |
|
|
|
|
|
|
|
if opts.type == 'rsa' then |
|
|
|
gen_rsa_key(opts) |
|
|
|
else |
|
|
|
gen_eddsa_key(opts) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
return { |
|
|
|
name = 'dkim_keygen', |
|
|
|
aliases = {'dkimkeygen'}, |
|
|
|
handler = handler, |
|
|
|
description = parser._description |
|
|
|
} |
|
|
|
|
|
|
|
|