Added support for gradle and restructured the source code directory from src to src/main/java directory
This commit is contained in:
325
src/main/java/com/verisignlabs/dnssec/cl/CLBase.java
Normal file
325
src/main/java/com/verisignlabs/dnssec/cl/CLBase.java
Normal file
@@ -0,0 +1,325 @@
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.cli.AlreadySelectedException;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PosixParser;
|
||||
import org.apache.commons.cli.UnrecognizedOptionException;
|
||||
|
||||
import com.verisignlabs.dnssec.security.DnsKeyAlgorithm;
|
||||
|
||||
/**
|
||||
* This is a base class for jdnssec command line tools. Each command line tool
|
||||
* should inherit from this class, create a subclass of CLIStateBase (overriding
|
||||
* setupOptions and processOptions), and implement the execute() method.
|
||||
* Subclasses also have their own main() methods, which should just create the
|
||||
* subclass variant of the CLIState and call run().
|
||||
*/
|
||||
public abstract class CLBase
|
||||
{
|
||||
protected static Logger log;
|
||||
|
||||
/**
|
||||
* This is a very simple log formatter that simply outputs the log level and
|
||||
* log string.
|
||||
*/
|
||||
public static class BareLogFormatter extends Formatter
|
||||
{
|
||||
@Override
|
||||
public String format(LogRecord arg0)
|
||||
{
|
||||
StringBuilder out = new StringBuilder();
|
||||
String lvl = arg0.getLevel().getName();
|
||||
|
||||
out.append(lvl);
|
||||
out.append(": ");
|
||||
out.append(arg0.getMessage());
|
||||
out.append("\n");
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a base class for command line parsing state. Subclasses should
|
||||
* override setupOptions and processOptions.
|
||||
*/
|
||||
public static class CLIStateBase
|
||||
{
|
||||
protected Options opts;
|
||||
protected String usageStr;
|
||||
|
||||
/**
|
||||
* The base constructor. This will setup the command line options.
|
||||
*
|
||||
* @param usage
|
||||
* The command line usage string (e.g.,
|
||||
* "jdnssec-foo [..options..] zonefile")
|
||||
*/
|
||||
public CLIStateBase(String usage)
|
||||
{
|
||||
usageStr = usage;
|
||||
setup();
|
||||
}
|
||||
|
||||
/** This is the base set of command line options provided to all subclasses. */
|
||||
private void setup()
|
||||
{
|
||||
// Set up the standard set of options that all jdnssec command line tools will implement.
|
||||
opts = new Options();
|
||||
|
||||
// boolean options
|
||||
opts.addOption("h", "help", false, "Print this message.");
|
||||
opts.addOption("m", "multiline", false,
|
||||
"Output DNS records using 'multiline' format");
|
||||
|
||||
OptionBuilder.hasOptionalArg();
|
||||
OptionBuilder.withLongOpt("verbose");
|
||||
OptionBuilder.withArgName("level");
|
||||
OptionBuilder.withDescription("verbosity level -- 0 is silence, 3 is info, "
|
||||
+ "5 is debug information, 6 is trace information. default is level 2 (warning)");
|
||||
opts.addOption(OptionBuilder.create('v'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("alias:original:mnemonic");
|
||||
OptionBuilder.withLongOpt("alg-alias");
|
||||
OptionBuilder.withDescription("Define an alias for an algorithm");
|
||||
opts.addOption(OptionBuilder.create('A'));
|
||||
|
||||
setupOptions(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an overridable method for subclasses to add their own command
|
||||
* line options.
|
||||
*
|
||||
* @param opts
|
||||
* the options object to add (via OptionBuilder, typically) new
|
||||
* options to.
|
||||
*/
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
// Subclasses generally override this.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main method for parsing the command line arguments.
|
||||
* Subclasses generally override processOptions() rather than this method.
|
||||
* This method create the parsing objects and processes the standard
|
||||
* options.
|
||||
*
|
||||
* @param args
|
||||
* The command line arguments.
|
||||
* @throws ParseException
|
||||
*/
|
||||
public void parseCommandLine(String args[]) throws ParseException
|
||||
{
|
||||
CommandLineParser cli_parser = new PosixParser();
|
||||
CommandLine cli = cli_parser.parse(opts, args);
|
||||
|
||||
if (cli.hasOption('h')) usage();
|
||||
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
int value = parseInt(cli.getOptionValue('v'), -1);
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
rootLogger.setLevel(Level.OFF);
|
||||
break;
|
||||
case 1:
|
||||
rootLogger.setLevel(Level.SEVERE);
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
rootLogger.setLevel(Level.WARNING);
|
||||
break;
|
||||
case 3:
|
||||
rootLogger.setLevel(Level.INFO);
|
||||
break;
|
||||
case 4:
|
||||
rootLogger.setLevel(Level.CONFIG);
|
||||
case 5:
|
||||
rootLogger.setLevel(Level.FINE);
|
||||
break;
|
||||
case 6:
|
||||
rootLogger.setLevel(Level.ALL);
|
||||
break;
|
||||
}
|
||||
|
||||
// I hate java.util.logging, btw.
|
||||
for (Handler h : rootLogger.getHandlers())
|
||||
{
|
||||
h.setLevel(rootLogger.getLevel());
|
||||
h.setFormatter(new BareLogFormatter());
|
||||
}
|
||||
|
||||
if (cli.hasOption('m'))
|
||||
{
|
||||
org.xbill.DNS.Options.set("multiline");
|
||||
}
|
||||
|
||||
String[] optstrs = null;
|
||||
if ((optstrs = cli.getOptionValues('A')) != null)
|
||||
{
|
||||
for (int i = 0; i < optstrs.length; i++)
|
||||
{
|
||||
addArgAlias(optstrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
processOptions(cli);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process additional tool-specific options. Subclasses generally override
|
||||
* this.
|
||||
*
|
||||
* @param cli
|
||||
* The {@link CommandLine} object containing the parsed command
|
||||
* line state.
|
||||
*/
|
||||
protected void processOptions(CommandLine cli) throws ParseException
|
||||
{
|
||||
// Subclasses generally override this.
|
||||
}
|
||||
|
||||
/** Print out the usage and help statements, then quit. */
|
||||
public void usage()
|
||||
{
|
||||
HelpFormatter f = new HelpFormatter();
|
||||
|
||||
PrintWriter out = new PrintWriter(System.err);
|
||||
|
||||
// print our own usage statement:
|
||||
f.printHelp(out, 75, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
|
||||
HelpFormatter.DEFAULT_DESC_PAD, null);
|
||||
|
||||
out.flush();
|
||||
System.exit(64);
|
||||
|
||||
}
|
||||
|
||||
protected void addArgAlias(String s)
|
||||
{
|
||||
if (s == null) return;
|
||||
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
|
||||
String[] v = s.split(":");
|
||||
if (v.length < 2) return;
|
||||
|
||||
int alias = parseInt(v[0], -1);
|
||||
if (alias <= 0) return;
|
||||
int orig = parseInt(v[1], -1);
|
||||
if (orig <= 0) return;
|
||||
String mn = null;
|
||||
if (v.length > 2) mn = v[2];
|
||||
|
||||
algs.addAlias(alias, mn, orig);
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int def)
|
||||
{
|
||||
try
|
||||
{
|
||||
int v = Integer.parseInt(s);
|
||||
return v;
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a date/time from a command line time/offset duration string.
|
||||
*
|
||||
* @param start
|
||||
* the start time to calculate offsets from.
|
||||
* @param duration
|
||||
* the time/offset string to parse.
|
||||
* @return the calculated time.
|
||||
*/
|
||||
public static Date convertDuration(Date start, String duration) throws ParseException
|
||||
{
|
||||
if (start == null) start = new Date();
|
||||
if (duration.startsWith("now"))
|
||||
{
|
||||
start = new Date();
|
||||
if (duration.indexOf("+") < 0) return start;
|
||||
|
||||
duration = duration.substring(3);
|
||||
}
|
||||
|
||||
if (duration.startsWith("+"))
|
||||
{
|
||||
long offset = (long) parseInt(duration.substring(1), 0) * 1000;
|
||||
return new Date(start.getTime() + offset);
|
||||
}
|
||||
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
try
|
||||
{
|
||||
return dateFormatter.parse(duration);
|
||||
}
|
||||
catch (java.text.ParseException e)
|
||||
{
|
||||
throw new ParseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void execute() throws Exception;
|
||||
|
||||
public void run(CLIStateBase state, String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
state.parseCommandLine(args);
|
||||
}
|
||||
catch (UnrecognizedOptionException e)
|
||||
{
|
||||
System.err.println("error: unknown option encountered: " + e.getMessage());
|
||||
state.usage();
|
||||
}
|
||||
catch (AlreadySelectedException e)
|
||||
{
|
||||
System.err.println("error: mutually exclusive options have "
|
||||
+ "been selected:\n " + e.getMessage());
|
||||
state.usage();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.err.println("error: unknown command line parsing exception:");
|
||||
e.printStackTrace();
|
||||
state.usage();
|
||||
}
|
||||
|
||||
log = Logger.getLogger(this.getClass().toString());
|
||||
|
||||
try
|
||||
{
|
||||
execute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
136
src/main/java/com/verisignlabs/dnssec/cl/DSTool.java
Normal file
136
src/main/java/com/verisignlabs/dnssec/cl/DSTool.java
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright (C) 2001-2003, 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.xbill.DNS.DLVRecord;
|
||||
import org.xbill.DNS.DNSKEYRecord;
|
||||
import org.xbill.DNS.DSRecord;
|
||||
import org.xbill.DNS.Record;
|
||||
|
||||
import com.verisignlabs.dnssec.security.*;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a DNSSEC DS/DLV generator
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class DSTool extends CLBase
|
||||
{
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase
|
||||
{
|
||||
public boolean createDLV = false;
|
||||
public String outputfile = null;
|
||||
public String keyname = null;
|
||||
public int digest_id = DSRecord.SHA1_DIGEST_ID;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-dstool [..options..] keyfile");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*
|
||||
* @return a set of command line options.
|
||||
*/
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
OptionBuilder.withLongOpt("dlv");
|
||||
OptionBuilder.withDescription("Generate a DLV record instead.");
|
||||
opts.addOption(OptionBuilder.create());
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withLongOpt("digest");
|
||||
OptionBuilder.withArgName("id");
|
||||
OptionBuilder.withDescription("The Digest ID to use (numerically): either 1 for SHA1 or 2 for SHA256");
|
||||
opts.addOption(OptionBuilder.create('d'));
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli)
|
||||
throws org.apache.commons.cli.ParseException
|
||||
{
|
||||
outputfile = cli.getOptionValue('f');
|
||||
createDLV = cli.hasOption("dlv");
|
||||
String optstr = cli.getOptionValue('d');
|
||||
if (optstr != null) digest_id = parseInt(optstr, digest_id);
|
||||
|
||||
String[] cl_args = cli.getArgs();
|
||||
|
||||
if (cl_args.length < 1)
|
||||
{
|
||||
System.err.println("error: missing key file ");
|
||||
usage();
|
||||
}
|
||||
|
||||
keyname = cl_args[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void execute() throws Exception
|
||||
{
|
||||
DnsKeyPair key = BINDKeyUtils.loadKey(state.keyname, null);
|
||||
DNSKEYRecord dnskey = key.getDNSKEYRecord();
|
||||
|
||||
if ((dnskey.getFlags() & DNSKEYRecord.Flags.SEP_KEY) == 0)
|
||||
{
|
||||
log.warning("DNSKEY is not an SEP-flagged key.");
|
||||
}
|
||||
|
||||
DSRecord ds = SignUtils.calculateDSRecord(dnskey, state.digest_id, dnskey.getTTL());
|
||||
Record res = ds;
|
||||
|
||||
if (state.createDLV)
|
||||
{
|
||||
log.fine("creating DLV.");
|
||||
DLVRecord dlv = new DLVRecord(ds.getName(), ds.getDClass(), ds.getTTL(),
|
||||
ds.getFootprint(), ds.getAlgorithm(),
|
||||
ds.getDigestID(), ds.getDigest());
|
||||
res = dlv;
|
||||
}
|
||||
|
||||
if (state.outputfile != null)
|
||||
{
|
||||
PrintWriter out = new PrintWriter(new FileWriter(state.outputfile));
|
||||
out.println(res);
|
||||
out.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println(res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
DSTool tool = new DSTool();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
}
|
||||
226
src/main/java/com/verisignlabs/dnssec/cl/KeyGen.java
Normal file
226
src/main/java/com/verisignlabs/dnssec/cl/KeyGen.java
Normal file
@@ -0,0 +1,226 @@
|
||||
// Copyright (C) 2001-2003, 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.xbill.DNS.DClass;
|
||||
import org.xbill.DNS.DNSKEYRecord;
|
||||
import org.xbill.DNS.Name;
|
||||
|
||||
import com.verisignlabs.dnssec.security.*;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a DNSSEC key generator
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class KeyGen extends CLBase
|
||||
{
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase
|
||||
{
|
||||
public int algorithm = 8;
|
||||
public int keylength = 1024;
|
||||
public boolean useLargeE = true;
|
||||
public String outputfile = null;
|
||||
public File keydir = null;
|
||||
public boolean zoneKey = true;
|
||||
public boolean kskFlag = false;
|
||||
public String owner = null;
|
||||
public long ttl = 86400;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-keygen [..options..] name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
// boolean options
|
||||
opts.addOption("k", "kskflag", false,
|
||||
"Key is a key-signing-key (sets the SEP flag).");
|
||||
opts.addOption("e", "large-exponent", false, "Use large RSA exponent (default)");
|
||||
opts.addOption("E", "small-exponent", false, "Use small RSA exponent");
|
||||
|
||||
// Argument options
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withLongOpt("nametype");
|
||||
OptionBuilder.withArgName("type");
|
||||
OptionBuilder.withDescription("ZONE | OTHER (default ZONE)");
|
||||
opts.addOption(OptionBuilder.create('n'));
|
||||
|
||||
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("algorithm");
|
||||
OptionBuilder.withDescription(String.join(" | ", algStrings) +
|
||||
" | alias, RSASHA256 is default.");
|
||||
opts.addOption(OptionBuilder.create('a'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("size");
|
||||
OptionBuilder.withDescription("key size, in bits. default is 1024. "
|
||||
+ "RSA: [512..4096], DSA: [512..1024], DH: [128..4096], "
|
||||
+ "ECDSA: ignored");
|
||||
opts.addOption(OptionBuilder.create('b'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("file");
|
||||
OptionBuilder.withLongOpt("output-file");
|
||||
OptionBuilder.withDescription("base filename for the public/private key files");
|
||||
opts.addOption(OptionBuilder.create('f'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withLongOpt("keydir");
|
||||
OptionBuilder.withArgName("dir");
|
||||
OptionBuilder.withDescription("place generated key files in this " + "directory");
|
||||
opts.addOption(OptionBuilder.create('d'));
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli)
|
||||
throws org.apache.commons.cli.ParseException
|
||||
{
|
||||
String optstr = null;
|
||||
String[] optstrs = null;
|
||||
|
||||
if (cli.hasOption('k')) kskFlag = true;
|
||||
if (cli.hasOption('e')) useLargeE = true;
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
if ((optstr = cli.getOptionValue('d')) != null)
|
||||
{
|
||||
keydir = new File(optstr);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('n')) != null)
|
||||
{
|
||||
if (!optstr.equalsIgnoreCase("ZONE"))
|
||||
{
|
||||
zoneKey = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstrs = cli.getOptionValues('A')) != null)
|
||||
{
|
||||
for (int i = 0; i < optstrs.length; i++)
|
||||
{
|
||||
addArgAlias(optstrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('a')) != null)
|
||||
{
|
||||
algorithm = parseAlg(optstr);
|
||||
if (algorithm < 0)
|
||||
{
|
||||
System.err.println("DNSSEC algorithm " + optstr + " is not supported");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('b')) != null)
|
||||
{
|
||||
keylength = parseInt(optstr, 1024);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("ttl")) != null)
|
||||
{
|
||||
ttl = parseInt(optstr, 86400);
|
||||
}
|
||||
|
||||
String[] cl_args = cli.getArgs();
|
||||
|
||||
if (cl_args.length < 1)
|
||||
{
|
||||
System.err.println("error: missing key owner name");
|
||||
usage();
|
||||
}
|
||||
|
||||
owner = cl_args[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static int parseAlg(String s)
|
||||
{
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
|
||||
int alg = parseInt(s, -1);
|
||||
if (alg > 0)
|
||||
{
|
||||
if (algs.supportedAlgorithm(alg)) return alg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return algs.stringToAlgorithm(s);
|
||||
}
|
||||
|
||||
public void execute() throws Exception
|
||||
{
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||
|
||||
// Minor hack to make the owner name absolute.
|
||||
if (!state.owner.endsWith("."))
|
||||
{
|
||||
state.owner = state.owner + ".";
|
||||
}
|
||||
|
||||
Name owner_name = Name.fromString(state.owner);
|
||||
|
||||
// Calculate our flags
|
||||
int flags = 0;
|
||||
if (state.zoneKey) flags |= DNSKEYRecord.Flags.ZONE_KEY;
|
||||
if (state.kskFlag) flags |= DNSKEYRecord.Flags.SEP_KEY;
|
||||
|
||||
log.fine("create key pair with (name = " + owner_name + ", ttl = " + state.ttl
|
||||
+ ", alg = " + state.algorithm + ", flags = " + flags + ", length = "
|
||||
+ state.keylength + ")");
|
||||
|
||||
DnsKeyPair pair = signer.generateKey(owner_name, state.ttl, DClass.IN,
|
||||
state.algorithm, flags, state.keylength,
|
||||
state.useLargeE);
|
||||
|
||||
if (state.outputfile != null)
|
||||
{
|
||||
BINDKeyUtils.writeKeyFiles(state.outputfile, pair, state.keydir);
|
||||
}
|
||||
else
|
||||
{
|
||||
BINDKeyUtils.writeKeyFiles(pair, state.keydir);
|
||||
System.out.println(BINDKeyUtils.keyFileBase(pair));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
KeyGen tool = new KeyGen();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
}
|
||||
121
src/main/java/com/verisignlabs/dnssec/cl/KeyInfoTool.java
Normal file
121
src/main/java/com/verisignlabs/dnssec/cl/KeyInfoTool.java
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (C) 2001-2003, 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.xbill.DNS.DNSKEYRecord;
|
||||
|
||||
import com.verisignlabs.dnssec.security.*;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a key introspection tool.
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class KeyInfoTool extends CLBase
|
||||
{
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase
|
||||
{
|
||||
public String[] keynames = null;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-keyinfo [..options..] keyfile");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
// no special options at the moment.
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli) throws ParseException
|
||||
{
|
||||
keynames = cli.getArgs();
|
||||
|
||||
if (keynames.length < 1)
|
||||
{
|
||||
System.err.println("error: missing key file ");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void execute() throws Exception
|
||||
{
|
||||
for (int i = 0; i < state.keynames.length; ++i)
|
||||
{
|
||||
String keyname = state.keynames[i];
|
||||
DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null);
|
||||
DNSKEYRecord dnskey = key.getDNSKEYRecord();
|
||||
DnsKeyAlgorithm dnskeyalg = DnsKeyAlgorithm.getInstance();
|
||||
|
||||
boolean isSEP = (dnskey.getFlags() & DNSKEYRecord.Flags.SEP_KEY) != 0;
|
||||
|
||||
System.out.println(keyname + ":");
|
||||
System.out.println("Name: " + dnskey.getName());
|
||||
System.out.println("SEP: " + isSEP);
|
||||
|
||||
System.out.println("Algorithm: " + dnskeyalg.algToString(dnskey.getAlgorithm())
|
||||
+ " (" + dnskey.getAlgorithm() + ")");
|
||||
System.out.println("ID: " + dnskey.getFootprint());
|
||||
System.out.println("KeyFileBase: " + BINDKeyUtils.keyFileBase(key));
|
||||
int basetype = dnskeyalg.baseType(dnskey.getAlgorithm());
|
||||
switch (basetype)
|
||||
{
|
||||
case DnsKeyAlgorithm.RSA: {
|
||||
RSAPublicKey pub = (RSAPublicKey) key.getPublic();
|
||||
System.out.println("RSA Public Exponent: " + pub.getPublicExponent());
|
||||
System.out.println("RSA Modulus: " + pub.getModulus());
|
||||
break;
|
||||
}
|
||||
case DnsKeyAlgorithm.DSA: {
|
||||
DSAPublicKey pub = (DSAPublicKey) key.getPublic();
|
||||
System.out.println("DSA base (G): " + pub.getParams().getG());
|
||||
System.out.println("DSA prime (P): " + pub.getParams().getP());
|
||||
System.out.println("DSA subprime (Q): " + pub.getParams().getQ());
|
||||
System.out.println("DSA public (Y): " + pub.getY());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state.keynames.length - i > 1)
|
||||
{
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
KeyInfoTool tool = new KeyInfoTool();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
}
|
||||
393
src/main/java/com/verisignlabs/dnssec/cl/SignKeyset.java
Normal file
393
src/main/java/com/verisignlabs/dnssec/cl/SignKeyset.java
Normal file
@@ -0,0 +1,393 @@
|
||||
// Copyright (C) 2001-2003, 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.xbill.DNS.Name;
|
||||
import org.xbill.DNS.RRSIGRecord;
|
||||
import org.xbill.DNS.RRset;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.Type;
|
||||
|
||||
import com.verisignlabs.dnssec.security.*;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a DNSSEC keyset signer.
|
||||
* Instead of being able to sign an entire zone, it will just sign a given
|
||||
* DNSKEY RRset.
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class SignKeyset extends CLBase
|
||||
{
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is an inner class used to hold all of the command line option state.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase
|
||||
{
|
||||
public File keyDirectory = null;
|
||||
public String[] keyFiles = null;
|
||||
public Date start = null;
|
||||
public Date expire = null;
|
||||
public String inputfile = null;
|
||||
public String outputfile = null;
|
||||
public boolean verifySigs = false;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
|
||||
// Argument options
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("dir");
|
||||
OptionBuilder.withLongOpt("key-directory");
|
||||
OptionBuilder.withDescription("directory to find key files (default '.').");
|
||||
opts.addOption(OptionBuilder.create('D'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("start-time");
|
||||
OptionBuilder.withDescription("signature starting time (default is now - 1 hour)");
|
||||
opts.addOption(OptionBuilder.create('s'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("expire-time");
|
||||
OptionBuilder.withDescription("signature expiration time (default is start-time + 30 days).");
|
||||
opts.addOption(OptionBuilder.create('e'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("outfile");
|
||||
OptionBuilder.withDescription("file the signed keyset is written to.");
|
||||
opts.addOption(OptionBuilder.create('f'));
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli)
|
||||
throws org.apache.commons.cli.ParseException
|
||||
{
|
||||
String optstr = null;
|
||||
|
||||
if (cli.hasOption('a')) verifySigs = true;
|
||||
|
||||
if ((optstr = cli.getOptionValue('D')) != null)
|
||||
{
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory())
|
||||
{
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('s')) != null)
|
||||
{
|
||||
start = convertDuration(null, optstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default is now - 1 hour.
|
||||
start = new Date(System.currentTimeMillis() - (3600 * 1000));
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('e')) != null)
|
||||
{
|
||||
expire = convertDuration(start, optstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
expire = convertDuration(start, "+2592000"); // 30 days
|
||||
}
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 1)
|
||||
{
|
||||
System.err.println("error: missing zone file and/or key files");
|
||||
usage();
|
||||
}
|
||||
|
||||
inputfile = files[0];
|
||||
if (files.length > 1)
|
||||
{
|
||||
keyFiles = new String[files.length - 1];
|
||||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the generated signatures.
|
||||
*
|
||||
* @param zonename
|
||||
* the origin name of the zone.
|
||||
* @param records
|
||||
* a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs
|
||||
* a list of keypairs used the sign the zone.
|
||||
* @return true if all of the signatures validated.
|
||||
*/
|
||||
private static boolean verifySigs(Name zonename, List<Record> records,
|
||||
List<DnsKeyPair> keypairs)
|
||||
{
|
||||
boolean secure = true;
|
||||
|
||||
DnsSecVerifier verifier = new DnsSecVerifier();
|
||||
|
||||
for (DnsKeyPair pair : keypairs)
|
||||
{
|
||||
verifier.addTrustedKey(pair);
|
||||
}
|
||||
|
||||
verifier.setVerifyAllSigs(true);
|
||||
|
||||
List<RRset> rrsets = SignUtils.assembleIntoRRsets(records);
|
||||
|
||||
for (RRset rrset : rrsets)
|
||||
{
|
||||
// skip unsigned rrsets.
|
||||
if (!rrset.sigs().hasNext()) continue;
|
||||
|
||||
boolean result = verifier.verify(rrset);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
||||
secure = false;
|
||||
}
|
||||
}
|
||||
|
||||
return secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the key pairs from the key files.
|
||||
*
|
||||
* @param keyfiles
|
||||
* a string array containing the base names or paths of the keys
|
||||
* to be loaded.
|
||||
* @param start_index
|
||||
* the starting index of keyfiles string array to use. This
|
||||
* allows us to use the straight command line argument array.
|
||||
* @param inDirectory
|
||||
* the directory to look in (may be null).
|
||||
* @return a list of keypair objects.
|
||||
*/
|
||||
private static List<DnsKeyPair> getKeys(String[] keyfiles, int start_index,
|
||||
File inDirectory) throws IOException
|
||||
{
|
||||
if (keyfiles == null) return null;
|
||||
|
||||
int len = keyfiles.length - start_index;
|
||||
if (len <= 0) return null;
|
||||
|
||||
ArrayList<DnsKeyPair> keys = new ArrayList<DnsKeyPair>(len);
|
||||
|
||||
for (int i = start_index; i < keyfiles.length; i++)
|
||||
{
|
||||
DnsKeyPair k = BINDKeyUtils.loadKeyPair(keyfiles[i], inDirectory);
|
||||
if (k != null) keys.add(k);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static class KeyFileFilter implements FileFilter
|
||||
{
|
||||
private String prefix;
|
||||
|
||||
public KeyFileFilter(Name origin)
|
||||
{
|
||||
prefix = "K" + origin.toString();
|
||||
}
|
||||
|
||||
public boolean accept(File pathname)
|
||||
{
|
||||
if (!pathname.isFile()) return false;
|
||||
String name = pathname.getName();
|
||||
if (name.startsWith(prefix) && name.endsWith(".private")) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename)
|
||||
throws IOException
|
||||
{
|
||||
if (inDirectory == null)
|
||||
{
|
||||
inDirectory = new File(".");
|
||||
}
|
||||
|
||||
// get the list of "K<zone>.*.private files.
|
||||
FileFilter filter = new KeyFileFilter(zonename);
|
||||
File[] files = inDirectory.listFiles(filter);
|
||||
|
||||
// read in all of the records
|
||||
ArrayList<DnsKeyPair> keys = new ArrayList<DnsKeyPair>();
|
||||
for (int i = 0; i < files.length; i++)
|
||||
{
|
||||
DnsKeyPair p = BINDKeyUtils.loadKeyPair(files[i].getName(), inDirectory);
|
||||
keys.add(p);
|
||||
}
|
||||
|
||||
if (keys.size() > 0) return keys;
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void execute() throws Exception
|
||||
{
|
||||
// Read in the zone
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null);
|
||||
if (records == null || records.size() == 0)
|
||||
{
|
||||
System.err.println("error: empty keyset file");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// Make sure that all records are DNSKEYs with the same name.
|
||||
Name keysetName = null;
|
||||
RRset keyset = new RRset();
|
||||
|
||||
for (Record r : records)
|
||||
{
|
||||
if (r.getType() != Type.DNSKEY)
|
||||
{
|
||||
System.err.println("error: Non DNSKEY RR found in keyset: " + r);
|
||||
continue;
|
||||
}
|
||||
if (keysetName == null)
|
||||
{
|
||||
keysetName = r.getName();
|
||||
}
|
||||
if (!r.getName().equals(keysetName))
|
||||
{
|
||||
System.err.println("error: DNSKEY with a different name found!");
|
||||
state.usage();
|
||||
}
|
||||
keyset.addRR(r);
|
||||
}
|
||||
|
||||
if (keyset.size() == 0)
|
||||
{
|
||||
System.err.println("error: No DNSKEYs found in keyset file");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// Load the key pairs.
|
||||
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
||||
|
||||
// If we *still* don't have any key pairs, look for keys the key
|
||||
// directory
|
||||
// that match
|
||||
if (keypairs == null)
|
||||
{
|
||||
keypairs = findZoneKeys(state.keyDirectory, keysetName);
|
||||
}
|
||||
|
||||
// If there *still* aren't any ZSKs defined, bail.
|
||||
if (keypairs == null || keypairs.size() == 0)
|
||||
{
|
||||
System.err.println("error: No signing keys could be determined.");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// default the output file, if not set.
|
||||
if (state.outputfile == null)
|
||||
{
|
||||
if (keysetName.isAbsolute())
|
||||
{
|
||||
state.outputfile = keysetName + "signed_keyset";
|
||||
}
|
||||
else
|
||||
{
|
||||
state.outputfile = keysetName + ".signed_keyset";
|
||||
}
|
||||
}
|
||||
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||
|
||||
List<RRSIGRecord> sigs = signer.signRRset(keyset, keypairs, state.start, state.expire);
|
||||
for (RRSIGRecord s : sigs)
|
||||
{
|
||||
keyset.addRR(s);
|
||||
}
|
||||
|
||||
// write out the signed RRset
|
||||
List<Record> signed_records = new ArrayList<Record>();
|
||||
for (Iterator<Record> i = keyset.rrs(); i.hasNext();)
|
||||
{
|
||||
signed_records.add(i.next());
|
||||
}
|
||||
for (Iterator<Record> i = keyset.sigs(); i.hasNext();)
|
||||
{
|
||||
signed_records.add(i.next());
|
||||
}
|
||||
|
||||
// write out the signed zone
|
||||
ZoneUtils.writeZoneFile(signed_records, state.outputfile);
|
||||
|
||||
if (state.verifySigs)
|
||||
{
|
||||
log.fine("verifying generated signatures");
|
||||
boolean res = verifySigs(keysetName, signed_records, keypairs);
|
||||
|
||||
if (res)
|
||||
{
|
||||
System.out.println("Generated signatures verified");
|
||||
// log.info("Generated signatures verified");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Generated signatures did not verify.");
|
||||
// log.warn("Generated signatures did not verify.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SignKeyset tool = new SignKeyset();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
}
|
||||
361
src/main/java/com/verisignlabs/dnssec/cl/SignRRset.java
Normal file
361
src/main/java/com/verisignlabs/dnssec/cl/SignRRset.java
Normal file
@@ -0,0 +1,361 @@
|
||||
// Copyright (C) 2001-2003, 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
|
||||
import org.xbill.DNS.Name;
|
||||
import org.xbill.DNS.RRSIGRecord;
|
||||
import org.xbill.DNS.RRset;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.Type;
|
||||
|
||||
import com.verisignlabs.dnssec.security.*;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a DNSSEC RRset signer.
|
||||
* Instead of being able to sign an entire zone, it will just sign a given
|
||||
* RRset. Note that it will sign any RRset with any private key without
|
||||
* consideration of whether or not the RRset *should* be signed in the context
|
||||
* of a zone.
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class SignRRset extends CLBase
|
||||
{
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is an inner class used to hold all of the command line option state.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase
|
||||
{
|
||||
private File keyDirectory = null;
|
||||
public String[] keyFiles = null;
|
||||
public Date start = null;
|
||||
public Date expire = null;
|
||||
public String inputfile = null;
|
||||
public String outputfile = null;
|
||||
public boolean verifySigs = false;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("dir");
|
||||
OptionBuilder.withLongOpt("key-directory");
|
||||
OptionBuilder.withDescription("directory to find key files (default '.').");
|
||||
opts.addOption(OptionBuilder.create('D'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("start-time");
|
||||
OptionBuilder.withDescription("signature starting time (default is now - 1 hour)");
|
||||
opts.addOption(OptionBuilder.create('s'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("expire-time");
|
||||
OptionBuilder.withDescription("signature expiration time (default is start-time + 30 days).");
|
||||
opts.addOption(OptionBuilder.create('e'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("outfile");
|
||||
OptionBuilder.withDescription("file the signed rrset is written to.");
|
||||
opts.addOption(OptionBuilder.create('f'));
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException
|
||||
{
|
||||
String optstr = null;
|
||||
|
||||
if (cli.hasOption('a')) verifySigs = true;
|
||||
|
||||
if ((optstr = cli.getOptionValue('D')) != null)
|
||||
{
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory())
|
||||
{
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('s')) != null)
|
||||
{
|
||||
start = convertDuration(null, optstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default is now - 1 hour.
|
||||
start = new Date(System.currentTimeMillis() - (3600 * 1000));
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('e')) != null)
|
||||
{
|
||||
expire = convertDuration(start, optstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
expire = convertDuration(start, "+2592000"); // 30 days
|
||||
}
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 1)
|
||||
{
|
||||
System.err.println("error: missing zone file and/or key files");
|
||||
usage();
|
||||
}
|
||||
|
||||
inputfile = files[0];
|
||||
if (files.length > 1)
|
||||
{
|
||||
keyFiles = new String[files.length - 1];
|
||||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the generated signatures.
|
||||
*
|
||||
* @param zonename
|
||||
* the origin name of the zone.
|
||||
* @param records
|
||||
* a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs
|
||||
* a list of keypairs used the sign the zone.
|
||||
* @return true if all of the signatures validated.
|
||||
*/
|
||||
private static boolean verifySigs(Name zonename, List<Record> records, List<DnsKeyPair> keypairs)
|
||||
{
|
||||
boolean secure = true;
|
||||
|
||||
DnsSecVerifier verifier = new DnsSecVerifier();
|
||||
|
||||
for (DnsKeyPair pair : keypairs)
|
||||
{
|
||||
verifier.addTrustedKey(pair);
|
||||
}
|
||||
|
||||
verifier.setVerifyAllSigs(true);
|
||||
|
||||
List<RRset> rrsets = SignUtils.assembleIntoRRsets(records);
|
||||
|
||||
for (RRset rrset : rrsets)
|
||||
{
|
||||
// skip unsigned rrsets.
|
||||
if (!rrset.sigs().hasNext()) continue;
|
||||
|
||||
boolean result = verifier.verify(rrset);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
||||
secure = false;
|
||||
}
|
||||
}
|
||||
|
||||
return secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the key pairs from the key files.
|
||||
*
|
||||
* @param keyfiles
|
||||
* a string array containing the base names or paths of the keys
|
||||
* to be loaded.
|
||||
* @param start_index
|
||||
* the starting index of keyfiles string array to use. This
|
||||
* allows us to use the straight command line argument array.
|
||||
* @param inDirectory
|
||||
* the directory to look in (may be null).
|
||||
* @return a list of keypair objects.
|
||||
*/
|
||||
private static List<DnsKeyPair> getKeys(String[] keyfiles, int start_index,
|
||||
File inDirectory) throws IOException
|
||||
{
|
||||
if (keyfiles == null) return null;
|
||||
|
||||
int len = keyfiles.length - start_index;
|
||||
if (len <= 0) return null;
|
||||
|
||||
ArrayList<DnsKeyPair> keys = new ArrayList<DnsKeyPair>(len);
|
||||
|
||||
for (int i = start_index; i < keyfiles.length; i++)
|
||||
{
|
||||
DnsKeyPair k = BINDKeyUtils.loadKeyPair(keyfiles[i], inDirectory);
|
||||
if (k != null) keys.add(k);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void execute() throws Exception
|
||||
{
|
||||
// Read in the zone
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null);
|
||||
if (records == null || records.size() == 0)
|
||||
{
|
||||
System.err.println("error: empty RRset file");
|
||||
state.usage();
|
||||
}
|
||||
// Construct the RRset. Complain if the records in the input file
|
||||
// consist of more than one RRset.
|
||||
RRset rrset = null;
|
||||
|
||||
for (Record r : records)
|
||||
{
|
||||
// skip RRSIGs
|
||||
if (r.getType() == Type.RRSIG || r.getType() == Type.SIG)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle the first record.
|
||||
if (rrset == null)
|
||||
{
|
||||
rrset = new RRset();
|
||||
rrset.addRR(r);
|
||||
continue;
|
||||
}
|
||||
// Ensure that the remaining records all belong to the same rrset.
|
||||
if (rrset.getName().equals(r.getName()) && rrset.getType() == r.getType()
|
||||
&& rrset.getDClass() == r.getDClass())
|
||||
{
|
||||
rrset.addRR(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.err.println("Records do not all belong to the same RRset.");
|
||||
state.usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (rrset.size() == 0)
|
||||
{
|
||||
System.err.println("No records found in inputfile.");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// Load the key pairs.
|
||||
|
||||
if (state.keyFiles.length == 0)
|
||||
{
|
||||
System.err.println("error: at least one keyfile must be specified");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
||||
|
||||
// Make sure that all the keypairs have the same name.
|
||||
// This will be used as the zone name, too.
|
||||
|
||||
Name keysetName = null;
|
||||
for (DnsKeyPair pair : keypairs)
|
||||
{
|
||||
if (keysetName == null)
|
||||
{
|
||||
keysetName = pair.getDNSKEYName();
|
||||
continue;
|
||||
}
|
||||
if (!pair.getDNSKEYName().equals(keysetName))
|
||||
{
|
||||
System.err.println("Keys do not all have the same name.");
|
||||
state.usage();
|
||||
}
|
||||
}
|
||||
|
||||
// default the output file, if not set.
|
||||
if (state.outputfile == null && !state.inputfile.equals("-"))
|
||||
{
|
||||
state.outputfile = state.inputfile + ".signed";
|
||||
}
|
||||
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||
|
||||
List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, state.start, state.expire);
|
||||
for (RRSIGRecord s : sigs)
|
||||
{
|
||||
rrset.addRR(s);
|
||||
}
|
||||
|
||||
// write out the signed RRset
|
||||
List<Record> signed_records = new ArrayList<Record>();
|
||||
for (Iterator<Record> i = rrset.rrs(); i.hasNext();)
|
||||
{
|
||||
signed_records.add(i.next());
|
||||
}
|
||||
for (Iterator<Record> i = rrset.sigs(); i.hasNext();)
|
||||
{
|
||||
signed_records.add(i.next());
|
||||
}
|
||||
|
||||
// write out the signed zone
|
||||
ZoneUtils.writeZoneFile(signed_records, state.outputfile);
|
||||
|
||||
if (state.verifySigs)
|
||||
{
|
||||
log.fine("verifying generated signatures");
|
||||
boolean res = verifySigs(keysetName, signed_records, keypairs);
|
||||
|
||||
if (res)
|
||||
{
|
||||
System.out.println("Generated signatures verified");
|
||||
// log.info("Generated signatures verified");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Generated signatures did not verify.");
|
||||
// log.warn("Generated signatures did not verify.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SignRRset tool = new SignRRset();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
}
|
||||
755
src/main/java/com/verisignlabs/dnssec/cl/SignZone.java
Normal file
755
src/main/java/com/verisignlabs/dnssec/cl/SignZone.java
Normal file
@@ -0,0 +1,755 @@
|
||||
// Copyright (C) 2001-2003, 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
|
||||
import org.xbill.DNS.DNSKEYRecord;
|
||||
import org.xbill.DNS.DSRecord;
|
||||
import org.xbill.DNS.Name;
|
||||
import org.xbill.DNS.RRset;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.TextParseException;
|
||||
import org.xbill.DNS.Type;
|
||||
import org.xbill.DNS.utils.base16;
|
||||
|
||||
import com.verisignlabs.dnssec.security.BINDKeyUtils;
|
||||
import com.verisignlabs.dnssec.security.DnsKeyPair;
|
||||
import com.verisignlabs.dnssec.security.DnsSecVerifier;
|
||||
import com.verisignlabs.dnssec.security.JCEDnsSecSigner;
|
||||
import com.verisignlabs.dnssec.security.SignUtils;
|
||||
import com.verisignlabs.dnssec.security.ZoneUtils;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a DNSSEC zone signer.
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class SignZone extends CLBase
|
||||
{
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is an inner class used to hold all of the command line option state.
|
||||
*/
|
||||
private static class CLIState extends CLIStateBase
|
||||
{
|
||||
public File keyDirectory = null;
|
||||
public File keysetDirectory = null;
|
||||
public String[] kskFiles = null;
|
||||
public String[] keyFiles = null;
|
||||
public String zonefile = null;
|
||||
public Date start = null;
|
||||
public Date expire = null;
|
||||
public String outputfile = null;
|
||||
public boolean verifySigs = false;
|
||||
public boolean useOptOut = false;
|
||||
public boolean fullySignKeyset = false;
|
||||
public List<Name> includeNames = null;
|
||||
public boolean useNsec3 = false;
|
||||
public byte[] salt = null;
|
||||
public int iterations = 0;
|
||||
public int digest_id = DSRecord.SHA1_DIGEST_ID;
|
||||
public long nsec3paramttl = -1;
|
||||
public boolean verboseSigning = false;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-signzone [..options..] zone_file [key_file ...]");
|
||||
}
|
||||
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
opts.addOption("F", "fully-sign-keyset", false,
|
||||
"sign the zone apex keyset with all available keys.");
|
||||
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
|
||||
|
||||
// Argument options
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("dir");
|
||||
OptionBuilder.withLongOpt("keyset-directory");
|
||||
OptionBuilder.withDescription("directory to find keyset files (default '.').");
|
||||
opts.addOption(OptionBuilder.create('d'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("dir");
|
||||
OptionBuilder.withLongOpt("key-directory");
|
||||
OptionBuilder.withDescription("directory to find key files (default '.').");
|
||||
opts.addOption(OptionBuilder.create('D'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("start-time");
|
||||
OptionBuilder.withDescription("signature starting time (default is now - 1 hour)");
|
||||
opts.addOption(OptionBuilder.create('s'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("expire-time");
|
||||
OptionBuilder.withDescription("signature expiration time (default is start-time + 30 days).");
|
||||
opts.addOption(OptionBuilder.create('e'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("outfile");
|
||||
OptionBuilder.withDescription("file the signed zone is written to (default is <origin>.signed).");
|
||||
opts.addOption(OptionBuilder.create('f'));
|
||||
|
||||
OptionBuilder.hasArgs();
|
||||
OptionBuilder.withArgName("KSK file");
|
||||
OptionBuilder.withLongOpt("ksk-file");
|
||||
OptionBuilder.withDescription("this key is a key signing key (may repeat).");
|
||||
opts.addOption(OptionBuilder.create('k'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("file");
|
||||
OptionBuilder.withLongOpt("include-file");
|
||||
OptionBuilder.withDescription("include names in this file in the NSEC/NSEC3 chain.");
|
||||
opts.addOption(OptionBuilder.create('I'));
|
||||
|
||||
// NSEC3 options
|
||||
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
|
||||
opts.addOption("O", "use-opt-out", false,
|
||||
"generate a fully Opt-Out zone (only valid with NSEC3).");
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withLongOpt("salt");
|
||||
OptionBuilder.withArgName("hex value");
|
||||
OptionBuilder.withDescription("supply a salt value.");
|
||||
opts.addOption(OptionBuilder.create('S'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withLongOpt("random-salt");
|
||||
OptionBuilder.withArgName("length");
|
||||
OptionBuilder.withDescription("generate a random salt.");
|
||||
opts.addOption(OptionBuilder.create('R'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withLongOpt("iterations");
|
||||
OptionBuilder.withArgName("value");
|
||||
OptionBuilder.withDescription("use this value for the iterations in NSEC3.");
|
||||
opts.addOption(OptionBuilder.create());
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withLongOpt("nsec3paramttl");
|
||||
OptionBuilder.withArgName("ttl");
|
||||
OptionBuilder.withDescription("use this value for the NSEC3PARAM RR ttl");
|
||||
opts.addOption(OptionBuilder.create());
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("id");
|
||||
OptionBuilder.withLongOpt("ds-digest");
|
||||
OptionBuilder.withDescription("Digest algorithm to use for generated DSs");
|
||||
opts.addOption(OptionBuilder.create());
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli) throws ParseException
|
||||
{
|
||||
String optstr = null;
|
||||
|
||||
if (cli.hasOption('a')) verifySigs = true;
|
||||
if (cli.hasOption('3')) useNsec3 = true;
|
||||
if (cli.hasOption('O')) useOptOut = true;
|
||||
if (cli.hasOption('V')) verboseSigning = true;
|
||||
|
||||
if (useOptOut && !useNsec3)
|
||||
{
|
||||
System.err.println("Opt-Out not supported without NSEC3 -- ignored.");
|
||||
useOptOut = false;
|
||||
}
|
||||
|
||||
if (cli.hasOption('F')) fullySignKeyset = true;
|
||||
|
||||
if ((optstr = cli.getOptionValue('d')) != null)
|
||||
{
|
||||
keysetDirectory = new File(optstr);
|
||||
if (!keysetDirectory.isDirectory())
|
||||
{
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('D')) != null)
|
||||
{
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory())
|
||||
{
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('s')) != null)
|
||||
{
|
||||
start = CLBase.convertDuration(null, optstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default is now - 1 hour.
|
||||
start = new Date(System.currentTimeMillis() - (3600 * 1000));
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('e')) != null)
|
||||
{
|
||||
expire = CLBase.convertDuration(start, optstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
expire = CLBase.convertDuration(start, "+2592000"); // 30 days
|
||||
}
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
kskFiles = cli.getOptionValues('k');
|
||||
|
||||
if ((optstr = cli.getOptionValue('I')) != null)
|
||||
{
|
||||
File includeNamesFile = new File(optstr);
|
||||
try
|
||||
{
|
||||
includeNames = getNameList(includeNamesFile);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ParseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('S')) != null)
|
||||
{
|
||||
salt = base16.fromString(optstr);
|
||||
if (salt == null && !optstr.equals("-"))
|
||||
{
|
||||
System.err.println("error: salt is not valid hexidecimal.");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('R')) != null)
|
||||
{
|
||||
int length = parseInt(optstr, 0);
|
||||
if (length > 0 && length <= 255)
|
||||
{
|
||||
Random random = new Random();
|
||||
salt = new byte[length];
|
||||
random.nextBytes(salt);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("iterations")) != null)
|
||||
{
|
||||
iterations = parseInt(optstr, iterations);
|
||||
if (iterations < 0 || iterations > 8388607)
|
||||
{
|
||||
System.err.println("error: iterations value is invalid");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("ds-digest")) != null)
|
||||
{
|
||||
digest_id = parseInt(optstr, -1);
|
||||
if (digest_id < 0)
|
||||
{
|
||||
System.err.println("error: DS digest ID is not a valid identifier");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("nsec3paramttl")) != null)
|
||||
{
|
||||
nsec3paramttl = parseInt(optstr, -1);
|
||||
}
|
||||
|
||||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 1)
|
||||
{
|
||||
System.err.println("error: missing zone file and/or key files");
|
||||
usage();
|
||||
}
|
||||
|
||||
zonefile = files[0];
|
||||
if (files.length > 1)
|
||||
{
|
||||
keyFiles = new String[files.length - 1];
|
||||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the generated signatures.
|
||||
*
|
||||
* @param zonename
|
||||
* the origin name of the zone.
|
||||
* @param records
|
||||
* a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs
|
||||
* a list of keypairs used the sign the zone.
|
||||
* @return true if all of the signatures validated.
|
||||
*/
|
||||
private static boolean verifyZoneSigs(Name zonename, List<Record> records,
|
||||
List<DnsKeyPair> keypairs)
|
||||
{
|
||||
boolean secure = true;
|
||||
|
||||
DnsSecVerifier verifier = new DnsSecVerifier();
|
||||
|
||||
for (DnsKeyPair pair : keypairs)
|
||||
{
|
||||
verifier.addTrustedKey(pair);
|
||||
}
|
||||
|
||||
verifier.setVerifyAllSigs(true);
|
||||
|
||||
List<RRset> rrsets = SignUtils.assembleIntoRRsets(records);
|
||||
|
||||
for (RRset rrset : rrsets)
|
||||
{
|
||||
// skip unsigned rrsets.
|
||||
if (!rrset.sigs().hasNext()) continue;
|
||||
|
||||
boolean result = verifier.verify(rrset);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
||||
secure = false;
|
||||
}
|
||||
}
|
||||
|
||||
return secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the key pairs from the key files.
|
||||
*
|
||||
* @param keyfiles
|
||||
* a string array containing the base names or paths of the keys to
|
||||
* be loaded.
|
||||
* @param start_index
|
||||
* the starting index of keyfiles string array to use. This allows
|
||||
* us
|
||||
* to use the straight command line argument array.
|
||||
* @param inDirectory
|
||||
* the directory to look in (may be null).
|
||||
* @return a list of keypair objects.
|
||||
*/
|
||||
private static List<DnsKeyPair> getKeys(String[] keyfiles, int start_index,
|
||||
File inDirectory) throws IOException
|
||||
{
|
||||
if (keyfiles == null) return null;
|
||||
|
||||
int len = keyfiles.length - start_index;
|
||||
if (len <= 0) return null;
|
||||
|
||||
ArrayList<DnsKeyPair> keys = new ArrayList<DnsKeyPair>(len);
|
||||
|
||||
for (int i = start_index; i < keyfiles.length; i++)
|
||||
{
|
||||
DnsKeyPair k = BINDKeyUtils.loadKeyPair(keyfiles[i], inDirectory);
|
||||
if (k != null) keys.add(k);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static List<DnsKeyPair> getKeys(List<Record> dnskeyrrs, File inDirectory)
|
||||
throws IOException
|
||||
{
|
||||
List<DnsKeyPair> res = new ArrayList<DnsKeyPair>();
|
||||
for (Record r : dnskeyrrs)
|
||||
{
|
||||
if (r.getType() != Type.DNSKEY) continue;
|
||||
|
||||
// Construct a public-key-only DnsKeyPair just so we can calculate the
|
||||
// base name.
|
||||
DnsKeyPair pub = new DnsKeyPair((DNSKEYRecord) r);
|
||||
DnsKeyPair pair = BINDKeyUtils.loadKeyPair(BINDKeyUtils.keyFileBase(pub),
|
||||
inDirectory);
|
||||
if (pair != null)
|
||||
{
|
||||
res.add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.size() > 0) return res;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class KeyFileFilter implements FileFilter
|
||||
{
|
||||
private String prefix;
|
||||
|
||||
public KeyFileFilter(Name origin)
|
||||
{
|
||||
prefix = "K" + origin.toString();
|
||||
}
|
||||
|
||||
public boolean accept(File pathname)
|
||||
{
|
||||
if (!pathname.isFile()) return false;
|
||||
String name = pathname.getName();
|
||||
if (name.startsWith(prefix) && name.endsWith(".private")) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename)
|
||||
throws IOException
|
||||
{
|
||||
if (inDirectory == null)
|
||||
{
|
||||
inDirectory = new File(".");
|
||||
}
|
||||
|
||||
// get the list of "K<zone>.*.private files.
|
||||
FileFilter filter = new KeyFileFilter(zonename);
|
||||
File[] files = inDirectory.listFiles(filter);
|
||||
|
||||
// read in all of the records
|
||||
ArrayList<DnsKeyPair> keys = new ArrayList<DnsKeyPair>();
|
||||
for (int i = 0; i < files.length; i++)
|
||||
{
|
||||
DnsKeyPair p = BINDKeyUtils.loadKeyPair(files[i].getName(), inDirectory);
|
||||
keys.add(p);
|
||||
}
|
||||
|
||||
if (keys.size() > 0) return keys;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an implementation of a file filter used for finding BIND 9-style
|
||||
* keyset-* files.
|
||||
*/
|
||||
private static class KeysetFileFilter implements FileFilter
|
||||
{
|
||||
public boolean accept(File pathname)
|
||||
{
|
||||
if (!pathname.isFile()) return false;
|
||||
String name = pathname.getName();
|
||||
if (name.startsWith("keyset-")) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load keysets (which contain delegation point security info).
|
||||
*
|
||||
* @param inDirectory
|
||||
* the directory to look for the keyset files (may be null, in
|
||||
* which
|
||||
* case it defaults to looking in the current working directory).
|
||||
* @param zonename
|
||||
* the name of the zone we are signing, so we can ignore keysets
|
||||
* that
|
||||
* do not belong in the zone.
|
||||
* @return a list of {@link org.xbill.DNS.Record}s found in the keyset
|
||||
* files.
|
||||
*/
|
||||
private static List<Record> getKeysets(File inDirectory, Name zonename)
|
||||
throws IOException
|
||||
{
|
||||
if (inDirectory == null)
|
||||
{
|
||||
inDirectory = new File(".");
|
||||
}
|
||||
|
||||
// get the list of "keyset-" files.
|
||||
FileFilter filter = new KeysetFileFilter();
|
||||
File[] files = inDirectory.listFiles(filter);
|
||||
|
||||
// read in all of the records
|
||||
ArrayList<Record> keysetRecords = new ArrayList<Record>();
|
||||
for (int i = 0; i < files.length; i++)
|
||||
{
|
||||
List<Record> l = ZoneUtils.readZoneFile(files[i].getAbsolutePath(), zonename);
|
||||
keysetRecords.addAll(l);
|
||||
}
|
||||
|
||||
// discard records that do not belong to the zone in question.
|
||||
for (Iterator<Record> i = keysetRecords.iterator(); i.hasNext();)
|
||||
{
|
||||
Record r = i.next();
|
||||
if (!r.getName().subdomain(zonename))
|
||||
{
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return keysetRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of DNS names from a file.
|
||||
*
|
||||
* @param nameListFile
|
||||
* the path of a file containing a bare list of DNS names.
|
||||
* @return a list of {@link org.xbill.DNS.Name} objects.
|
||||
*/
|
||||
private static List<Name> getNameList(File nameListFile) throws IOException
|
||||
{
|
||||
BufferedReader br = new BufferedReader(new FileReader(nameListFile));
|
||||
List<Name> res = new ArrayList<Name>();
|
||||
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Name n = Name.fromString(line);
|
||||
// force the name to be absolute.
|
||||
// FIXME: we should probably get some fancy logic here to
|
||||
// detect if the name needs the origin appended, or just the
|
||||
// root.
|
||||
if (!n.isAbsolute()) n = Name.concatenate(n, Name.root);
|
||||
|
||||
res.add(n);
|
||||
}
|
||||
catch (TextParseException e)
|
||||
{
|
||||
log.severe("DNS Name parsing error:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
br.close();
|
||||
if (res.size() == 0) return null;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given keypairs can be used to sign the zone.
|
||||
*
|
||||
* @param zonename
|
||||
* the zone origin.
|
||||
* @param keypairs
|
||||
* a list of {@link DnsKeyPair} objects that will be used to sign
|
||||
* the
|
||||
* zone.
|
||||
* @return true if the keypairs valid.
|
||||
*/
|
||||
private static boolean keyPairsValidForZone(Name zonename, List<DnsKeyPair> keypairs)
|
||||
{
|
||||
if (keypairs == null) return true; // technically true, I guess.
|
||||
|
||||
for (DnsKeyPair kp : keypairs)
|
||||
{
|
||||
Name keyname = kp.getDNSKEYRecord().getName();
|
||||
if (!keyname.equals(zonename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void execute() throws Exception
|
||||
{
|
||||
// Read in the zone
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null);
|
||||
if (records == null || records.size() == 0)
|
||||
{
|
||||
System.err.println("error: empty zone file");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// calculate the zone name.
|
||||
Name zonename = ZoneUtils.findZoneName(records);
|
||||
if (zonename == null)
|
||||
{
|
||||
System.err.println("error: invalid zone file - no SOA");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// Load the key pairs.
|
||||
|
||||
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
||||
List<DnsKeyPair> kskpairs = getKeys(state.kskFiles, 0, state.keyDirectory);
|
||||
|
||||
// If we didn't get any keys on the command line, look at the zone apex for
|
||||
// any public keys.
|
||||
if (keypairs == null && kskpairs == null)
|
||||
{
|
||||
List<Record> dnskeys = ZoneUtils.findRRs(records, zonename, Type.DNSKEY);
|
||||
keypairs = getKeys(dnskeys, state.keyDirectory);
|
||||
}
|
||||
|
||||
// If we *still* don't have any key pairs, look for keys the key directory
|
||||
// that match
|
||||
if (keypairs == null && kskpairs == null)
|
||||
{
|
||||
keypairs = findZoneKeys(state.keyDirectory, zonename);
|
||||
}
|
||||
|
||||
// If we don't have any KSKs, but we do have more than one zone
|
||||
// signing key (presumably), presume that the zone signing keys
|
||||
// are just not differentiated and try to figure out which keys
|
||||
// are actually ksks by looking at the SEP flag.
|
||||
if ((kskpairs == null || kskpairs.size() == 0) && keypairs != null
|
||||
&& keypairs.size() > 1)
|
||||
{
|
||||
for (Iterator<DnsKeyPair> i = keypairs.iterator(); i.hasNext();)
|
||||
{
|
||||
DnsKeyPair pair = i.next();
|
||||
DNSKEYRecord kr = pair.getDNSKEYRecord();
|
||||
if ((kr.getFlags() & DNSKEYRecord.Flags.SEP_KEY) != 0)
|
||||
{
|
||||
if (kskpairs == null) kskpairs = new ArrayList<DnsKeyPair>();
|
||||
kskpairs.add(pair);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no ZSKs defined at this point (yet there are KSKs
|
||||
// provided), all KSKs will be treated as ZSKs, as well.
|
||||
if (keypairs == null || keypairs.size() == 0)
|
||||
{
|
||||
keypairs = kskpairs;
|
||||
}
|
||||
|
||||
// If there *still* aren't any ZSKs defined, bail.
|
||||
if (keypairs == null || keypairs.size() == 0)
|
||||
{
|
||||
System.err.println("No zone signing keys could be determined.");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// default the output file, if not set.
|
||||
if (state.outputfile == null && !state.zonefile.equals("-"))
|
||||
{
|
||||
if (zonename.isAbsolute())
|
||||
{
|
||||
state.outputfile = zonename + "signed";
|
||||
}
|
||||
else
|
||||
{
|
||||
state.outputfile = zonename + ".signed";
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the keys can be in the zone.
|
||||
if (!keyPairsValidForZone(zonename, keypairs)
|
||||
|| !keyPairsValidForZone(zonename, kskpairs))
|
||||
{
|
||||
System.err.println("error: specified keypairs are not valid for the zone.");
|
||||
state.usage();
|
||||
}
|
||||
|
||||
// We force the signing keys to be in the zone by just appending
|
||||
// them to the zone here. Currently JCEDnsSecSigner.signZone
|
||||
// removes duplicate records.
|
||||
if (kskpairs != null)
|
||||
{
|
||||
for (DnsKeyPair pair : kskpairs)
|
||||
{
|
||||
records.add(pair.getDNSKEYRecord());
|
||||
}
|
||||
}
|
||||
if (keypairs != null)
|
||||
{
|
||||
for (DnsKeyPair pair : keypairs)
|
||||
{
|
||||
records.add(pair.getDNSKEYRecord());
|
||||
}
|
||||
}
|
||||
|
||||
// read in the keysets, if any.
|
||||
List<Record> keysetrecs = getKeysets(state.keysetDirectory, zonename);
|
||||
if (keysetrecs != null)
|
||||
{
|
||||
records.addAll(keysetrecs);
|
||||
}
|
||||
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner(state.verboseSigning);
|
||||
|
||||
// Sign the zone.
|
||||
List<Record> signed_records;
|
||||
|
||||
if (state.useNsec3)
|
||||
{
|
||||
signed_records = signer.signZoneNSEC3(zonename, records, kskpairs, keypairs,
|
||||
state.start, state.expire,
|
||||
state.fullySignKeyset, state.useOptOut,
|
||||
state.includeNames, state.salt,
|
||||
state.iterations, state.digest_id,
|
||||
state.nsec3paramttl);
|
||||
}
|
||||
else
|
||||
{
|
||||
signed_records = signer.signZone(zonename, records, kskpairs, keypairs,
|
||||
state.start, state.expire, state.fullySignKeyset,
|
||||
state.digest_id);
|
||||
}
|
||||
|
||||
// write out the signed zone
|
||||
ZoneUtils.writeZoneFile(signed_records, state.outputfile);
|
||||
|
||||
if (state.verifySigs)
|
||||
{
|
||||
// FIXME: ugh.
|
||||
if (kskpairs != null)
|
||||
{
|
||||
keypairs.addAll(kskpairs);
|
||||
}
|
||||
|
||||
log.fine("verifying generated signatures");
|
||||
boolean res = verifyZoneSigs(zonename, signed_records, keypairs);
|
||||
|
||||
if (res)
|
||||
{
|
||||
System.out.println("Generated signatures verified");
|
||||
// log.info("Generated signatures verified");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Generated signatures did not verify.");
|
||||
// log.warn("Generated signatures did not verify.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SignZone tool = new SignZone();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
}
|
||||
166
src/main/java/com/verisignlabs/dnssec/cl/VerifyZone.java
Normal file
166
src/main/java/com/verisignlabs/dnssec/cl/VerifyZone.java
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright (C) 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.xbill.DNS.Record;
|
||||
|
||||
import com.verisignlabs.dnssec.security.ZoneUtils;
|
||||
import com.verisignlabs.dnssec.security.ZoneVerifier;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a DNSSEC zone validator.
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class VerifyZone extends CLBase
|
||||
{
|
||||
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase
|
||||
{
|
||||
public String zonefile = null;
|
||||
public String[] keyfiles = null;
|
||||
public int startfudge = 0;
|
||||
public int expirefudge = 0;
|
||||
public boolean ignoreTime = false;
|
||||
public boolean ignoreDups = false;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-verifyzone [..options..] zonefile");
|
||||
}
|
||||
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
OptionBuilder.hasOptionalArg();
|
||||
OptionBuilder.withLongOpt("sig-start-fudge");
|
||||
OptionBuilder.withArgName("seconds");
|
||||
OptionBuilder.withDescription("'fudge' RRSIG inception times by 'seconds' seconds.");
|
||||
opts.addOption(OptionBuilder.create('S'));
|
||||
|
||||
OptionBuilder.hasOptionalArg();
|
||||
OptionBuilder.withLongOpt("sig-expire-fudge");
|
||||
OptionBuilder.withArgName("seconds");
|
||||
OptionBuilder.withDescription("'fudge' RRSIG expiration times by 'seconds' seconds.");
|
||||
opts.addOption(OptionBuilder.create('E'));
|
||||
|
||||
OptionBuilder.withLongOpt("ignore-time");
|
||||
OptionBuilder.withDescription("Ignore RRSIG inception and expiration time errors.");
|
||||
opts.addOption(OptionBuilder.create());
|
||||
|
||||
OptionBuilder.withLongOpt("ignore-duplicate-rrs");
|
||||
OptionBuilder.withDescription("Ignore duplicate record errors.");
|
||||
opts.addOption(OptionBuilder.create());
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli)
|
||||
{
|
||||
if (cli.hasOption("ignore-time"))
|
||||
{
|
||||
ignoreTime = true;
|
||||
}
|
||||
|
||||
if (cli.hasOption("ignore-duplicate-rrs"))
|
||||
{
|
||||
ignoreDups = true;
|
||||
}
|
||||
|
||||
String optstr = null;
|
||||
if ((optstr = cli.getOptionValue('S')) != null)
|
||||
{
|
||||
startfudge = parseInt(optstr, 0);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('E')) != null)
|
||||
{
|
||||
expirefudge = parseInt(optstr, 0);
|
||||
}
|
||||
|
||||
String[] optstrs = null;
|
||||
if ((optstrs = cli.getOptionValues('A')) != null)
|
||||
{
|
||||
for (int i = 0; i < optstrs.length; i++)
|
||||
{
|
||||
addArgAlias(optstrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
String[] cl_args = cli.getArgs();
|
||||
|
||||
if (cl_args.length < 1)
|
||||
{
|
||||
System.err.println("error: missing zone file");
|
||||
usage();
|
||||
}
|
||||
|
||||
zonefile = cl_args[0];
|
||||
|
||||
if (cl_args.length >= 2)
|
||||
{
|
||||
keyfiles = new String[cl_args.length - 1];
|
||||
System.arraycopy(cl_args, 1, keyfiles, 0, keyfiles.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void execute() throws Exception
|
||||
{
|
||||
ZoneVerifier zoneverifier = new ZoneVerifier();
|
||||
zoneverifier.getVerifier().setStartFudge(state.startfudge);
|
||||
zoneverifier.getVerifier().setExpireFudge(state.expirefudge);
|
||||
zoneverifier.getVerifier().setIgnoreTime(state.ignoreTime);
|
||||
zoneverifier.setIgnoreDuplicateRRs(state.ignoreDups);
|
||||
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null);
|
||||
|
||||
log.fine("verifying zone...");
|
||||
int errors = zoneverifier.verifyZone(records);
|
||||
log.fine("completed verification process.");
|
||||
|
||||
if (errors > 0)
|
||||
{
|
||||
System.out.println("zone did not verify.");
|
||||
System.exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("zone verified.");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
VerifyZone tool = new VerifyZone();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
}
|
||||
214
src/main/java/com/verisignlabs/dnssec/cl/ZoneFormat.java
Normal file
214
src/main/java/com/verisignlabs/dnssec/cl/ZoneFormat.java
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright (C) 2011 VeriSign, Inc.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.xbill.DNS.Master;
|
||||
import org.xbill.DNS.NSEC3PARAMRecord;
|
||||
import org.xbill.DNS.NSEC3Record;
|
||||
import org.xbill.DNS.Name;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.Section;
|
||||
import org.xbill.DNS.Type;
|
||||
import org.xbill.DNS.utils.base32;
|
||||
|
||||
import com.verisignlabs.dnssec.security.RecordComparator;
|
||||
|
||||
/**
|
||||
* This class forms the command line implementation of a zone file normalizer.
|
||||
* That is, a tool to rewrite zones in a consistent, comparable format.
|
||||
*
|
||||
* @author David Blacka (original)
|
||||
* @author $Author: davidb $
|
||||
* @version $Revision: 2218 $
|
||||
*/
|
||||
public class ZoneFormat extends CLBase
|
||||
{
|
||||
private CLIState state;
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase
|
||||
{
|
||||
public String file;
|
||||
public boolean assignNSEC3;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
super("jdnssec-zoneformat [..options..] zonefile");
|
||||
}
|
||||
|
||||
protected void setupOptions(Options opts)
|
||||
{
|
||||
opts.addOption("N", "nsec3", false,
|
||||
"attempt to determine the original ownernames for NSEC3 RRs.");
|
||||
}
|
||||
|
||||
protected void processOptions(CommandLine cli) throws ParseException
|
||||
{
|
||||
if (cli.hasOption('N')) assignNSEC3 = true;
|
||||
|
||||
String[] cl_args = cli.getArgs();
|
||||
|
||||
if (cl_args.length < 1)
|
||||
{
|
||||
System.err.println("error: must specify a zone file");
|
||||
usage();
|
||||
}
|
||||
|
||||
file = cl_args[0];
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Record> readZoneFile(String filename) throws IOException
|
||||
{
|
||||
Master master = new Master(filename);
|
||||
|
||||
List<Record> res = new ArrayList<Record>();
|
||||
Record r = null;
|
||||
|
||||
while ((r = master.nextRecord()) != null)
|
||||
{
|
||||
// Normalize each record by round-tripping it through canonical wire line
|
||||
// format. Mostly this just lowercases names that are subject to it.
|
||||
byte[] wire = r.toWireCanonical();
|
||||
Record canon_record = Record.fromWire(wire, Section.ANSWER);
|
||||
res.add(canon_record);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void formatZone(List<Record> zone)
|
||||
{
|
||||
// Put the zone into a consistent (name and RR type) order.
|
||||
RecordComparator cmp = new RecordComparator();
|
||||
|
||||
Collections.sort(zone, cmp);
|
||||
|
||||
for (Record r : zone)
|
||||
{
|
||||
System.out.println(r.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void determineNSEC3Owners(List<Record> zone)
|
||||
throws NoSuchAlgorithmException
|
||||
{
|
||||
// Put the zone into a consistent (name and RR type) order.
|
||||
Collections.sort(zone, new RecordComparator());
|
||||
|
||||
// first, find the NSEC3PARAM record -- this is an inefficient linear
|
||||
// search, although it should be near the head of the list.
|
||||
NSEC3PARAMRecord nsec3param = null;
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
base32 b32 = new base32(base32.Alphabet.BASE32HEX, false, true);
|
||||
Name zonename = null;
|
||||
|
||||
for (Record r : zone)
|
||||
{
|
||||
if (r.getType() == Type.SOA)
|
||||
{
|
||||
zonename = r.getName();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r.getType() == Type.NSEC3PARAM)
|
||||
{
|
||||
nsec3param = (NSEC3PARAMRecord) r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't determine a zone name, we have an issue.
|
||||
if (zonename == null) return;
|
||||
// If there wasn't one, we have nothing to do.
|
||||
if (nsec3param == null) return;
|
||||
|
||||
// Next pass, calculate a mapping between ownernames and hashnames
|
||||
Name last_name = null;
|
||||
for (Record r : zone)
|
||||
{
|
||||
if (r.getName().equals(last_name)) continue;
|
||||
if (r.getType() == Type.NSEC3) continue;
|
||||
|
||||
Name n = r.getName();
|
||||
byte[] hash = nsec3param.hashName(n);
|
||||
String hashname = b32.toString(hash);
|
||||
map.put(hashname, n.toString().toLowerCase());
|
||||
last_name = n;
|
||||
|
||||
// inefficiently create hashes for the possible ancestor ENTs
|
||||
for (int i = zonename.labels() + 1; i < n.labels(); ++i)
|
||||
{
|
||||
Name parent = new Name(n, n.labels() - i);
|
||||
byte[] parent_hash = nsec3param.hashName(parent);
|
||||
String parent_hashname = b32.toString(parent_hash);
|
||||
if (!map.containsKey(parent_hashname))
|
||||
{
|
||||
map.put(parent_hashname, parent.toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final pass, assign the names if we can
|
||||
for (ListIterator<Record> i = zone.listIterator(); i.hasNext();)
|
||||
{
|
||||
Record r = i.next();
|
||||
if (r.getType() != Type.NSEC3) continue;
|
||||
NSEC3Record nsec3 = (NSEC3Record) r;
|
||||
String hashname = nsec3.getName().getLabelString(0).toLowerCase();
|
||||
String ownername = (String) map.get(hashname);
|
||||
|
||||
NSEC3Record new_nsec3 = new NSEC3Record(nsec3.getName(), nsec3.getDClass(),
|
||||
nsec3.getTTL(), nsec3.getHashAlgorithm(),
|
||||
nsec3.getFlags(), nsec3.getIterations(),
|
||||
nsec3.getSalt(), nsec3.getNext(),
|
||||
nsec3.getTypes(), ownername);
|
||||
i.set(new_nsec3);
|
||||
}
|
||||
}
|
||||
|
||||
public void execute() throws IOException, NoSuchAlgorithmException
|
||||
{
|
||||
List<Record> z = readZoneFile(state.file);
|
||||
if (state.assignNSEC3) determineNSEC3Owners(z);
|
||||
formatZone(z);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
ZoneFormat tool = new ZoneFormat();
|
||||
tool.state = new CLIState();
|
||||
|
||||
tool.run(tool.state, args);
|
||||
}
|
||||
|
||||
}
|
||||
33
src/main/java/com/verisignlabs/dnssec/cl/package.html
Normal file
33
src/main/java/com/verisignlabs/dnssec/cl/package.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<body bgcolor="#FFFFFF">
|
||||
|
||||
java-dnssec-tools is a collection of Java-based command line tools for
|
||||
managing DNSSEC zones and keys.
|
||||
<p>
|
||||
|
||||
<center>
|
||||
<table width="90%" border=1 cellpadding=10 cellspacing=0>
|
||||
<tr><td class="TableRowColor">
|
||||
|
||||
Copyright © 2003-2005 Verisign, Inc. by
|
||||
<a href="mailto:davidb@verisignlabs.com">David Blacka</a><P>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.<P>
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.<P>
|
||||
|
||||
You should have received a copy of the
|
||||
<a href="http://www.gnu.org/copyleft/lgpl.html">GNU Library General Public License</a>
|
||||
along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
</td></tr>
|
||||
</table><P>
|
||||
</center>
|
||||
|
||||
</body>
|
||||
Reference in New Issue
Block a user