From e5270de8ee11015e120c1570d672e6c2e06c8671 Mon Sep 17 00:00:00 2001 From: David Blacka Date: Mon, 2 Feb 2009 04:45:49 +0000 Subject: [PATCH] Move all signZone() method variants into JCEDnsSecSigner, make the SignZone class use them. git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@112 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e --- src/com/verisignlabs/dnssec/cl/SignZone.java | 548 +++++------------- .../dnssec/security/JCEDnsSecSigner.java | 428 ++++++++++---- 2 files changed, 444 insertions(+), 532 deletions(-) diff --git a/src/com/verisignlabs/dnssec/cl/SignZone.java b/src/com/verisignlabs/dnssec/cl/SignZone.java index 01ccdc5..4e1f496 100644 --- a/src/com/verisignlabs/dnssec/cl/SignZone.java +++ b/src/com/verisignlabs/dnssec/cl/SignZone.java @@ -1,6 +1,6 @@ // $Id$ // -// Copyright (C) 2001-2003 VeriSign, Inc. +// Copyright (C) 2001-2003, 2009 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -25,21 +25,42 @@ import java.io.FileFilter; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.security.GeneralSecurityException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.TimeZone; import java.util.logging.Handler; -import java.util.logging.Logger; import java.util.logging.Level; +import java.util.logging.Logger; -import org.apache.commons.cli.*; +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.xbill.DNS.*; +import org.apache.commons.cli.PosixParser; +import org.apache.commons.cli.UnrecognizedOptionException; +import org.xbill.DNS.DNSKEYRecord; +import org.xbill.DNS.DNSSEC; +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.utils.base16; -import com.verisignlabs.dnssec.security.*; +import com.verisignlabs.dnssec.security.BINDKeyUtils; +import com.verisignlabs.dnssec.security.DnsKeyAlgorithm; +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. @@ -93,66 +114,80 @@ public class SignZone // 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."); + 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.") + opts.addOption(OptionBuilder.hasOptionalArg() + .withLongOpt("verbose") + .withArgName("level") + .withDescription("verbosity level.") .create('v')); - opts.addOption(OptionBuilder.hasArg().withArgName("dir") + opts.addOption(OptionBuilder.hasArg() + .withArgName("dir") .withLongOpt("keyset-directory") .withDescription("directory to find keyset files (default '.').") .create('d')); - opts.addOption(OptionBuilder.hasArg().withArgName("dir") + 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") + 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") + .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 .signed).").create('f')); - opts.addOption(OptionBuilder.hasArg().withArgName("KSK file") + .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 .signed).") + .create('f')); + opts.addOption(OptionBuilder.hasArg() + .withArgName("KSK file") .withLongOpt("ksk-file") .withDescription("this key is a key signing key (may repeat).") .create('k')); - opts.addOption(OptionBuilder.hasArg().withArgName("file") + opts.addOption(OptionBuilder.hasArg() + .withArgName("file") .withLongOpt("include-file") - .withDescription("include names in this " - + "file in the NSEC/NSEC3 chain.").create('I')); + .withDescription("include names in this file in the NSEC/NSEC3 chain.") + .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)."); + opts.addOption("O", "use-opt-out", false, + "generate a fully Opt-Out zone (only valid with NSEC3)."); - opts.addOption(OptionBuilder.hasArg().withLongOpt("salt") - .withArgName("hex value").withDescription("supply a salt value.") + 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.") + opts.addOption(OptionBuilder.hasArg() + .withLongOpt("random-salt") + .withArgName("length") + .withDescription("generate a random salt.") .create('R')); - opts.addOption(OptionBuilder.hasArg().withLongOpt("iterations") + opts.addOption(OptionBuilder.hasArg() + .withLongOpt("iterations") .withArgName("value") .withDescription("use this value for the iterations in NSEC3.") .create()); opts.addOption(OptionBuilder.hasArg() - .withArgName("alias:original:mnemonic").withLongOpt("alg-alias") + .withArgName("alias:original:mnemonic") + .withLongOpt("alg-alias") .withDescription("Define an alias for an algorithm (may repeat).") .create('A')); - opts.addOption(OptionBuilder.hasArg().withArgName("id") + opts.addOption(OptionBuilder.hasArg() + .withArgName("id") .withLongOpt("ds-digest") .withDescription("Digest algorithm to use for generated DSs") .create()); @@ -177,19 +212,19 @@ public class SignZone switch (value) { - case 0 : - rootLogger.setLevel(Level.OFF); - break; - case 4 : - default : - rootLogger.setLevel(Level.INFO); - break; - case 5 : - rootLogger.setLevel(Level.FINE); - break; - case 6 : - rootLogger.setLevel(Level.ALL); - break; + case 0: + rootLogger.setLevel(Level.OFF); + break; + case 4: + default: + rootLogger.setLevel(Level.INFO); + break; + case 5: + rootLogger.setLevel(Level.FINE); + break; + case 6: + rootLogger.setLevel(Level.ALL); + break; } Handler[] handlers = rootLogger.getHandlers(); for (int i = 0; i < handlers.length; i++) @@ -352,14 +387,10 @@ public class SignZone // print our own usage statement: out.println("usage: signZone.sh [..options..] " + "zone_file [key_file ...] "); - f.printHelp(out, - 75, - "signZone.sh", - null, - opts, - HelpFormatter.DEFAULT_LEFT_PAD, - HelpFormatter.DEFAULT_DESC_PAD, - "\ntime/offset = YYYYMMDDHHmmss|+offset|\"now\"+offset\n"); + f.printHelp(out, 75, "signZone.sh", null, opts, + HelpFormatter.DEFAULT_LEFT_PAD, + HelpFormatter.DEFAULT_DESC_PAD, + "\ntime/offset = YYYYMMDDHHmmss|+offset|\"now\"+offset\n"); out.flush(); System.exit(64); @@ -369,8 +400,10 @@ public class SignZone /** * This is just a convenience method for parsing integers from strings. * - * @param s the string to parse. - * @param def the default value, if the string doesn't parse. + * @param s + * the string to parse. + * @param def + * the default value, if the string doesn't parse. * @return the parsed integer, or the default. */ private static int parseInt(String s, int def) @@ -389,13 +422,16 @@ public class SignZone /** * 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. + * @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 records, - List keypairs) + List keypairs) { boolean secure = true; @@ -433,15 +469,18 @@ public class SignZone /** * 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). + * @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 getKeys(String[] keyfiles, int start_index, - File inDirectory) throws IOException + File inDirectory) throws IOException { if (keyfiles == null) return null; @@ -477,13 +516,13 @@ public class SignZone /** * 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. + * @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 getKeysets(File inDirectory, Name zonename) throws IOException @@ -521,8 +560,8 @@ public class SignZone /** * Load a list of DNS names from a file. * - * @param nameListFile the path of a file containing a bare list of DNS - * names. + * @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 getNameList(File nameListFile) throws IOException @@ -557,8 +596,10 @@ public class SignZone /** * 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. + * @param start + * the start time to calculate offsets from. + * @param duration + * the time/offset string to parse. * @return the calculated time. */ private static Date convertDuration(Date start, String duration) @@ -587,9 +628,11 @@ public class SignZone /** * 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. + * @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 keypairs) @@ -600,306 +643,12 @@ public class SignZone { DnsKeyPair kp = (DnsKeyPair) i.next(); Name keyname = kp.getDNSKEYRecord().getName(); - if (!keyname.equals(zonename)) - { - return false; - } + if (!keyname.equals(zonename)) { return false; } } return true; } - /** - * Conditionally sign an RRset and add it to the toList. - * - * @param toList the list to which we are adding the processed RRsets. - * @param zonename the zone apex name. - * @param rrset the rrset under consideration. - * @param keysigningkeypairs the List of KSKs.. - * @param zonekeypairs the List of zone keys. - * @param start the RRSIG inception time. - * @param expire the RRSIG expiration time. - * @param fullySignKeyset if true, sign the zone apex keyset with both KSKs - * and ZSKs. - * @param last_cut the name of the last delegation point encountered. - * @return the name of the new last_cut. - */ - private static Name addRRset(JCEDnsSecSigner signer, List toList, - Name zonename, RRset rrset, List keysigningkeypairs, List zonekeypairs, - Date start, Date expire, boolean fullySignKeyset, Name last_cut) - throws IOException, GeneralSecurityException - { - // add the records themselves - for (Iterator i = rrset.rrs(); i.hasNext();) - { - toList.add(i.next()); - } - - int type = SignUtils.recordSecType(zonename, rrset.getName(), rrset - .getType(), last_cut); - - // we don't sign non-normal sets (delegations, glue, invalid). - // we also don't sign the zone key set unless we've been asked. - if (type == SignUtils.RR_DELEGATION) - { - return rrset.getName(); - } - if (type == SignUtils.RR_GLUE || type == SignUtils.RR_INVALID) - { - return last_cut; - } - - // check for the zone apex keyset. - if (rrset.getName().equals(zonename) && rrset.getType() == Type.DNSKEY) - { - // if we have key signing keys, sign the keyset with them, - // otherwise we will just sign them with the zonesigning keys. - if (keysigningkeypairs != null && keysigningkeypairs.size() > 0) - { - List sigs = signer - .signRRset(rrset, keysigningkeypairs, start, expire); - toList.addAll(sigs); - - // If we aren't going to sign with all the keys, bail out now. - if (!fullySignKeyset) return last_cut; - } - } - - // otherwise, we are OK to sign this set. - List sigs = signer.signRRset(rrset, zonekeypairs, start, expire); - toList.addAll(sigs); - - return last_cut; - } - - /** - * Given a zone, sign it. - * - * @param signer A signer (utility) object to use to actually sign stuff. - * @param zonename The name of the zone. - * @param records The records comprising the zone. They do not have to be in - * any particular order, as this method will order them as - * necessary. - * @param keysigningkeypairs The key pairs that are designated as "key - * signing keys". - * @param zonekeypair This key pairs that are designated as "zone signing - * keys". - * @param start The RRSIG inception time. - * @param expire The RRSIG expiration time. - * @param fullySignKeyset Sign the zone apex keyset with all available keys. - * @param digest_id The digest identifier to use when generating DS records. - * - * @return an ordered list of {@link org.xbill.DNS.Record} objects, - * representing the signed zone. - */ - private static List signZone(JCEDnsSecSigner signer, Name zonename, - List records, List keysigningkeypairs, List zonekeypairs, Date start, - Date expire, boolean fullySignKeyset, int digest_id) - throws IOException, GeneralSecurityException - { - - // Remove any existing DNSSEC records (NSEC, NSEC3, RRSIG) - SignUtils.removeGeneratedRecords(zonename, records); - - // Sort the zone - Collections.sort(records, new RecordComparator()); - - // Remove any duplicate records. - SignUtils.removeDuplicateRecords(records); - - // Generate DS records - SignUtils.generateDSRecords(zonename, records, digest_id); - - // Generate the NSEC records - SignUtils.generateNSECRecords(zonename, records); - - // Assemble into RRsets and sign. - RRset rrset = new RRset(); - ArrayList signed_records = new ArrayList(); - Name last_cut = null; - - for (ListIterator i = records.listIterator(); i.hasNext();) - { - Record r = (Record) i.next(); - - // First record - if (rrset.size() == 0) - { - rrset.addRR(r); - continue; - } - - // Current record is part of the current RRset. - if (rrset.getName().equals(r.getName()) - && rrset.getDClass() == r.getDClass() - && rrset.getType() == r.getType()) - { - rrset.addRR(r); - continue; - } - - // Otherwise, we have completed the RRset - // Sign the records - - // add the RRset to the list of signed_records, regardless of - // whether or not we actually end up signing the set. - last_cut = addRRset(signer, - signed_records, - zonename, - rrset, - keysigningkeypairs, - zonekeypairs, - start, - expire, - fullySignKeyset, - last_cut); - - rrset.clear(); - rrset.addRR(r); - } - - // add the last RR set - addRRset(signer, - signed_records, - zonename, - rrset, - keysigningkeypairs, - zonekeypairs, - start, - expire, - fullySignKeyset, - last_cut); - - return signed_records; - } - - /** - * Given a zone sign it using NSEC3 records. - * - * @param signer A signer (utility) object used to actually sign stuff. - * @param zonename The name of the zone being signed. - * @param records The records comprising the zone. They do not have to be in - * any particular order, as this method will order them as - * necessary. - * @param keysigningkeypairs The key pairs that are designated as "key - * signing keys". - * @param zonekeypairs This key pairs that are designated as "zone signing - * keys". - * @param start The RRSIG inception time. - * @param expire The RRSIG expiration time. - * @param fullySignKeyset If true then the DNSKEY RRset will be signed by - * all available keys, if false, only the key signing keys. - * @param useOptOut If true, insecure delegations will be omitted from the - * NSEC3 chain, and all NSEC3 records will have the Opt-Out flag - * set. - * @param includedNames A list of names to include in the NSEC3 chain - * regardless. - * @param salt The salt to use for the NSEC3 hashing. null means no salt. - * @param iterations The number of iterations to use for the NSEC3 hashing. - * @param ds_digest_id The digest algorithm to use when generating DS - * records. - * - * @return an ordered list of {@link org.xbill.DNS.Record} objects, - * representing the signed zone. - * - * @throws IOException - * @throws GeneralSecurityException - */ - private static List signZoneNSEC3(JCEDnsSecSigner signer, Name zonename, - List records, List keysigningkeypairs, List zonekeypairs, Date start, - Date expire, boolean fullySignKeyset, boolean useOptOut, - List includedNames, byte[] salt, int iterations, int ds_digest_id) - throws IOException, GeneralSecurityException - { - // Remove any existing DNSSEC records (NSEC, NSEC3, NSEC3PARAM, RRSIG) - SignUtils.removeGeneratedRecords(zonename, records); - - // Sort the zone - Collections.sort(records, new RecordComparator()); - - // Remove duplicate records - SignUtils.removeDuplicateRecords(records); - - // Generate DS records - SignUtils.generateDSRecords(zonename, records, ds_digest_id); - - // Generate NSEC3 records - if (useOptOut) - { - SignUtils.generateOptOutNSEC3Records(zonename, - records, - includedNames, - salt, - iterations); - } - else - { - SignUtils.generateNSEC3Records(zonename, records, salt, iterations); - } - - // Re-sort so we can assemble into rrsets. - Collections.sort(records, new RecordComparator()); - - // Assemble into RRsets and sign. - RRset rrset = new RRset(); - ArrayList signed_records = new ArrayList(); - Name last_cut = null; - - for (ListIterator i = records.listIterator(); i.hasNext();) - { - Record r = (Record) i.next(); - - // First record - if (rrset.size() == 0) - { - rrset.addRR(r); - continue; - } - - // Current record is part of the current RRset. - if (rrset.getName().equals(r.getName()) - && rrset.getDClass() == r.getDClass() - && rrset.getType() == r.getType()) - { - rrset.addRR(r); - continue; - } - - // Otherwise, we have completed the RRset - // Sign the records - - // add the RRset to the list of signed_records, regardless of - // whether or not we actually end up signing the set. - last_cut = addRRset(signer, - signed_records, - zonename, - rrset, - keysigningkeypairs, - zonekeypairs, - start, - expire, - fullySignKeyset, - last_cut); - - rrset.clear(); - rrset.addRR(r); - } - - // add the last RR set - addRRset(signer, - signed_records, - zonename, - rrset, - keysigningkeypairs, - zonekeypairs, - start, - expire, - fullySignKeyset, - last_cut); - - return signed_records; - } - public static void execute(CLIState state) throws Exception { // Load the key pairs. @@ -1014,31 +763,19 @@ public class SignZone if (state.useNsec3) { - signed_records = signZoneNSEC3(signer, - zonename, - records, - kskpairs, - keypairs, - state.start, - state.expire, - state.fullySignKeyset, - state.useOptOut, - state.includeNames, - state.salt, - state.iterations, - state.digest_id); + 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); } else { - signed_records = signZone(signer, - zonename, - records, - kskpairs, - keypairs, - state.start, - state.expire, - state.fullySignKeyset, - state.digest_id); + signed_records = signer.signZone(zonename, records, kskpairs, keypairs, + state.start, state.expire, + state.fullySignKeyset, state.digest_id); } // write out the signed zone @@ -1080,8 +817,7 @@ public class SignZone } catch (UnrecognizedOptionException e) { - System.err.println("error: unknown option encountered: " - + e.getMessage()); + System.err.println("error: unknown option encountered: " + e.getMessage()); state.usage(); } catch (AlreadySelectedException e) diff --git a/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java b/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java index e26e1fe..124ab5c 100644 --- a/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java +++ b/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java @@ -1,6 +1,6 @@ // $Id$ // -// Copyright (C) 2001-2003 VeriSign, Inc. +// Copyright (C) 2001-2003, 2009 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -43,13 +43,14 @@ import org.xbill.DNS.Type; * This class contains routines for signing DNS zones. * * In particular, it contains both an ability to sign an individual RRset and - * the ability to sign and entire zone. It primarily glues together the more + * the ability to sign an entire zone. It primarily glues together the more * basic primitives found in {@link SignUtils}. * * @author David Blacka (original) * @author $Author$ * @version $Revision$ */ + public class JCEDnsSecSigner { private DnsKeyConverter mKeyConverter; @@ -57,36 +58,43 @@ public class JCEDnsSecSigner /** * Cryptographically generate a new DNSSEC key. * - * @param owner the KEY RR's owner name. - * @param ttl the KEY RR's TTL. - * @param dclass the KEY RR's DNS class. - * @param algorithm the DNSSEC algorithm (RSAMD5, RSASHA1, or DSA). - * @param flags any flags for the KEY RR. - * @param keysize the size of the key to generate. - * @param useLargeExponent if generating an RSA key, use the large exponent. + * @param owner + * the KEY RR's owner name. + * @param ttl + * the KEY RR's TTL. + * @param dclass + * the KEY RR's DNS class. + * @param algorithm + * the DNSSEC algorithm (RSAMD5, RSASHA1, or DSA). + * @param flags + * any flags for the KEY RR. + * @param keysize + * the size of the key to generate. + * @param useLargeExponent + * if generating an RSA key, use the large exponent. * @return a DnsKeyPair with the public and private keys populated. */ public DnsKeyPair generateKey(Name owner, long ttl, int dclass, - int algorithm, int flags, int keysize, boolean useLargeExponent) + int algorithm, int flags, int keysize, + boolean useLargeExponent) throws NoSuchAlgorithmException { DnsKeyAlgorithm algorithms = DnsKeyAlgorithm.getInstance(); if (ttl < 0) ttl = 86400; // set to a reasonable default. - KeyPair pair = algorithms.generateKeyPair(algorithm, keysize, useLargeExponent); + KeyPair pair = algorithms.generateKeyPair(algorithm, keysize, + useLargeExponent); if (mKeyConverter == null) { mKeyConverter = new DnsKeyConverter(); } - DNSKEYRecord keyrec = mKeyConverter.generateDNSKEYRecord(owner, - dclass, - ttl, - flags, - algorithm, - pair.getPublic()); + DNSKEYRecord keyrec = mKeyConverter.generateDNSKEYRecord(owner, dclass, + ttl, flags, + algorithm, + pair.getPublic()); DnsKeyPair dnspair = new DnsKeyPair(); dnspair.setDNSKEYRecord(keyrec); @@ -99,10 +107,14 @@ public class JCEDnsSecSigner /** * Sign an RRset. * - * @param rrset the RRset to sign -- any existing signatures are ignored. - * @param keypars a list of DnsKeyPair objects containing private keys. - * @param start the inception time for the resulting RRSIG records. - * @param expire the expiration time for the resulting RRSIG records. + * @param rrset + * the RRset to sign -- any existing signatures are ignored. + * @param keypars + * a list of DnsKeyPair objects containing private keys. + * @param start + * the inception time for the resulting RRSIG records. + * @param expire + * the expiration time for the resulting RRSIG records. * @return a list of RRSIGRecord objects. */ public List signRRset(RRset rrset, List keypairs, Date start, Date expire) @@ -115,23 +127,20 @@ public class JCEDnsSecSigner if (expire == null) expire = new Date(start.getTime() + 1000L); if (keypairs.size() == 0) return null; - // first, pre-calculate the rrset bytes. + // first, pre-calculate the RRset bytes. byte[] rrset_data = SignUtils.generateCanonicalRRsetData(rrset); ArrayList sigs = new ArrayList(keypairs.size()); - // for each keypair, sign the rrset. + // for each keypair, sign the RRset. for (Iterator i = keypairs.iterator(); i.hasNext();) { DnsKeyPair pair = (DnsKeyPair) i.next(); DNSKEYRecord keyrec = pair.getDNSKEYRecord(); if (keyrec == null) continue; - RRSIGRecord presig = SignUtils.generatePreRRSIG(rrset, - keyrec, - start, - expire, - rrset.getTTL()); + RRSIGRecord presig = SignUtils.generatePreRRSIG(rrset, keyrec, start, + expire, rrset.getTTL()); byte[] sign_data = SignUtils.generateSigData(rrset_data, presig); Signature signer = pair.getSigner(); @@ -142,8 +151,7 @@ public class JCEDnsSecSigner System.out.println("missing private key that goes with:\n" + pair.getDNSKEYRecord()); throw new GeneralSecurityException( - "cannot sign without a valid Signer " - + "(probably missing private key)"); + "cannot sign without a valid Signer (probably missing private key)"); } // sign the data. @@ -154,8 +162,9 @@ public class JCEDnsSecSigner // Convert to RFC 2536 format, if necessary. if (algs.baseType(pair.getDNSKEYAlgorithm()) == DnsKeyAlgorithm.DSA) { - sig = SignUtils.convertDSASignature(((DSAPublicKey) pair.getPublic()) - .getParams(), sig); + sig = SignUtils.convertDSASignature( + ((DSAPublicKey) pair.getPublic()).getParams(), + sig); } RRSIGRecord sigrec = SignUtils.generateRRSIG(sig, presig); sigs.add(sigrec); @@ -165,11 +174,14 @@ public class JCEDnsSecSigner } /** - * Create a completely self-signed KEY RRset. + * Create a completely self-signed DNSKEY RRset. * - * @param keypairs the public & private keypairs to use in the keyset. - * @param start the RRSIG inception time. - * @param expire the RRSIG expiration time. + * @param keypairs + * the public & private keypairs to use in the keyset. + * @param start + * the RRSIG inception time. + * @param expire + * the RRSIG expiration time. * @return a signed RRset. */ public RRset makeKeySet(List keypairs, Date start, Date expire) @@ -198,21 +210,30 @@ public class JCEDnsSecSigner /** * Conditionally sign an RRset and add it to the toList. * - * @param toList the list to which we are adding the processed RRsets. - * @param zonename the zone apex name. - * @param rrset the rrset under consideration. - * @param keysigningkeypairs the List of KSKs.. - * @param zonekeypairs the List of zone keys. - * @param start the RRSIG inception time. - * @param expire the RRSIG expiration time. - * @param fullySignKeyset if true, sign the zone apex keyset with both KSKs - * and ZSKs. - * @param last_cut the name of the last delegation point encountered. + * @param toList + * the list to which we are adding the processed RRsets. + * @param zonename + * the zone apex name. + * @param rrset + * the RRset under consideration. + * @param kskpairs + * the List of KSKs.. + * @param zskpairs + * the List of zone keys. + * @param start + * the RRSIG inception time. + * @param expire + * the RRSIG expiration time. + * @param fullySignKeyset + * if true, sign the zone apex keyset with both KSKs and ZSKs. + * @param last_cut + * the name of the last delegation point encountered. + * * @return the name of the new last_cut. */ - private Name addRRset(List toList, Name zonename, RRset rrset, - List keysigningkeypairs, List zonekeypairs, Date start, Date expire, - boolean fullySignKeyset, Name last_cut) + private Name addRRset(List toList, Name zonename, RRset rrset, List kskpairs, + List zskpairs, Date start, Date expire, + boolean fullySignKeyset, Name last_cut) throws IOException, GeneralSecurityException { // add the records themselves @@ -221,28 +242,21 @@ public class JCEDnsSecSigner toList.add(i.next()); } - int type = SignUtils.recordSecType(zonename, rrset.getName(), rrset - .getType(), last_cut); + int type = SignUtils.recordSecType(zonename, rrset.getName(), + rrset.getType(), last_cut); // we don't sign non-normal sets (delegations, glue, invalid). - // we also don't sign the zone key set unless we've been asked. - if (type == SignUtils.RR_DELEGATION) - { - return rrset.getName(); - } - if (type == SignUtils.RR_GLUE || type == SignUtils.RR_INVALID) - { - return last_cut; - } + if (type == SignUtils.RR_DELEGATION) { return rrset.getName(); } + if (type == SignUtils.RR_GLUE || type == SignUtils.RR_INVALID) { return last_cut; } // check for the zone apex keyset. if (rrset.getName().equals(zonename) && rrset.getType() == Type.DNSKEY) { - // if we have key signing keys, sign the keyset with them, - // otherwise we will just sign them with the zonesigning keys. - if (keysigningkeypairs != null && keysigningkeypairs.size() > 0) + // if we have ksks, sign the keyset with them, otherwise we will just sign + // them with the zsks. + if (kskpairs != null && kskpairs.size() > 0) { - List sigs = signRRset(rrset, keysigningkeypairs, start, expire); + List sigs = signRRset(rrset, kskpairs, start, expire); toList.addAll(sigs); // If we aren't going to sign with all the keys, bail out now. @@ -251,70 +265,108 @@ public class JCEDnsSecSigner } // otherwise, we are OK to sign this set. - List sigs = signRRset(rrset, zonekeypairs, start, expire); + List sigs = signRRset(rrset, zskpairs, start, expire); toList.addAll(sigs); return last_cut; } + // Various NSEC/NSEC3 generation modes + private static final int NSEC_MODE = 0; + private static final int NSEC3_MODE = 1; + private static final int NSEC3_OPTOUT_MODE = 2; + private static final int NSEC_EXP_OPT_IN = 3; + /** - * Given a zone, sign it. + * Master zone signing method. This method handles all of the different zone + * signing variants (NSEC with or without Opt-In, NSEC3 with or without + * Opt-Out, etc.) External users of this class are expected to use the + * appropriate public signZone* methods instead of this. + * + * @param zonename + * The name of the zone + * @param records + * The records comprising the zone. They do not have to be in any + * particular order, as this method will order them as necessary. + * @param kskpairs + * The key pairs designated as "key signing keys" + * @param zskpairs + * The key pairs designated as "zone signing keys" + * @param start + * The RRSIG inception time + * @param expire + * The RRSIG expiration time + * @param fullySignKeyset + * If true, all keys (ksk or zsk) will sign the DNSKEY RRset. If + * false, only the ksks will sign it. + * @param ds_digest_alg + * The hash algorithm to use for generating DS records + * (DSRecord.SHA1_DIGEST_ID, e.g.) + * @param mode + * The NSEC/NSEC3 generation mode: NSEC_MODE, NSEC3_MODE, + * NSEC3_OPTOUT_MODE, etc. + * @param includedNames + * When using an Opt-In/Opt-Out mode, the names listed here will be + * included in the NSEC/NSEC3 chain regardless + * @param salt + * When using an NSEC3 mode, use this salt. + * @param iterations + * When using an NSEC3 mode, use this number of iterations + * @param beConservative + * If true, then only turn on the Opt-In flag when there are insecure + * delegations in the span. Currently this only works for + * NSEC_EXP_OPT_IN mode. * - * @param zonename the name of the zone. - * @param records the records comprising the zone. They do not have to be in - * any particular order, as this method will order them as - * necessary. - * @param keysigningkeypairs the key pairs that are designated as "key - * signing keys". - * @param start the RRSIG inception time. - * @param expire the RRSIG expiration time. - * @param useOptIn generate Opt-In style NXT records. It will consider any - * insecure delegation to be unsigned. To override this, include - * the name of the insecure delegation in the NXTIncludeNames list. - * @param useConservativeOptIn if true, Opt-In NXT records will only be - * generated if there are insecure, unsigned delegations in the - * span. Not effect if useOptIn is false. - * @param fullySignKeyset sign the zone apex keyset with all available keys. - * @param ds_digest_id TODO - * @param zonekeypair this key pairs that are designated as "zone signing - * keys". - * @param NXTIncludeNames names that are to be included in the NXT chain - * regardless. This may be null and is only used if useOptIn is - * true. * @return an ordered list of {@link org.xbill.DNS.Record} objects, * representing the signed zone. + * + * @throws IOException + * @throws GeneralSecurityException */ - public List signZone(Name zonename, List records, List keysigningkeypairs, - List zonekeypairs, Date start, Date expire, boolean useOptIn, - boolean useConservativeOptIn, boolean fullySignKeyset, - List NSECIncludeNames, int ds_digest_id) + private List signZone(Name zonename, List records, List kskpairs, + List zskpairs, Date start, Date expire, + boolean fullySignKeyset, int ds_digest_alg, int mode, + List includedNames, byte[] salt, int iterations, + boolean beConservative) throws IOException, GeneralSecurityException { - - // Remove any existing DNSSEC records (NSEC, RRSIG) + // Remove any existing generated DNSSEC records (NSEC, NSEC3, NSEC3PARAM, + // RRSIG) SignUtils.removeGeneratedRecords(zonename, records); - // Sort the zone - Collections.sort(records, new RecordComparator()); - // Remove any duplicate records. + RecordComparator rc = new RecordComparator(); + // Sort the zone + Collections.sort(records, rc); + + // Remove duplicate records SignUtils.removeDuplicateRecords(records); - // Generate DS records - SignUtils.generateDSRecords(zonename, records, ds_digest_id); + // Generate DS records. This replaces any non-zone-apex DNSKEY RRs with DS + // RRs. + SignUtils.generateDSRecords(zonename, records, ds_digest_alg); - // Generate NXT records - if (useOptIn) - { - SignUtils.generateOptInNSECRecords(zonename, - records, - NSECIncludeNames, - useConservativeOptIn); - } - else + // Generate the NSEC or NSEC3 records based on 'mode' + switch (mode) { + case NSEC_MODE: SignUtils.generateNSECRecords(zonename, records); + break; + case NSEC3_MODE: + SignUtils.generateNSEC3Records(zonename, records, salt, iterations); + break; + case NSEC3_OPTOUT_MODE: + SignUtils.generateOptOutNSEC3Records(zonename, records, includedNames, + salt, iterations); + break; + case NSEC_EXP_OPT_IN: + SignUtils.generateOptInNSECRecords(zonename, records, includedNames, + beConservative); + break; } + // Re-sort so we can assemble into rrsets. + Collections.sort(records, rc); + // Assemble into RRsets and sign. RRset rrset = new RRset(); ArrayList signed_records = new ArrayList(); @@ -325,7 +377,7 @@ public class JCEDnsSecSigner Record r = (Record) i.next(); // First record - if (rrset.getName() == null) + if (rrset.size() == 0) { rrset.addRR(r); continue; @@ -345,31 +397,155 @@ public class JCEDnsSecSigner // add the RRset to the list of signed_records, regardless of // whether or not we actually end up signing the set. - last_cut = addRRset(signed_records, - zonename, - rrset, - keysigningkeypairs, - zonekeypairs, - start, - expire, - fullySignKeyset, - last_cut); + last_cut = addRRset(signed_records, zonename, rrset, kskpairs, zskpairs, + start, expire, fullySignKeyset, last_cut); rrset.clear(); rrset.addRR(r); } // add the last RR set - addRRset(signed_records, - zonename, - rrset, - keysigningkeypairs, - zonekeypairs, - start, - expire, - fullySignKeyset, - last_cut); + addRRset(signed_records, zonename, rrset, kskpairs, zskpairs, start, + expire, fullySignKeyset, last_cut); return signed_records; } + + /** + * Given a zone, sign it using standard NSEC records. + * + * @param zonename + * The name of the zone. + * @param records + * The records comprising the zone. They do not have to be in any + * particular order, as this method will order them as necessary. + * @param kskpairs + * The key pairs that are designated as "key signing keys". + * @param zskpairs + * This key pairs that are designated as "zone signing keys". + * @param start + * The RRSIG inception time. + * @param expire + * The RRSIG expiration time. + * @param fullySignKeyset + * Sign the zone apex keyset with all available keys (instead of just + * the key signing keys). + * @param ds_digest_alg + * The digest algorithm to use when generating DS records. + * + * @return an ordered list of {@link org.xbill.DNS.Record} objects, + * representing the signed zone. + */ + public List signZone(Name zonename, List records, List kskpairs, + List zskpairs, Date start, Date expire, + boolean fullySignKeyset, int ds_digest_alg) + throws IOException, GeneralSecurityException + { + return signZone(zonename, records, kskpairs, zskpairs, start, expire, + fullySignKeyset, ds_digest_alg, NSEC_MODE, null, null, 0, + false); + } + + /** + * Given a zone, sign it using NSEC3 records. + * + * @param signer + * A signer (utility) object used to actually sign stuff. + * @param zonename + * The name of the zone being signed. + * @param records + * The records comprising the zone. They do not have to be in any + * particular order, as this method will order them as necessary. + * @param kskpairs + * The key pairs that are designated as "key signing keys". + * @param zskpairs + * This key pairs that are designated as "zone signing keys". + * @param start + * The RRSIG inception time. + * @param expire + * The RRSIG expiration time. + * @param fullySignKeyset + * If true then the DNSKEY RRset will be signed by all available + * keys, if false, only the key signing keys. + * @param useOptOut + * If true, insecure delegations will be omitted from the NSEC3 + * chain, and all NSEC3 records will have the Opt-Out flag set. + * @param includedNames + * A list of names to include in the NSEC3 chain regardless. + * @param salt + * The salt to use for the NSEC3 hashing. null means no salt. + * @param iterations + * The number of iterations to use for the NSEC3 hashing. + * @param ds_digest_alg + * The digest algorithm to use when generating DS records. + * + * @return an ordered list of {@link org.xbill.DNS.Record} objects, + * representing the signed zone. + * + * @throws IOException + * @throws GeneralSecurityException + */ + public List signZoneNSEC3(Name zonename, List records, List kskpairs, + List zskpairs, Date start, Date expire, + boolean fullySignKeyset, boolean useOptOut, + List includedNames, byte[] salt, int iterations, + int ds_digest_alg) + throws IOException, GeneralSecurityException + { + if (useOptOut) + { + return signZone(zonename, records, kskpairs, zskpairs, start, expire, + fullySignKeyset, ds_digest_alg, NSEC3_OPTOUT_MODE, + includedNames, salt, iterations, false); + } + else + { + return signZone(zonename, records, kskpairs, zskpairs, start, expire, + fullySignKeyset, ds_digest_alg, NSEC3_MODE, null, salt, + iterations, false); + } + } + + /** + * Given a zone, sign it using experimental Opt-In NSEC records (see RFC + * 4956). + * + * @param zonename + * the name of the zone. + * @param records + * the records comprising the zone. They do not have to be in any + * particular order, as this method will order them as necessary. + * @param kskpairs + * the key pairs that are designated as "key signing keys". + * @param zskpairs + * this key pairs that are designated as "zone signing keys". + * @param start + * the RRSIG inception time. + * @param expire + * the RRSIG expiration time. + * @param useConservativeOptIn + * if true, Opt-In NSEC records will only be generated if there are + * insecure, unsigned delegations in the span. + * @param fullySignKeyset + * sign the zone apex keyset with all available keys. + * @param ds_digest_alg + * The digest algorithm to use when generating DS records. + * @param NSECIncludeNames + * names that are to be included in the NSEC chain regardless. This + * may be null. + * @return an ordered list of {@link org.xbill.DNS.Record} objects, + * representing the signed zone. + */ + public List signZoneOptIn(Name zonename, List records, List kskpairs, + List zskpairs, Date start, Date expire, + boolean useConservativeOptIn, + boolean fullySignKeyset, List NSECIncludeNames, + int ds_digest_alg) + throws IOException, GeneralSecurityException + { + + return signZone(zonename, records, kskpairs, zskpairs, start, expire, + fullySignKeyset, ds_digest_alg, NSEC_EXP_OPT_IN, + NSECIncludeNames, null, 0, useConservativeOptIn); + } }