add support for algorithm aliases, fix SignZone so you can specify more than one KSK

git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@64 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e
This commit is contained in:
David Blacka 2006-05-23 21:24:00 +00:00
parent 5ba24d35b1
commit 435acff6d0
9 changed files with 631 additions and 308 deletions

View File

@ -27,10 +27,10 @@ import java.util.logging.Logger;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.xbill.DNS.DClass; import org.xbill.DNS.DClass;
import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
import com.verisignlabs.dnssec.security.BINDKeyUtils; import com.verisignlabs.dnssec.security.BINDKeyUtils;
import com.verisignlabs.dnssec.security.DnsKeyAlgorithm;
import com.verisignlabs.dnssec.security.DnsKeyPair; import com.verisignlabs.dnssec.security.DnsKeyPair;
import com.verisignlabs.dnssec.security.JCEDnsSecSigner; import com.verisignlabs.dnssec.security.JCEDnsSecSigner;
@ -66,6 +66,73 @@ public class KeyGen
setupCLI(); setupCLI();
} }
/**
* Set up the command line options.
*
* @return a set of command line options.
*/
private void setupCLI()
{
opts = new Options();
// boolean options
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("k",
"kskflag",
false,
"Key is a key-signing-key (sets the SEP flag).");
// Argument options
OptionBuilder.hasArg();
OptionBuilder.withLongOpt("nametype");
OptionBuilder.withArgName("type");
OptionBuilder.withDescription("ZONE | OTHER (default ZONE)");
opts.addOption(OptionBuilder.create('n'));
OptionBuilder.hasOptionalArg();
OptionBuilder.withLongOpt("verbose");
OptionBuilder.withArgName("level");
OptionBuilder.withDescription("verbosity level -- 0 is silence, "
+ "5 is debug information, " + "6 is trace information.\n"
+ "default is level 5.");
opts.addOption(OptionBuilder.create('v'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("algorithm");
OptionBuilder
.withDescription("RSA | RSASHA1 | RSAMD5 | DH | DSA | alias, "
+ "RSASHA1 is default.");
opts.addOption(OptionBuilder.create('a'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("size");
OptionBuilder.withDescription("key size, in bits. (default = 1024)\n"
+ "RSA|RSASHA1|RSAMD5: [512..4096]\n"
+ "DSA: [512..1024]\n"
+ "DH: [128..4096]");
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'));
OptionBuilder.hasArg();
OptionBuilder.withLongOpt("alg-alias");
OptionBuilder.withArgName("alias:original:mnemonic");
OptionBuilder.withDescription("define an alias for an algorithm");
opts.addOption(OptionBuilder.create('A'));
}
public void parseCommandLine(String[] args) public void parseCommandLine(String[] args)
throws org.apache.commons.cli.ParseException throws org.apache.commons.cli.ParseException
{ {
@ -111,6 +178,16 @@ public class KeyGen
zoneKey = false; zoneKey = false;
} }
} }
String[] optstrs;
if ((optstrs = cli.getOptionValues('A')) != null)
{
for (int i = 0; i < optstrs.length; i++)
{
addArgAlias(optstrs[i]);
}
}
if ((optstr = cli.getOptionValue('a')) != null) if ((optstr = cli.getOptionValue('a')) != null)
{ {
algorithm = parseAlg(optstr); algorithm = parseAlg(optstr);
@ -137,62 +214,23 @@ public class KeyGen
owner = cl_args[0]; owner = cl_args[0];
} }
/** private void addArgAlias(String s)
* Set up the command line options.
*
* @return a set of command line options.
*/
private void setupCLI()
{ {
opts = new Options(); if (s == null) return;
// boolean options DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("k",
"kskflag",
false,
"Key is a key-signing-key (sets the SEP flag).");
// Argument options String[] v = s.split(":");
OptionBuilder.hasArg(); if (v.length < 2) return;
OptionBuilder.withLongOpt("nametype");
OptionBuilder.withArgName("type");
OptionBuilder.withDescription("ZONE | OTHER (default ZONE)");
opts.addOption(OptionBuilder.create('n'));
OptionBuilder.hasOptionalArg(); int alias = parseInt(v[0], -1);
OptionBuilder.withLongOpt("verbose"); if (alias <= 0) return;
OptionBuilder.withArgName("level"); int orig = parseInt(v[1], -1);
OptionBuilder.withDescription("verbosity level -- 0 is silence, " if (orig <= 0) return;
+ "5 is debug information, " + "6 is trace information.\n" String mn = null;
+ "default is level 5."); if (v.length > 2) mn = v[2];
opts.addOption(OptionBuilder.create('v'));
OptionBuilder.hasArg(); algs.addAlias(alias, mn, orig);
OptionBuilder.withArgName("algorithm");
OptionBuilder.withDescription("RSA | RSASHA1 | RSAMD5 | DH | DSA, "
+ "RSASHA1 is default.");
opts.addOption(OptionBuilder.create('a'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("size");
OptionBuilder.withDescription("key size, in bits. (default = 1024)\n"
+ "RSA|RSASHA1|RSAMD5: [512..4096]\n"
+ "DSA: [512..1024]\n"
+ "DH: [128..4096]");
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'));
} }
/** Print out the usage and help statements, then quit. */ /** Print out the usage and help statements, then quit. */
@ -239,34 +277,12 @@ public class KeyGen
private static int parseAlg(String s) private static int parseAlg(String s)
{ {
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
int alg = parseInt(s, -1); int alg = parseInt(s, -1);
if (alg > 0) return alg; if (alg > 0) return alg;
s = s.toUpperCase(); return algs.stringToAlgorithm(s);
if (s.equals("RSA"))
{
return DNSSEC.RSASHA1;
}
else if (s.equals("RSAMD5"))
{
return DNSSEC.RSA;
}
else if (s.equals("DH"))
{
return DNSSEC.DH;
}
else if (s.equals("DSA"))
{
return DNSSEC.DSA;
}
else if (s.equals("RSASHA1"))
{
return DNSSEC.RSASHA1;
}
// default
return DNSSEC.RSASHA1;
} }
public static void execute(CLIState state) throws Exception public static void execute(CLIState state) throws Exception

View File

@ -44,13 +44,7 @@ import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type; 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.*;
import com.verisignlabs.dnssec.security.DnsKeyPair;
import com.verisignlabs.dnssec.security.DnsSecVerifier;
import com.verisignlabs.dnssec.security.JCEDnsSecSigner;
import com.verisignlabs.dnssec.security.RecordComparator;
import com.verisignlabs.dnssec.security.SignUtils;
import com.verisignlabs.dnssec.security.ZoneUtils;
/** /**
* This class forms the command line implementation of a DNSSEC zone signer. * This class forms the command line implementation of a DNSSEC zone signer.
@ -91,6 +85,76 @@ public class SignZone
setupCLI(); setupCLI();
} }
/**
* Set up the command line options.
*
* @return a set of command line options.
*/
private void setupCLI()
{
opts = new Options();
// boolean options
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("a", "verify", false, "verify generated signatures>");
opts.addOption("F",
"fully-sign-keyset",
false,
"sign the zone apex keyset with all available keys.");
// Argument options
opts.addOption(OptionBuilder.hasOptionalArg().withLongOpt("verbose")
.withArgName("level").withDescription("verbosity level")
.create('v'));
opts.addOption(OptionBuilder.hasArg().withArgName("dir")
.withLongOpt("keyset-directory")
.withDescription("directory to find keyset files (default '.').")
.create('d'));
opts.addOption(OptionBuilder.hasArg().withArgName("dir")
.withLongOpt("key-directory")
.withDescription("directory to find key files (default '.').")
.create('D'));
opts.addOption(OptionBuilder.hasArg().withArgName("time/offset")
.withLongOpt("start-time")
.withDescription("signature starting time "
+ "(default is now - 1 hour)").create('s'));
opts.addOption(OptionBuilder.hasArg().withArgName("time/offset")
.withLongOpt("expire-time")
.withDescription("signature expiration time "
+ "(default is start-time + 30 days).").create('e'));
opts.addOption(OptionBuilder.hasArg().withArgName("outfile")
.withDescription("file the signed zone is written to "
+ "(default is <origin>.signed).").create('f'));
opts.addOption(OptionBuilder.hasArg().withArgName("KSK file")
.withLongOpt("ksk-file")
.withDescription("this key is the key signing key.").create('k'));
opts.addOption(OptionBuilder.hasArg().withArgName("file")
.withLongOpt("include-file")
.withDescription("include names in this "
+ "file in the NSEC/NSEC3 chain.").create('I'));
opts.addOption(OptionBuilder.hasArg()
.withArgName("alias:original:mnemonic").withLongOpt("alg-alias")
.withDescription("Define an alias for an algorithm").create('A'));
// NSEC3 options
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
opts.addOption("O",
"use-opt-in",
false,
"generate a fully Opt-In zone.");
opts.addOption(OptionBuilder.hasArg().withLongOpt("salt")
.withArgName("hex value").withDescription("supply a salt value.")
.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());
}
public void parseCommandLine(String[] args) public void parseCommandLine(String[] args)
throws org.apache.commons.cli.ParseException, ParseException, throws org.apache.commons.cli.ParseException, ParseException,
IOException IOException
@ -99,6 +163,7 @@ public class SignZone
CommandLine cli = cli_parser.parse(opts, args); CommandLine cli = cli_parser.parse(opts, args);
String optstr = null; String optstr = null;
String[] optstrs = null;
if (cli.hasOption('h')) usage(); if (cli.hasOption('h')) usage();
@ -138,6 +203,14 @@ public class SignZone
useOptIn = false; useOptIn = false;
} }
if ((optstrs = cli.getOptionValues('A')) != null)
{
for (int i = 0; i < optstrs.length; i++)
{
addArgAlias(optstrs[i]);
}
}
if (cli.hasOption('F')) fullySignKeyset = true; if (cli.hasOption('F')) fullySignKeyset = true;
if ((optstr = cli.getOptionValue('d')) != null) if ((optstr = cli.getOptionValue('d')) != null)
@ -182,15 +255,7 @@ public class SignZone
outputfile = cli.getOptionValue('f'); outputfile = cli.getOptionValue('f');
// FIXME: this is a bit awkward, because we really want -k to repeat, kskFiles = cli.getOptionValues('k');
// but the CLI classes don't do it quite right. Instead we just convert
// our single argument to an array.
String kskFile = cli.getOptionValue('k');
if (kskFile != null)
{
kskFiles = new String[1];
kskFiles[0] = kskFile;
}
if ((optstr = cli.getOptionValue('I')) != null) if ((optstr = cli.getOptionValue('I')) != null)
{ {
@ -235,72 +300,23 @@ public class SignZone
} }
} }
/** private void addArgAlias(String s)
* Set up the command line options.
*
* @return a set of command line options.
*/
private void setupCLI()
{ {
opts = new Options(); if (s == null) return;
// boolean options DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("a", "verify", false, "verify generated signatures>");
opts.addOption("F",
"fully-sign-keyset",
false,
"sign the zone apex keyset with all available keys.");
// Argument options String[] v = s.split(":");
opts.addOption(OptionBuilder.hasOptionalArg().withLongOpt("verbose") if (v.length < 2) return;
.withArgName("level").withDescription("verbosity level")
.create('v'));
opts.addOption(OptionBuilder.hasArg().withArgName("dir")
.withLongOpt("keyset-directory")
.withDescription("directory to find keyset files (default '.').")
.create('d'));
opts.addOption(OptionBuilder.hasArg().withArgName("dir")
.withLongOpt("key-directory")
.withDescription("directory to find key files (default '.').")
.create('D'));
opts.addOption(OptionBuilder.hasArg().withArgName("time/offset")
.withLongOpt("start-time")
.withDescription("signature starting time "
+ "(default is now - 1 hour)").create('s'));
opts.addOption(OptionBuilder.hasArg().withArgName("time/offset")
.withLongOpt("expire-time")
.withDescription("signature expiration time "
+ "(default is start-time + 30 days).").create('e'));
opts.addOption(OptionBuilder.hasArg().withArgName("outfile")
.withDescription("file the signed zone is written to "
+ "(default is <origin>.signed).").create('f'));
opts.addOption(OptionBuilder.hasArg()
.withArgName("KSK file").withLongOpt("ksk-file")
.withDescription("this key is the key signing key.")
.create('k'));
opts.addOption(OptionBuilder.hasArg().withArgName("file")
.withLongOpt("include-file")
.withDescription("include names in this "
+ "file in the NSEC/NSEC3 chain.").create('I'));
// NSEC3 options int alias = parseInt(v[0], -1);
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC"); if (alias <= 0) return;
opts.addOption("O", int orig = parseInt(v[1], -1);
"use-opt-in", if (orig <= 0) return;
false, String mn = null;
"generate a fully Opt-In zone."); if (v.length > 2) mn = v[2];
opts.addOption(OptionBuilder.hasArg().withLongOpt("salt") algs.addAlias(alias, mn, orig);
.withArgName("hex value").withDescription("supply a salt value.")
.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());
} }
/** Print out the usage and help statements, then quit. */ /** Print out the usage and help statements, then quit. */

View File

@ -33,12 +33,7 @@ import org.apache.commons.cli.*;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.xbill.DNS.*; import org.xbill.DNS.*;
import com.verisignlabs.dnssec.security.BINDKeyUtils; import com.verisignlabs.dnssec.security.*;
import com.verisignlabs.dnssec.security.DnsKeyPair;
import com.verisignlabs.dnssec.security.DnsSecVerifier;
import com.verisignlabs.dnssec.security.RecordComparator;
import com.verisignlabs.dnssec.security.SignUtils;
import com.verisignlabs.dnssec.security.ZoneUtils;
/** /**
* This class forms the command line implementation of a DNSSEC zone * This class forms the command line implementation of a DNSSEC zone
@ -69,6 +64,40 @@ public class VerifyZone
setupCLI(); setupCLI();
} }
/**
* Set up the command line options.
*
* @return a set of command line options.
*/
private void setupCLI()
{
opts = new Options();
// boolean options
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("s",
"strict",
false,
"Zone will only be considered valid if all "
+ "signatures could be cryptographically verified");
// Argument options
opts.addOption(OptionBuilder.hasArg().withLongOpt("keydir")
.withArgName("dir").withDescription("directory to find "
+ "trusted key files").create('d'));
opts.addOption(OptionBuilder.hasOptionalArg().withLongOpt("verbose")
.withArgName("level")
.withDescription("verbosity level -- 0 is silence, "
+ "5 is debug information, 6 is trace information.\n"
+ "default is level 5.").create('v'));
opts.addOption(OptionBuilder.hasArg()
.withArgName("alias:original:mnemonic").withLongOpt("alg-alias")
.withDescription("Define an alias for an algorithm").create('A'));
}
public void parseCommandLine(String[] args) public void parseCommandLine(String[] args)
throws org.apache.commons.cli.ParseException throws org.apache.commons.cli.ParseException
{ {
@ -105,6 +134,15 @@ public class VerifyZone
keydir = new File(optstr); keydir = new File(optstr);
} }
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(); String[] cl_args = cli.getArgs();
if (cl_args.length < 1) if (cl_args.length < 1)
@ -122,38 +160,23 @@ public class VerifyZone
} }
} }
/** private void addArgAlias(String s)
* Set up the command line options.
*
* @return a set of command line options.
*/
private void setupCLI()
{ {
opts = new Options(); if (s == null) return;
// boolean options DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("s",
"strict",
false,
"Zone will only be considered valid if all "
+ "signatures could be cryptographically verified");
// Argument options String[] v = s.split(":");
OptionBuilder.hasArg(); if (v.length < 2) return;
OptionBuilder.withLongOpt("keydir");
OptionBuilder.withArgName("dir");
OptionBuilder.withDescription("directory to find trusted key files");
opts.addOption(OptionBuilder.create('d'));
OptionBuilder.hasOptionalArg(); int alias = parseInt(v[0], -1);
OptionBuilder.withLongOpt("verbose"); if (alias <= 0) return;
OptionBuilder.withArgName("level"); int orig = parseInt(v[1], -1);
OptionBuilder.withDescription("verbosity level -- 0 is silence, " if (orig <= 0) return;
+ "5 is debug information, 6 is trace information.\n" String mn = null;
+ "default is level 5."); if (v.length > 2) mn = v[2];
opts.addOption(OptionBuilder.create('v'));
algs.addAlias(alias, mn, orig);
} }
/** Print out the usage and help statements, then quit. */ /** Print out the usage and help statements, then quit. */
@ -209,7 +232,9 @@ public class VerifyZone
for (Iterator i = keypairs.iterator(); i.hasNext();) for (Iterator i = keypairs.iterator(); i.hasNext();)
{ {
verifier.addTrustedKey((DnsKeyPair) i.next()); DnsKeyPair pair = (DnsKeyPair) i.next();
if (pair.getPublic() == null) continue;
verifier.addTrustedKey(pair);
} }
List rrsets = SignUtils.assembleIntoRRsets(records); List rrsets = SignUtils.assembleIntoRRsets(records);
@ -255,7 +280,7 @@ public class VerifyZone
zonename = r.getName(); zonename = r.getName();
} }
if (r.getName().equals(zonename) && r.getType() == Type.DNSKEY) if (r.getName().equals(zonename) && r.getType() == Type.DNSKEY)
{ {
DnsKeyPair pair = new DnsKeyPair((DNSKEYRecord) r); DnsKeyPair pair = new DnsKeyPair((DNSKEYRecord) r);
res.add(pair); res.add(pair);
@ -264,6 +289,7 @@ public class VerifyZone
return res; return res;
} }
private static List getTrustedKeys(String[] keyfiles, File inDirectory) private static List getTrustedKeys(String[] keyfiles, File inDirectory)
throws IOException throws IOException
{ {
@ -283,7 +309,6 @@ public class VerifyZone
public static void execute(CLIState state) throws Exception public static void execute(CLIState state) throws Exception
{ {
List records = ZoneUtils.readZoneFile(state.zonefile, null); List records = ZoneUtils.readZoneFile(state.zonefile, null);
List keypairs = null; List keypairs = null;
if (state.keyfiles != null) if (state.keyfiles != null)

View File

@ -0,0 +1,263 @@
/*
* $Id$
*
* Copyright (c) 2006 VeriSign. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. The name of the author may not
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package com.verisignlabs.dnssec.security;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.util.HashMap;
import java.util.logging.Logger;
import org.xbill.DNS.DNSSEC;
/**
* This class handles translated DNS signing algorithm identifiers into
* various usable java implementations.
*
* Besides centralizing the logic surrounding matching a DNSKEY algorithm
* identifier with various crypto implementations, it also handles algorithm
* aliasing -- that is, defining a new algorithm identifier to be equivalent
* to an existing identifier.
*
* @author David Blacka (orig)
* @author $Author: davidb $ (latest)
* @version $Revision: 2098 $
*/
public class DnsKeyAlgorithm
{
public static final int UNKNOWN = -1;
public static final int RSA = 1;
public static final int DH = 2;
public static final int DSA = 3;
private static class Entry
{
public String sigName;
public int baseType;
public Entry(String sigName, int baseType)
{
this.sigName = sigName;
this.baseType = baseType;
}
}
/**
* This is a mapping of algorithm identifier to Entry. The Entry contains
* the data needed to map the algorithm to the various crypto
* implementations.
*/
private HashMap mAlgorithmMap;
/**
* This is a mapping of algorithm mnemonics to algorithm identifiers.
*/
private HashMap mMnemonicToIdMap;
/**
* This is a mapping of identifiers to preferred mnemonic -- the preferred
* one is the first defined one
*/
private HashMap mIdToMnemonicMap;
/** This is a cached key pair generator for RSA keys. */
private KeyPairGenerator mRSAKeyGenerator;
/** This is a cache key pair generator for DSA keys. */
private KeyPairGenerator mDSAKeyGenerator;
private Logger log = Logger.getLogger(this.getClass()
.toString());
/** This is the global instance for this class. */
private static DnsKeyAlgorithm mInstance = null;
public DnsKeyAlgorithm()
{
mAlgorithmMap = new HashMap();
mMnemonicToIdMap = new HashMap();
mIdToMnemonicMap = new HashMap();
// Load the standard DNSSEC algorithms.
addAlgorithm(DNSSEC.RSAMD5, new Entry("MD5withRSA", RSA));
addMnemonic("RSAMD5", DNSSEC.RSAMD5);
addAlgorithm(DNSSEC.DH, new Entry("", DH));
addMnemonic("DH", DNSSEC.DH);
addAlgorithm(DNSSEC.DSA, new Entry("SHA1withDSA", DSA));
addMnemonic("DSA", DNSSEC.DSA);
addAlgorithm(DNSSEC.RSASHA1, new Entry("SHA1withRSA", RSA));
addMnemonic("RSASHA1", DNSSEC.RSASHA1);
addMnemonic("RSA", DNSSEC.RSASHA1);
}
private void addAlgorithm(int algorithm, Entry entry)
{
Integer a = new Integer(algorithm);
mAlgorithmMap.put(a, entry);
}
private void addMnemonic(String m, int alg)
{
Integer a = new Integer(alg);
mMnemonicToIdMap.put(m.toUpperCase(), a);
if (! mIdToMnemonicMap.containsKey(a))
{
mIdToMnemonicMap.put(a, m);
}
}
public void addAlias(int alias, String mnemonic, int original_algorithm)
{
Integer a = new Integer(alias);
Integer o = new Integer(original_algorithm);
if (mAlgorithmMap.containsKey(a))
{
log.warning("Unable to alias algorithm " + alias
+ " because it already exists.");
return;
}
if (!mAlgorithmMap.containsKey(o))
{
log.warning("Unable to alias algorith " + alias
+ " to unknown algorithm identifier " + original_algorithm);
return;
}
mAlgorithmMap.put(a, mAlgorithmMap.get(o));
if (mnemonic != null)
{
addMnemonic(mnemonic, alias);
}
}
private Entry getEntry(int alg)
{
return (Entry) mAlgorithmMap.get(new Integer(alg));
}
public Signature getSignature(int algorithm)
{
Entry entry = getEntry(algorithm);
if (entry == null) return null;
Signature s = null;
try
{
s = Signature.getInstance(entry.sigName);
}
catch (NoSuchAlgorithmException e)
{
log.severe("Unable to get signature implementation for algorithm "
+ algorithm + ": " + e);
}
return s;
}
public int stringToAlgorithm(String s)
{
Integer alg = (Integer) mMnemonicToIdMap.get(s.toUpperCase());
if (alg != null) return alg.intValue();
return -1;
}
public String algToString(int algorithm)
{
return (String) mIdToMnemonicMap.get(new Integer(algorithm));
}
public int baseType(int algorithm)
{
Entry entry = getEntry(algorithm);
if (entry != null) return entry.baseType;
return UNKNOWN;
}
public int standardAlgorithm(int algorithm)
{
switch (baseType(algorithm))
{
case RSA :
return DNSSEC.RSASHA1;
case DSA :
return DNSSEC.DSA;
case DH :
return DNSSEC.DH;
default :
return UNKNOWN;
}
}
public boolean isDSA(int algorithm)
{
return (baseType(algorithm) == DSA);
}
public KeyPair generateKeyPair(int algorithm, int keysize)
throws NoSuchAlgorithmException
{
KeyPair pair = null;
switch (baseType(algorithm))
{
case RSA :
if (mRSAKeyGenerator == null)
{
mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA");
}
mRSAKeyGenerator.initialize(keysize);
pair = mRSAKeyGenerator.generateKeyPair();
break;
case DSA :
if (mDSAKeyGenerator == null)
{
mDSAKeyGenerator = KeyPairGenerator.getInstance("DSA");
}
mDSAKeyGenerator.initialize(keysize);
pair = mDSAKeyGenerator.generateKeyPair();
break;
default :
throw new NoSuchAlgorithmException("Alg " + algorithm);
}
return pair;
}
public static DnsKeyAlgorithm getInstance()
{
if (mInstance == null) mInstance = new DnsKeyAlgorithm();
return mInstance;
}
}

View File

@ -44,7 +44,6 @@ import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPrivateKeySpec; import javax.crypto.spec.DHPrivateKeySpec;
import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.KEYRecord; import org.xbill.DNS.KEYRecord;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
import org.xbill.DNS.security.KEYConverter; import org.xbill.DNS.security.KEYConverter;
@ -68,11 +67,35 @@ public class DnsKeyConverter
{ {
} }
/** Given a DNS KEY record, return the JCA public key */ /**
* Given a DNS KEY record, return the JCA public key
*
* @throws NoSuchAlgorithmException
*/
public PublicKey parseDNSKEYRecord(DNSKEYRecord pKeyRecord) public PublicKey parseDNSKEYRecord(DNSKEYRecord pKeyRecord)
throws NoSuchAlgorithmException
{ {
if (pKeyRecord.getKey() == null) return null; if (pKeyRecord.getKey() == null) return null;
// FIXME: this won't work at all with alg aliases.
// For now, instead of re-implementing parseRecord (or adding this stuff
// to DNSjava), we will just translate the algorithm back to a standard
// algorithm. Note that this will unnecessarily convert RSAMD5 to RSASHA1.
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
int standard_alg = algs.standardAlgorithm(pKeyRecord.getAlgorithm());
if (standard_alg <= 0)
throw new NoSuchAlgorithmException("DNSKEY algorithm "
+ pKeyRecord.getAlgorithm() + " is unrecognized");
if (pKeyRecord.getAlgorithm() != standard_alg)
{
pKeyRecord = new DNSKEYRecord(pKeyRecord.getName(), pKeyRecord
.getDClass(), pKeyRecord.getTTL(), pKeyRecord.getFlags(),
pKeyRecord.getProtocol(), standard_alg, pKeyRecord.getKey());
}
return KEYConverter.parseRecord(pKeyRecord); return KEYConverter.parseRecord(pKeyRecord);
} }
@ -104,15 +127,15 @@ public class DnsKeyConverter
public PrivateKey convertEncodedPrivateKey(byte[] key, int algorithm) public PrivateKey convertEncodedPrivateKey(byte[] key, int algorithm)
{ {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
try try
{ {
switch (algorithm) switch (algs.baseType(algorithm))
{ {
case DNSSEC.RSAMD5 : case DnsKeyAlgorithm.RSA :
case DNSSEC.RSASHA1 :
return mRSAKeyFactory.generatePrivate(spec); return mRSAKeyFactory.generatePrivate(spec);
case DNSSEC.DSA : case DnsKeyAlgorithm.DSA :
return mDSAKeyFactory.generatePrivate(spec); return mDSAKeyFactory.generatePrivate(spec);
} }
} }
@ -122,11 +145,23 @@ public class DnsKeyConverter
return null; return null;
} }
private int parseInt(String s, int def)
{
try
{
return Integer.parseInt(s);
}
catch (NumberFormatException e)
{
return def;
}
}
/** /**
* @return a JCA private key, given a BIND9-style textual encoding * @return a JCA private key, given a BIND9-style textual encoding
*/ */
public PrivateKey parsePrivateKeyString(String key) throws IOException, public PrivateKey parsePrivateKeyString(String key)
NoSuchAlgorithmException throws IOException, NoSuchAlgorithmException
{ {
StringTokenizer lines = new StringTokenizer(key, "\n"); StringTokenizer lines = new StringTokenizer(key, "\n");
@ -149,11 +184,24 @@ public class DnsKeyConverter
} }
else if (line.startsWith("Algorithm: ")) else if (line.startsWith("Algorithm: "))
{ {
if (val.startsWith("1 ")) return parsePrivateRSA(lines); // here we assume that the value looks like # (MNEM) or just the
if (val.startsWith("5 ")) return parsePrivateRSA(lines); // number.
if (val.startsWith("2 ")) return parsePrivateDH(lines); String[] toks = val.split("\\s", 2);
if (val.startsWith("3 ")) return parsePrivateDSA(lines); val = toks[0];
throw new IOException("unsupported private key algorithm: " + val); int alg = parseInt(val, -1);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
switch (algs.baseType(alg))
{
case DnsKeyAlgorithm.RSA :
return parsePrivateRSA(lines);
case DnsKeyAlgorithm.DSA :
return parsePrivateDSA(lines);
case DnsKeyAlgorithm.DH :
return parsePrivateDH(lines);
default :
throw new IOException("unsupported private key algorithm: " + val);
}
} }
} }
return null; return null;
@ -390,11 +438,11 @@ public class DnsKeyConverter
} }
else if (priv instanceof DSAPrivateKey && pub instanceof DSAPublicKey) else if (priv instanceof DSAPrivateKey && pub instanceof DSAPublicKey)
{ {
return generatePrivateDSA((DSAPrivateKey) priv, (DSAPublicKey) pub); return generatePrivateDSA((DSAPrivateKey) priv, (DSAPublicKey) pub, alg);
} }
else if (priv instanceof DHPrivateKey && pub instanceof DHPublicKey) else if (priv instanceof DHPrivateKey && pub instanceof DHPublicKey)
{ {
return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub); return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub, alg);
} }
return null; return null;
@ -426,16 +474,11 @@ public class DnsKeyConverter
{ {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw); PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
out.println("Private-key-format: v1.2"); out.println("Private-key-format: v1.2");
if (algorithm == DNSSEC.RSAMD5) out.println("Algorithm: " + algorithm + " ("
{ + algs.algToString(algorithm) + ")");
out.println("Algorithm: 1 (RSAMD5)");
}
else
{
out.println("Algorithm: 5 (RSASHA1)");
}
out.print("Modulus: "); out.print("Modulus: ");
out.println(b64BigInt(key.getModulus())); out.println(b64BigInt(key.getModulus()));
out.print("PublicExponent: "); out.print("PublicExponent: ");
@ -457,15 +500,18 @@ public class DnsKeyConverter
} }
/** Given a DH key pair, return the BIND9-style text encoding */ /** Given a DH key pair, return the BIND9-style text encoding */
private String generatePrivateDH(DHPrivateKey key, DHPublicKey pub) private String generatePrivateDH(DHPrivateKey key, DHPublicKey pub,
int algorithm)
{ {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw); PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
DHParameterSpec p = key.getParams(); DHParameterSpec p = key.getParams();
out.println("Private-key-format: v1.2"); out.println("Private-key-format: v1.2");
out.println("Algorithm: 2 (DH)"); out.println("Algorithm: " + algorithm + " ("
+ algs.algToString(algorithm) + ")");
out.print("Prime(p): "); out.print("Prime(p): ");
out.println(b64BigInt(p.getP())); out.println(b64BigInt(p.getP()));
out.print("Generator(g): "); out.print("Generator(g): ");
@ -479,15 +525,18 @@ public class DnsKeyConverter
} }
/** Given a DSA key pair, return the BIND9-style text encoding */ /** Given a DSA key pair, return the BIND9-style text encoding */
private String generatePrivateDSA(DSAPrivateKey key, DSAPublicKey pub) private String generatePrivateDSA(DSAPrivateKey key, DSAPublicKey pub,
int algorithm)
{ {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw); PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
DSAParams p = key.getParams(); DSAParams p = key.getParams();
out.println("Private-key-format: v1.2"); out.println("Private-key-format: v1.2");
out.println("Algorithm: 3 (DSA)"); out.println("Algorithm: " + algorithm + " ("
+ algs.algToString(algorithm) + ")");
out.print("Prime(p): "); out.print("Prime(p): ");
out.println(b64BigInt(p.getP())); out.println(b64BigInt(p.getP()));
out.print("Subprime(q): "); out.print("Subprime(q): ");

View File

@ -146,35 +146,8 @@ public class DnsKeyPair
/** @return the appropriate Signature object for this keypair. */ /** @return the appropriate Signature object for this keypair. */
protected Signature getSignature() protected Signature getSignature()
{ {
Signature s = null; DnsKeyAlgorithm algorithms = DnsKeyAlgorithm.getInstance();
return algorithms.getSignature(getDNSKEYAlgorithm());
// First try and deduce the algorithm from the KEYRecord (which
// will be specific), then try and deduce it from the private key.
// We should have one or the other.
try
{
switch (getDNSKEYAlgorithm())
{
case DNSSEC.RSAMD5 :
s = Signature.getInstance("MD5withRSA");
break;
case DNSSEC.DSA :
s = Signature.getInstance("SHA1withDSA");
break;
case DNSSEC.RSASHA1 :
s = Signature.getInstance("SHA1withRSA");
break;
case -1 :
s = null;
break;
}
}
catch (NoSuchAlgorithmException e)
{
log.severe("error getting Signature object: " + e);
}
return s;
} }
/** /**
@ -184,8 +157,16 @@ public class DnsKeyPair
{ {
if (mPublicKey == null && getDNSKEYRecord() != null) if (mPublicKey == null && getDNSKEYRecord() != null)
{ {
DnsKeyConverter conv = getKeyConverter(); try
setPublic(conv.parseDNSKEYRecord(getDNSKEYRecord())); {
DnsKeyConverter conv = getKeyConverter();
setPublic(conv.parseDNSKEYRecord(getDNSKEYRecord()));
}
catch (NoSuchAlgorithmException e)
{
log.severe(e.toString());
return null;
}
} }
return mPublicKey; return mPublicKey;
@ -220,6 +201,7 @@ public class DnsKeyPair
/** /**
* @return the opaque private key string, null if one doesn't exist. * @return the opaque private key string, null if one doesn't exist.
* @throws NoSuchAlgorithmException
*/ */
public String getPrivateKeyString() public String getPrivateKeyString()
{ {
@ -304,6 +286,7 @@ public class DnsKeyPair
/** /**
* @return a Signature object initialized for verifying, or null if this key * @return a Signature object initialized for verifying, or null if this key
* pair does not have a valid public key. * pair does not have a valid public key.
* @throws NoSuchAlgorithmException
*/ */
public Signature getVerifier() public Signature getVerifier()
{ {

View File

@ -263,14 +263,18 @@ public class DnsSecVerifier implements Verifier
{ {
byte[] data = SignUtils.generateSigData(rrset, sigrec); byte[] data = SignUtils.generateSigData(rrset, sigrec);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
Signature signer = keypair.getVerifier(); Signature signer = keypair.getVerifier();
signer.update(data); signer.update(data);
byte[] sig = sigrec.getSignature(); byte[] sig = sigrec.getSignature();
if (sigrec.getAlgorithm() == DNSSEC.DSA)
if (algs.baseType(sigrec.getAlgorithm()) == DnsKeyAlgorithm.DSA)
{ {
sig = SignUtils.convertDSASignature(sig); sig = SignUtils.convertDSASignature(sig);
} }
if (!signer.verify(sig)) if (!signer.verify(sig))
{ {
log.info("Signature failed to verify cryptographically"); log.info("Signature failed to verify cryptographically");

View File

@ -22,7 +22,6 @@ package com.verisignlabs.dnssec.security;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Signature; import java.security.Signature;
import java.security.interfaces.DSAPublicKey; import java.security.interfaces.DSAPublicKey;
@ -34,7 +33,6 @@ import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
import org.xbill.DNS.RRSIGRecord; import org.xbill.DNS.RRSIGRecord;
import org.xbill.DNS.RRset; import org.xbill.DNS.RRset;
@ -54,10 +52,7 @@ import org.xbill.DNS.Type;
*/ */
public class JCEDnsSecSigner public class JCEDnsSecSigner
{ {
private DnsKeyConverter mKeyConverter; private DnsKeyConverter mKeyConverter;
private KeyPairGenerator mRSAKeyGenerator;
private KeyPairGenerator mDSAKeyGenerator;
/** /**
* Cryptographically generate a new DNSSEC key. * Cryptographically generate a new DNSSEC key.
@ -73,32 +68,11 @@ public class JCEDnsSecSigner
public DnsKeyPair generateKey(Name owner, long ttl, int dclass, public DnsKeyPair generateKey(Name owner, long ttl, int dclass,
int algorithm, int flags, int keysize) throws NoSuchAlgorithmException int algorithm, int flags, int keysize) throws NoSuchAlgorithmException
{ {
KeyPair pair; DnsKeyAlgorithm algorithms = DnsKeyAlgorithm.getInstance();
if (ttl < 0) ttl = 86400; // set to a reasonable default. if (ttl < 0) ttl = 86400; // set to a reasonable default.
switch (algorithm) KeyPair pair = algorithms.generateKeyPair(algorithm, keysize);
{
case DNSSEC.RSAMD5 :
case DNSSEC.RSASHA1 :
if (mRSAKeyGenerator == null)
{
mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA");
}
mRSAKeyGenerator.initialize(keysize);
pair = mRSAKeyGenerator.generateKeyPair();
break;
case DNSSEC.DSA :
if (mDSAKeyGenerator == null)
{
mDSAKeyGenerator = KeyPairGenerator.getInstance("DSA");
}
mDSAKeyGenerator.initialize(keysize);
pair = mDSAKeyGenerator.generateKeyPair();
break;
default :
throw new NoSuchAlgorithmException("Alg " + algorithm);
}
if (mKeyConverter == null) if (mKeyConverter == null)
{ {
@ -111,6 +85,7 @@ public class JCEDnsSecSigner
flags, flags,
algorithm, algorithm,
pair.getPublic()); pair.getPublic());
DnsKeyPair dnspair = new DnsKeyPair(); DnsKeyPair dnspair = new DnsKeyPair();
dnspair.setDNSKEYRecord(keyrec); dnspair.setDNSKEYRecord(keyrec);
dnspair.setPublic(pair.getPublic()); // keep from conv. the keyrec back. dnspair.setPublic(pair.getPublic()); // keep from conv. the keyrec back.
@ -173,11 +148,12 @@ public class JCEDnsSecSigner
signer.update(sign_data); signer.update(sign_data);
byte[] sig = signer.sign(); byte[] sig = signer.sign();
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
// Convert to RFC 2536 format, if necessary. // Convert to RFC 2536 format, if necessary.
if (pair.getDNSKEYAlgorithm() == DNSSEC.DSA) if (algs.baseType(pair.getDNSKEYAlgorithm()) == DnsKeyAlgorithm.DSA)
{ {
sig = SignUtils.convertDSASignature(((DSAPublicKey) pair.getPublic()).getParams(), sig = SignUtils.convertDSASignature(((DSAPublicKey) pair.getPublic())
sig); .getParams(), sig);
} }
RRSIGRecord sigrec = SignUtils.generateRRSIG(sig, presig); RRSIGRecord sigrec = SignUtils.generateRRSIG(sig, presig);
sigs.add(sigrec); sigs.add(sigrec);
@ -234,8 +210,8 @@ public class JCEDnsSecSigner
*/ */
private Name addRRset(List toList, Name zonename, RRset rrset, private Name addRRset(List toList, Name zonename, RRset rrset,
List keysigningkeypairs, List zonekeypairs, Date start, Date expire, List keysigningkeypairs, List zonekeypairs, Date start, Date expire,
boolean fullySignKeyset, Name last_cut) throws IOException, boolean fullySignKeyset, Name last_cut)
GeneralSecurityException throws IOException, GeneralSecurityException
{ {
// add the records themselves // add the records themselves
for (Iterator i = rrset.rrs(); i.hasNext();) for (Iterator i = rrset.rrs(); i.hasNext();)
@ -243,10 +219,8 @@ public class JCEDnsSecSigner
toList.add(i.next()); toList.add(i.next());
} }
int type = SignUtils.recordSecType(zonename, int type = SignUtils.recordSecType(zonename, rrset.getName(), rrset
rrset.getName(), .getType(), last_cut);
rrset.getType(),
last_cut);
// we don't sign non-normal sets (delegations, glue, invalid). // we don't sign non-normal sets (delegations, glue, invalid).
// we also don't sign the zone key set unless we've been asked. // we also don't sign the zone key set unless we've been asked.

View File

@ -157,13 +157,6 @@ public class SignUtils
// Caculate the offset where the RDATA begins (we have to skip // Caculate the offset where the RDATA begins (we have to skip
// past the length byte) // past the length byte)
// FIXME: currently, draft-ietf-dnsext-dnssec-records-06 has us
// sorting by length first, then bytes. This can be accomplished
// by not skipping past the RDLENGTH field, I think.
// FIXME update: I pointed this out as an error, and subsequent
// versions should correct this, setting the standard back to
// bytes, then length.
int offset = rrset.getName().toWireCanonical().length + 10; int offset = rrset.getName().toWireCanonical().length + 10;
ByteArrayComparator bac = new ByteArrayComparator(offset, false); ByteArrayComparator bac = new ByteArrayComparator(offset, false);