Add ability for jdnssec-signzone to find the necessary keys by either looking in the zone to find DNSKEY RRs, or by looking on disk for key files matching the zonename.
git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@122 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e
This commit is contained in:
parent
49dfddb432
commit
8b61f84308
@ -52,6 +52,7 @@ import org.xbill.DNS.Name;
|
|||||||
import org.xbill.DNS.RRset;
|
import org.xbill.DNS.RRset;
|
||||||
import org.xbill.DNS.Record;
|
import org.xbill.DNS.Record;
|
||||||
import org.xbill.DNS.TextParseException;
|
import org.xbill.DNS.TextParseException;
|
||||||
|
import org.xbill.DNS.Type;
|
||||||
import org.xbill.DNS.utils.base16;
|
import org.xbill.DNS.utils.base16;
|
||||||
|
|
||||||
import com.verisignlabs.dnssec.security.BINDKeyUtils;
|
import com.verisignlabs.dnssec.security.BINDKeyUtils;
|
||||||
@ -117,80 +118,88 @@ public class SignZone
|
|||||||
opts.addOption("F", "fully-sign-keyset", false,
|
opts.addOption("F", "fully-sign-keyset", false,
|
||||||
"sign the zone apex keyset with all available keys.");
|
"sign the zone apex keyset with all available keys.");
|
||||||
|
|
||||||
|
OptionBuilder.hasOptionalArg();
|
||||||
|
OptionBuilder.withLongOpt("verbose");
|
||||||
|
OptionBuilder.withArgName("level");
|
||||||
|
OptionBuilder.withDescription("verbosity level.");
|
||||||
// Argument options
|
// Argument options
|
||||||
opts.addOption(OptionBuilder.hasOptionalArg()
|
opts.addOption(OptionBuilder.create('v'));
|
||||||
.withLongOpt("verbose")
|
|
||||||
.withArgName("level")
|
OptionBuilder.hasArg();
|
||||||
.withDescription("verbosity level.")
|
OptionBuilder.withArgName("dir");
|
||||||
.create('v'));
|
OptionBuilder.withLongOpt("keyset-directory");
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.withDescription("directory to find keyset files (default '.').");
|
||||||
.withArgName("dir")
|
opts.addOption(OptionBuilder.create('d'));
|
||||||
.withLongOpt("keyset-directory")
|
|
||||||
.withDescription("directory to find keyset files (default '.').")
|
OptionBuilder.hasArg();
|
||||||
.create('d'));
|
OptionBuilder.withArgName("dir");
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.withLongOpt("key-directory");
|
||||||
.withArgName("dir")
|
OptionBuilder.withDescription("directory to find key files (default '.').");
|
||||||
.withLongOpt("key-directory")
|
opts.addOption(OptionBuilder.create('D'));
|
||||||
.withDescription("directory to find key files (default '.').")
|
|
||||||
.create('D'));
|
OptionBuilder.hasArg();
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.withArgName("time/offset");
|
||||||
.withArgName("time/offset")
|
OptionBuilder.withLongOpt("start-time");
|
||||||
.withLongOpt("start-time")
|
OptionBuilder.withDescription("signature starting time (default is now - 1 hour)");
|
||||||
.withDescription("signature starting time (default is now - 1 hour)")
|
opts.addOption(OptionBuilder.create('s'));
|
||||||
.create('s'));
|
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.hasArg();
|
||||||
.withArgName("time/offset")
|
OptionBuilder.withArgName("time/offset");
|
||||||
.withLongOpt("expire-time")
|
OptionBuilder.withLongOpt("expire-time");
|
||||||
.withDescription(
|
OptionBuilder.withDescription("signature expiration time (default is start-time + 30 days).");
|
||||||
"signature expiration time (default is start-time + 30 days).")
|
opts.addOption(OptionBuilder.create('e'));
|
||||||
.create('e'));
|
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.hasArg();
|
||||||
.withArgName("outfile")
|
OptionBuilder.withArgName("outfile");
|
||||||
.withDescription(
|
OptionBuilder.withDescription("file the signed zone is written to (default is <origin>.signed).");
|
||||||
"file the signed zone is written to (default is <origin>.signed).")
|
opts.addOption(OptionBuilder.create('f'));
|
||||||
.create('f'));
|
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.hasArg();
|
||||||
.withArgName("KSK file")
|
OptionBuilder.withArgName("KSK file");
|
||||||
.withLongOpt("ksk-file")
|
OptionBuilder.withLongOpt("ksk-file");
|
||||||
.withDescription("this key is a key signing key (may repeat).")
|
OptionBuilder.withDescription("this key is a key signing key (may repeat).");
|
||||||
.create('k'));
|
opts.addOption(OptionBuilder.create('k'));
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
|
||||||
.withArgName("file")
|
OptionBuilder.hasArg();
|
||||||
.withLongOpt("include-file")
|
OptionBuilder.withArgName("file");
|
||||||
.withDescription("include names in this file in the NSEC/NSEC3 chain.")
|
OptionBuilder.withLongOpt("include-file");
|
||||||
.create('I'));
|
OptionBuilder.withDescription("include names in this file in the NSEC/NSEC3 chain.");
|
||||||
|
opts.addOption(OptionBuilder.create('I'));
|
||||||
|
|
||||||
// NSEC3 options
|
// NSEC3 options
|
||||||
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
|
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
|
||||||
opts.addOption("O", "use-opt-out", false,
|
opts.addOption("O", "use-opt-out", false,
|
||||||
"generate a fully Opt-Out zone (only valid with NSEC3).");
|
"generate a fully Opt-Out zone (only valid with NSEC3).");
|
||||||
|
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.hasArg();
|
||||||
.withLongOpt("salt")
|
OptionBuilder.withLongOpt("salt");
|
||||||
.withArgName("hex value")
|
OptionBuilder.withArgName("hex value");
|
||||||
.withDescription("supply a salt value.")
|
OptionBuilder.withDescription("supply a salt value.");
|
||||||
.create('S'));
|
opts.addOption(OptionBuilder.create('S'));
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
|
||||||
.withLongOpt("random-salt")
|
|
||||||
.withArgName("length")
|
|
||||||
.withDescription("generate a random salt.")
|
|
||||||
.create('R'));
|
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
|
||||||
.withLongOpt("iterations")
|
|
||||||
.withArgName("value")
|
|
||||||
.withDescription("use this value for the iterations in NSEC3.")
|
|
||||||
.create());
|
|
||||||
|
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
OptionBuilder.hasArg();
|
||||||
.withArgName("alias:original:mnemonic")
|
OptionBuilder.withLongOpt("random-salt");
|
||||||
.withLongOpt("alg-alias")
|
OptionBuilder.withArgName("length");
|
||||||
.withDescription("Define an alias for an algorithm (may repeat).")
|
OptionBuilder.withDescription("generate a random salt.");
|
||||||
.create('A'));
|
opts.addOption(OptionBuilder.create('R'));
|
||||||
opts.addOption(OptionBuilder.hasArg()
|
|
||||||
.withArgName("id")
|
OptionBuilder.hasArg();
|
||||||
.withLongOpt("ds-digest")
|
OptionBuilder.withLongOpt("iterations");
|
||||||
.withDescription("Digest algorithm to use for generated DSs")
|
OptionBuilder.withArgName("value");
|
||||||
.create());
|
OptionBuilder.withDescription("use this value for the iterations in NSEC3.");
|
||||||
|
opts.addOption(OptionBuilder.create());
|
||||||
|
|
||||||
|
OptionBuilder.hasArg();
|
||||||
|
OptionBuilder.withArgName("alias:original:mnemonic");
|
||||||
|
OptionBuilder.withLongOpt("alg-alias");
|
||||||
|
OptionBuilder.withDescription("Define an alias for an algorithm (may repeat).");
|
||||||
|
opts.addOption(OptionBuilder.create('A'));
|
||||||
|
|
||||||
|
OptionBuilder.hasArg();
|
||||||
|
OptionBuilder.withArgName("id");
|
||||||
|
OptionBuilder.withLongOpt("ds-digest");
|
||||||
|
OptionBuilder.withDescription("Digest algorithm to use for generated DSs");
|
||||||
|
opts.addOption(OptionBuilder.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void parseCommandLine(String[] args)
|
public void parseCommandLine(String[] args)
|
||||||
@ -212,19 +221,19 @@ public class SignZone
|
|||||||
|
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
rootLogger.setLevel(Level.OFF);
|
rootLogger.setLevel(Level.OFF);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
default:
|
default:
|
||||||
rootLogger.setLevel(Level.INFO);
|
rootLogger.setLevel(Level.INFO);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
rootLogger.setLevel(Level.FINE);
|
rootLogger.setLevel(Level.FINE);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
rootLogger.setLevel(Level.ALL);
|
rootLogger.setLevel(Level.ALL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Handler[] handlers = rootLogger.getHandlers();
|
Handler[] handlers = rootLogger.getHandlers();
|
||||||
for (int i = 0; i < handlers.length; i++)
|
for (int i = 0; i < handlers.length; i++)
|
||||||
@ -385,7 +394,7 @@ public class SignZone
|
|||||||
PrintWriter out = new PrintWriter(System.err);
|
PrintWriter out = new PrintWriter(System.err);
|
||||||
|
|
||||||
// print our own usage statement:
|
// print our own usage statement:
|
||||||
out.println("usage: signZone.sh [..options..] "
|
out.println("usage: jdnssec-signzone [..options..] "
|
||||||
+ "zone_file [key_file ...] ");
|
+ "zone_file [key_file ...] ");
|
||||||
f.printHelp(out, 75, "signZone.sh", null, opts,
|
f.printHelp(out, 75, "signZone.sh", null, opts,
|
||||||
HelpFormatter.DEFAULT_LEFT_PAD,
|
HelpFormatter.DEFAULT_LEFT_PAD,
|
||||||
@ -498,6 +507,69 @@ public class SignZone
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List getKeys(List dnskeyrrs, File inDirectory)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
List res = new ArrayList();
|
||||||
|
for (Iterator i = dnskeyrrs.iterator(); i.hasNext();)
|
||||||
|
{
|
||||||
|
// Construct a public-key-only DnsKeyPair just so we can calculate the
|
||||||
|
// base name.
|
||||||
|
DnsKeyPair pub = new DnsKeyPair((DNSKEYRecord) i.next());
|
||||||
|
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 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 keys = new ArrayList();
|
||||||
|
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
|
* This is an implementation of a file filter used for finding BIND 9-style
|
||||||
* keyset-* files.
|
* keyset-* files.
|
||||||
@ -651,15 +723,42 @@ public class SignZone
|
|||||||
|
|
||||||
public static void execute(CLIState state) throws Exception
|
public static void execute(CLIState state) throws Exception
|
||||||
{
|
{
|
||||||
// Load the key pairs.
|
// Read in the zone
|
||||||
|
List records = ZoneUtils.readZoneFile(state.zonefile, null);
|
||||||
|
if (records == null || records.size() == 0)
|
||||||
|
{
|
||||||
|
System.err.println("error: empty zone file");
|
||||||
|
state.usage();
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: should we do what BIND 9.3.x snapshots do and look at
|
// calculate the zone name.
|
||||||
// zone apex DNSKEY RRs, and from that be able to load all of the
|
Name zonename = ZoneUtils.findZoneName(records);
|
||||||
// keys?
|
if (zonename == null)
|
||||||
|
{
|
||||||
|
System.err.println("error: invalid zone file - no SOA");
|
||||||
|
state.usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the key pairs.
|
||||||
|
|
||||||
List keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
List keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
||||||
List kskpairs = getKeys(state.kskFiles, 0, state.keyDirectory);
|
List 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 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
|
// If we don't have any KSKs, but we do have more than one zone
|
||||||
// signing key (presumably), presume that the zone signing keys
|
// signing key (presumably), presume that the zone signing keys
|
||||||
// are just not differentiated and try to figure out which keys
|
// are just not differentiated and try to figure out which keys
|
||||||
@ -694,22 +793,6 @@ public class SignZone
|
|||||||
state.usage();
|
state.usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read in the zone
|
|
||||||
List 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// default the output file, if not set.
|
// default the output file, if not set.
|
||||||
if (state.outputfile == null)
|
if (state.outputfile == null)
|
||||||
{
|
{
|
||||||
|
@ -138,4 +138,37 @@ public class ZoneUtils
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List findRRs(List records, Name name, int type)
|
||||||
|
{
|
||||||
|
List res = new ArrayList();
|
||||||
|
for (Iterator i = records.iterator(); i.hasNext();)
|
||||||
|
{
|
||||||
|
Object o = i.next();
|
||||||
|
|
||||||
|
if (o instanceof Record)
|
||||||
|
{
|
||||||
|
Record r = (Record) o;
|
||||||
|
if (r.getName().equals(name) && r.getType() == type)
|
||||||
|
{
|
||||||
|
res.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (o instanceof RRset)
|
||||||
|
{
|
||||||
|
RRset r = (RRset) o;
|
||||||
|
if (r.getName().equals(name) && r.getType() == type)
|
||||||
|
{
|
||||||
|
for (Iterator j = r.rrs(); j.hasNext();)
|
||||||
|
{
|
||||||
|
res.add(j.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user