NSEC3 support, remove plain opt-in support until private algs work
git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@35 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e
This commit is contained in:
parent
ab479a3e7b
commit
04ab26f434
Binary file not shown.
@ -28,13 +28,8 @@ import java.io.PrintWriter;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.TimeZone;
|
||||
import java.util.*;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ -47,6 +42,7 @@ import org.xbill.DNS.RRset;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.TextParseException;
|
||||
import org.xbill.DNS.Type;
|
||||
import org.xbill.DNS.utils.base16;
|
||||
|
||||
import com.verisignlabs.dnssec.security.BINDKeyUtils;
|
||||
import com.verisignlabs.dnssec.security.DnsKeyPair;
|
||||
@ -68,8 +64,7 @@ public class SignZone
|
||||
private static Logger log;
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
* This is an inner class used to hold all of the command line option state.
|
||||
*/
|
||||
private static class CLIState
|
||||
{
|
||||
@ -85,9 +80,11 @@ public class SignZone
|
||||
public boolean verifySigs = false;
|
||||
public boolean selfSignKeys = true;
|
||||
public boolean useOptIn = false;
|
||||
public boolean optInConserve = false;
|
||||
public boolean fullySignKeyset = false;
|
||||
public List includeNames = null;
|
||||
public boolean useNsec3 = false;
|
||||
public byte[] salt = null;
|
||||
public int iterations = 0;
|
||||
|
||||
public CLIState()
|
||||
{
|
||||
@ -115,23 +112,25 @@ public class SignZone
|
||||
case 0 :
|
||||
rootLogger.setLevel(Level.OFF);
|
||||
break;
|
||||
case 5 :
|
||||
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++)
|
||||
handlers[i].setLevel(rootLogger.getLevel());
|
||||
}
|
||||
|
||||
if (cli.hasOption('a')) verifySigs = true;
|
||||
if (cli.hasOption('3')) useNsec3 = true;
|
||||
if (cli.hasOption('O')) useOptIn = true;
|
||||
if (cli.hasOption('C'))
|
||||
{
|
||||
useOptIn = true;
|
||||
optInConserve = true;
|
||||
}
|
||||
|
||||
if (cli.hasOption('F')) fullySignKeyset = true;
|
||||
|
||||
@ -185,6 +184,21 @@ public class SignZone
|
||||
includeNames = getNameList(includeNamesFile);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("salt")) != null)
|
||||
{
|
||||
salt = base16.fromString(optstr);
|
||||
}
|
||||
if ((optstr = cli.getOptionValue("random-salt")) != null)
|
||||
{
|
||||
int length = parseInt(optstr, 0);
|
||||
if (length > 0 && length <= 255)
|
||||
{
|
||||
Random random = new Random();
|
||||
salt = new byte[length];
|
||||
random.nextBytes(salt);
|
||||
}
|
||||
}
|
||||
|
||||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 2)
|
||||
@ -209,73 +223,60 @@ public class SignZone
|
||||
|
||||
// boolean options
|
||||
opts.addOption("h", "help", false, "Print this message.");
|
||||
opts.addOption("a", false, "verify generated signatures>");
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
opts.addOption("F",
|
||||
"fully-sign-keyset",
|
||||
false,
|
||||
"sign the zone apex keyset with all "
|
||||
+ "available keys, instead of just key-signing-keys.");
|
||||
|
||||
// Opt-In generation switches
|
||||
OptionGroup opt_in_opts = new OptionGroup();
|
||||
opt_in_opts.addOption(new Option("O", "generate a fully Opt-In zone."));
|
||||
opt_in_opts.addOption(new Option("C",
|
||||
"generate a conservative Opt-In zone."));
|
||||
opts.addOptionGroup(opt_in_opts);
|
||||
"sign the zone apex keyset with all available keys.");
|
||||
|
||||
// Argument options
|
||||
OptionBuilder.hasOptionalArg();
|
||||
OptionBuilder.withLongOpt("verbose");
|
||||
OptionBuilder.withArgName("level");
|
||||
OptionBuilder.withDescription("verbosity level -- 0 is silence, "
|
||||
+ "5 is debug information, " + "6 is trace information. "
|
||||
+ "No argument means 5.");
|
||||
opts.addOption(OptionBuilder.create('v'));
|
||||
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.hasArgs().withArgName("KSK file")
|
||||
.withLongOpt("ksk-file").withDescription("this key is a key "
|
||||
+ "signing key (may repeat).").create('k'));
|
||||
opts.addOption(OptionBuilder.hasArg().withArgName("file")
|
||||
.withLongOpt("include-file")
|
||||
.withDescription("include names in this "
|
||||
+ "file in the NSEC/NSEC3 chain.").create('I'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("dir");
|
||||
OptionBuilder.withLongOpt("keyset-directory");
|
||||
OptionBuilder.withDescription("directory to find keyset files (default '.').");
|
||||
opts.addOption(OptionBuilder.create('d'));
|
||||
// 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.");
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("dir");
|
||||
OptionBuilder.withLongOpt("key-directory");
|
||||
OptionBuilder.withDescription("directory to find key files (default '.').");
|
||||
opts.addOption(OptionBuilder.create('D'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("start-time");
|
||||
OptionBuilder.withDescription("signature starting time (default is now - 1 hour)");
|
||||
opts.addOption(OptionBuilder.create('s'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("time/offset");
|
||||
OptionBuilder.withLongOpt("expire-time");
|
||||
OptionBuilder.withDescription("signature expiration time (default is "
|
||||
+ "start-time + 30 days");
|
||||
opts.addOption(OptionBuilder.create('e'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("outfile");
|
||||
OptionBuilder.withDescription("file the signed zone is written "
|
||||
+ "to (default is <origin>.signed).");
|
||||
opts.addOption(OptionBuilder.create('f'));
|
||||
|
||||
OptionBuilder.hasArgs();
|
||||
OptionBuilder.withArgName("KSK file");
|
||||
OptionBuilder.withLongOpt("ksk-file");
|
||||
OptionBuilder.withDescription("this key is a key signing key "
|
||||
+ "(may repeat)");
|
||||
opts.addOption(OptionBuilder.create('k'));
|
||||
|
||||
OptionBuilder.hasArg();
|
||||
OptionBuilder.withArgName("file");
|
||||
OptionBuilder.withLongOpt("include-file");
|
||||
OptionBuilder.withDescription("include names in this file "
|
||||
+ "in the NSEC chain");
|
||||
opts.addOption(OptionBuilder.create('I'));
|
||||
opts.addOption(OptionBuilder.hasArg().withLongOpt("salt")
|
||||
.withArgName("hex value").withDescription("supply a salt value.")
|
||||
.create());
|
||||
opts.addOption(OptionBuilder.hasArg().withLongOpt("random-salt")
|
||||
.withArgName("length").withDescription("generate a random salt.")
|
||||
.create());
|
||||
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. */
|
||||
@ -286,7 +287,8 @@ public class SignZone
|
||||
PrintWriter out = new PrintWriter(System.err);
|
||||
|
||||
// print our own usage statement:
|
||||
out.println("usage: signZone.sh [..options..] zone_file [key_file ...] ");
|
||||
out.println("usage: signZone.sh [..options..] "
|
||||
+ "zone_file [key_file ...] ");
|
||||
f.printHelp(out,
|
||||
75,
|
||||
"signZone.sh",
|
||||
@ -572,10 +574,8 @@ public class SignZone
|
||||
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.
|
||||
@ -595,7 +595,8 @@ public class SignZone
|
||||
// otherwise we will just sign them with the zonesigning keys.
|
||||
if (keysigningkeypairs != null && keysigningkeypairs.size() > 0)
|
||||
{
|
||||
List sigs = signer.signRRset(rrset, keysigningkeypairs, start, expire);
|
||||
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.
|
||||
@ -639,13 +640,13 @@ public class SignZone
|
||||
*/
|
||||
private static List signZone(JCEDnsSecSigner signer, Name zonename,
|
||||
List records, List keysigningkeypairs, List zonekeypairs, Date start,
|
||||
Date expire, boolean useOptIn, boolean useConservativeOptIn,
|
||||
boolean fullySignKeyset, List NSECIncludeNames) throws IOException,
|
||||
GeneralSecurityException
|
||||
Date expire, boolean fullySignKeyset)
|
||||
throws IOException, GeneralSecurityException
|
||||
{
|
||||
|
||||
// Remove any existing DNSSEC records (NSEC, RRSIG)
|
||||
// Remove any existing DNSSEC records (NSEC, NSEC3, RRSIG)
|
||||
SignUtils.removeGeneratedRecords(zonename, records);
|
||||
|
||||
// Sort the zone
|
||||
Collections.sort(records, new RecordComparator());
|
||||
|
||||
@ -655,18 +656,8 @@ public class SignZone
|
||||
// Generate DS records
|
||||
SignUtils.generateDSRecords(zonename, records);
|
||||
|
||||
// Generate NXT records
|
||||
if (useOptIn)
|
||||
{
|
||||
SignUtils.generateOptInNSECRecords(zonename,
|
||||
records,
|
||||
NSECIncludeNames,
|
||||
useConservativeOptIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
SignUtils.generateNSECRecords(zonename, records);
|
||||
}
|
||||
// Generate the NSEC records
|
||||
SignUtils.generateNSECRecords(zonename, records);
|
||||
|
||||
// Assemble into RRsets and sign.
|
||||
RRset rrset = new RRset();
|
||||
@ -678,7 +669,102 @@ public class SignZone
|
||||
Record r = (Record) i.next();
|
||||
|
||||
// First record
|
||||
if (rrset.getName() == null)
|
||||
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;
|
||||
}
|
||||
|
||||
private static List signZoneNSEC3(JCEDnsSecSigner signer, Name zonename,
|
||||
List records, List keysigningkeypairs, List zonekeypairs, Date start,
|
||||
Date expire, boolean fullySignKeyset, boolean useOptIn,
|
||||
List includedNames, byte[] salt, int iterations)
|
||||
throws IOException, GeneralSecurityException
|
||||
{
|
||||
// Remove any existing DNSSEC records (NSEC, NSEC3, 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);
|
||||
|
||||
// Generate NSEC3 records
|
||||
if (useOptIn)
|
||||
{
|
||||
SignUtils.generateOptInNSEC3Records(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;
|
||||
@ -823,17 +909,34 @@ public class SignZone
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||
|
||||
// Sign the zone.
|
||||
List signed_records = signZone(signer,
|
||||
zonename,
|
||||
records,
|
||||
kskpairs,
|
||||
keypairs,
|
||||
state.start,
|
||||
state.expire,
|
||||
state.useOptIn,
|
||||
state.optInConserve,
|
||||
state.fullySignKeyset,
|
||||
state.includeNames);
|
||||
List signed_records;
|
||||
|
||||
if (state.useNsec3)
|
||||
{
|
||||
signed_records = signZoneNSEC3(signer,
|
||||
zonename,
|
||||
records,
|
||||
kskpairs,
|
||||
keypairs,
|
||||
state.start,
|
||||
state.expire,
|
||||
state.fullySignKeyset,
|
||||
state.useOptIn,
|
||||
state.includeNames,
|
||||
state.salt,
|
||||
state.iterations);
|
||||
}
|
||||
else
|
||||
{
|
||||
signed_records = signZone(signer,
|
||||
zonename,
|
||||
records,
|
||||
kskpairs,
|
||||
keypairs,
|
||||
state.start,
|
||||
state.expire,
|
||||
state.fullySignKeyset);
|
||||
}
|
||||
|
||||
// write out the signed zone
|
||||
// force multiline mode for now
|
||||
|
245
src/com/verisignlabs/dnssec/security/ProtoNSEC3.java
Normal file
245
src/com/verisignlabs/dnssec/security/ProtoNSEC3.java
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2005 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 org.xbill.DNS.*;
|
||||
import org.xbill.DNS.utils.base16;
|
||||
import org.xbill.DNS.utils.base32;
|
||||
|
||||
public class ProtoNSEC3
|
||||
{
|
||||
private boolean optInFlag;
|
||||
private byte hashAlg;
|
||||
private int iterations;
|
||||
private byte[] salt;
|
||||
private byte[] next;
|
||||
private byte[] owner; // cached numerical owner value.
|
||||
private TypeMap typemap;
|
||||
private Name zone;
|
||||
private Name name;
|
||||
private int dclass;
|
||||
private long ttl;
|
||||
|
||||
/**
|
||||
* Creates an NSEC3 Record from the given data.
|
||||
*
|
||||
* @param next The following name in an ordered list of the zone
|
||||
* @param types An array containing the types present.
|
||||
*/
|
||||
public ProtoNSEC3(byte[] owner, Name zone, int dclass, long ttl,
|
||||
boolean optInFlag, byte hashAlg, int iterations, byte[] salt,
|
||||
byte[] next, TypeMap typemap)
|
||||
{
|
||||
this.zone = zone;
|
||||
this.owner = owner;
|
||||
this.dclass = dclass;
|
||||
this.ttl = ttl;
|
||||
this.optInFlag = optInFlag;
|
||||
this.hashAlg = hashAlg;
|
||||
this.iterations = iterations;
|
||||
this.salt = salt;
|
||||
this.next = next;
|
||||
this.typemap = typemap;
|
||||
}
|
||||
|
||||
public ProtoNSEC3(byte[] owner, Name zone, int dclass, long ttl,
|
||||
boolean optInFlag, byte hashAlg, int iterations, byte[] salt,
|
||||
byte[] next, int[] types)
|
||||
{
|
||||
this(owner, zone, dclass, ttl, optInFlag, hashAlg, iterations, salt,
|
||||
next, TypeMap.fromTypes(types));
|
||||
}
|
||||
|
||||
private String hashToString(byte[] hash)
|
||||
{
|
||||
if (hash == null) return null;
|
||||
return base32.toString(hash).toLowerCase();
|
||||
}
|
||||
|
||||
public Name getName()
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
name = new Name(hashToString(owner), zone);
|
||||
}
|
||||
catch (TextParseException e)
|
||||
{
|
||||
// This isn't going to happen.
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public byte[] getNext()
|
||||
{
|
||||
return next;
|
||||
}
|
||||
|
||||
public void setNext(byte[] next)
|
||||
{
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public boolean getOptInFlag()
|
||||
{
|
||||
return optInFlag;
|
||||
}
|
||||
|
||||
public void setOptInFlag(boolean optInFlag)
|
||||
{
|
||||
this.optInFlag = optInFlag;
|
||||
}
|
||||
|
||||
public long getTTL()
|
||||
{
|
||||
return ttl;
|
||||
}
|
||||
|
||||
public void setTTL(long ttl)
|
||||
{
|
||||
this.ttl = ttl;
|
||||
}
|
||||
|
||||
public TypeMap getTypemap()
|
||||
{
|
||||
return typemap;
|
||||
}
|
||||
|
||||
public int[] getTypes()
|
||||
{
|
||||
return typemap.getTypes();
|
||||
}
|
||||
|
||||
public void setTypemap(TypeMap typemap)
|
||||
{
|
||||
this.typemap = typemap;
|
||||
}
|
||||
|
||||
public int getDClass()
|
||||
{
|
||||
return dclass;
|
||||
}
|
||||
|
||||
public byte getHashAlgorithm()
|
||||
{
|
||||
return hashAlg;
|
||||
}
|
||||
|
||||
public int getIterations()
|
||||
{
|
||||
return iterations;
|
||||
}
|
||||
|
||||
public byte[] getOwner()
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
public byte[] getSalt()
|
||||
{
|
||||
return salt;
|
||||
}
|
||||
|
||||
public Name getZone()
|
||||
{
|
||||
return zone;
|
||||
}
|
||||
|
||||
public NSEC3Record getNSEC3Record()
|
||||
{
|
||||
return new NSEC3Record(getName(), dclass, ttl, optInFlag, hashAlg,
|
||||
iterations, salt, next, getTypes());
|
||||
}
|
||||
|
||||
public void mergeTypes(TypeMap new_types)
|
||||
{
|
||||
int[] nt = new_types.getTypes();
|
||||
for (int i = 0; i < nt.length; i++)
|
||||
{
|
||||
if (!typemap.get(nt[i])) typemap.set(nt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public int compareTo(ProtoNSEC3 o)
|
||||
{
|
||||
if (o == null) return 1;
|
||||
byte[] o_owner = o.getOwner();
|
||||
int len = owner.length < o_owner.length ? o_owner.length : owner.length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
int d = (owner[i] - o_owner[i]);
|
||||
if (d != 0) return d;
|
||||
}
|
||||
return owner.length - o_owner.length;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getName());
|
||||
sb.append(' ');
|
||||
sb.append(ttl);
|
||||
sb.append(' ');
|
||||
sb.append(DClass.string(dclass));
|
||||
sb.append(" NSEC3 ");
|
||||
sb.append(optInFlag ? '1' : '0');
|
||||
sb.append(' ');
|
||||
sb.append(hashAlg);
|
||||
sb.append(' ');
|
||||
sb.append(iterations);
|
||||
sb.append(' ');
|
||||
sb.append(salt == null ? "-" : base16.toString(salt));
|
||||
sb.append(' ');
|
||||
String nextstr = (next == null) ? "(null)" : base32.toString(next)
|
||||
.toLowerCase();
|
||||
sb.append(nextstr);
|
||||
|
||||
int[] types = getTypes();
|
||||
for (int i = 0; i < types.length; i++)
|
||||
{
|
||||
sb.append(" ");
|
||||
sb.append(Type.string(types[i]));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class Comparator implements java.util.Comparator
|
||||
{
|
||||
|
||||
public int compare(Object o1, Object o2)
|
||||
{
|
||||
return ((ProtoNSEC3) o1).compareTo((ProtoNSEC3) o2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -31,7 +31,6 @@ import java.util.logging.Logger;
|
||||
import org.xbill.DNS.*;
|
||||
import org.xbill.DNS.utils.base64;
|
||||
|
||||
|
||||
/**
|
||||
* This class contains a bunch of utility methods that are generally useful in
|
||||
* signing zones.
|
||||
@ -43,15 +42,16 @@ import org.xbill.DNS.utils.base64;
|
||||
|
||||
public class SignUtils
|
||||
{
|
||||
private static final int ASN1_INT = 0x02;
|
||||
private static final int ASN1_SEQ = 0x30;
|
||||
private static final int ASN1_INT = 0x02;
|
||||
private static final int ASN1_SEQ = 0x30;
|
||||
|
||||
public static final int RR_NORMAL = 0;
|
||||
public static final int RR_DELEGATION = 1;
|
||||
public static final int RR_GLUE = 2;
|
||||
public static final int RR_INVALID = 3;
|
||||
public static final int RR_NORMAL = 0;
|
||||
public static final int RR_DELEGATION = 1;
|
||||
public static final int RR_GLUE = 2;
|
||||
public static final int RR_INVALID = 3;
|
||||
private static final int[] ENT_NSEC3_TYPES = {Type.RRSIG, Type.NSEC3};
|
||||
|
||||
private static Logger log;
|
||||
private static Logger log;
|
||||
|
||||
static
|
||||
{
|
||||
@ -78,9 +78,9 @@ public class SignUtils
|
||||
public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
|
||||
Date start, Date expire, long sig_ttl)
|
||||
{
|
||||
return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl,
|
||||
rrset.getType(), key.getAlgorithm(), (int) rrset.getTTL(), expire,
|
||||
start, key.getFootprint(), key.getName(), null);
|
||||
return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl, rrset
|
||||
.getType(), key.getAlgorithm(), (int) rrset.getTTL(), expire, start,
|
||||
key.getFootprint(), key.getName(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,9 +97,9 @@ public class SignUtils
|
||||
public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key,
|
||||
Date start, Date expire, long sig_ttl)
|
||||
{
|
||||
return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl,
|
||||
rec.getType(), key.getAlgorithm(), rec.getTTL(), expire, start,
|
||||
key.getFootprint(), key.getName(), null);
|
||||
return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl, rec
|
||||
.getType(), key.getAlgorithm(), rec.getTTL(), expire, start, key
|
||||
.getFootprint(), key.getName(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,10 +229,10 @@ public class SignUtils
|
||||
*/
|
||||
public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig)
|
||||
{
|
||||
return new RRSIGRecord(presig.getName(), presig.getDClass(),
|
||||
presig.getTTL(), presig.getTypeCovered(), presig.getAlgorithm(),
|
||||
presig.getOrigTTL(), presig.getExpire(), presig.getTimeSigned(),
|
||||
presig.getFootprint(), presig.getSigner(), signature);
|
||||
return new RRSIGRecord(presig.getName(), presig.getDClass(), presig
|
||||
.getTTL(), presig.getTypeCovered(), presig.getAlgorithm(), presig
|
||||
.getOrigTTL(), presig.getExpire(), presig.getTimeSigned(), presig
|
||||
.getFootprint(), presig.getSigner(), signature);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -479,7 +479,8 @@ public class SignUtils
|
||||
// Current record is part of the current RRset.
|
||||
if (rrset.getName().equals(r.getName())
|
||||
&& rrset.getDClass() == r.getDClass()
|
||||
&& ((r.getType() == Type.RRSIG && rrset.getType() == ((RRSIGRecord) r).getTypeCovered()) || rrset.getType() == r.getType()))
|
||||
&& ((r.getType() == Type.RRSIG && rrset.getType() == ((RRSIGRecord) r)
|
||||
.getTypeCovered()) || rrset.getType() == r.getType()))
|
||||
{
|
||||
rrset.addRR(r);
|
||||
continue;
|
||||
@ -650,11 +651,281 @@ public class SignUtils
|
||||
log.finer("Generated: " + nsec);
|
||||
}
|
||||
|
||||
public static void generateNSEC3Records(Name zonename, List records,
|
||||
byte[] salt, int iterations) throws NoSuchAlgorithmException
|
||||
{
|
||||
List proto_nsec3s = new ArrayList();
|
||||
NodeInfo current_node = null;
|
||||
NodeInfo last_node = null;
|
||||
// For detecting glue.
|
||||
Name last_cut = null;
|
||||
|
||||
for (Iterator i = records.iterator(); i.hasNext();)
|
||||
{
|
||||
Record r = (Record) i.next();
|
||||
Name r_name = r.getName();
|
||||
int r_type = r.getType();
|
||||
|
||||
// Classify this record so we know if we can skip it.
|
||||
int r_sectype = recordSecType(zonename, r_name, r_type, last_cut);
|
||||
|
||||
// skip irrelevant records
|
||||
if (r_sectype == RR_INVALID || r_sectype == RR_GLUE) continue;
|
||||
|
||||
// note our last delegation point so we can recognize glue.
|
||||
if (r_sectype == RR_DELEGATION) last_cut = r_name;
|
||||
|
||||
// For the first iteration, we create our current node.
|
||||
if (current_node == null)
|
||||
{
|
||||
current_node = new NodeInfo(r);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are at the same name, we are on the same node.
|
||||
if (r_name.equals(current_node.name))
|
||||
{
|
||||
current_node.addType(r_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point, r represents the start of a new node.
|
||||
// So we move current_node to last_node and generate a new current node.
|
||||
// But first, we need to do something with the last node.
|
||||
generateNSEC3ForNode(last_node,
|
||||
zonename,
|
||||
salt,
|
||||
iterations,
|
||||
false,
|
||||
proto_nsec3s);
|
||||
|
||||
last_node = current_node;
|
||||
current_node = new NodeInfo(r);
|
||||
}
|
||||
|
||||
// process last two nodes.
|
||||
generateNSEC3ForNode(last_node,
|
||||
zonename,
|
||||
salt,
|
||||
iterations,
|
||||
false,
|
||||
proto_nsec3s);
|
||||
generateNSEC3ForNode(current_node,
|
||||
zonename,
|
||||
salt,
|
||||
iterations,
|
||||
false,
|
||||
proto_nsec3s);
|
||||
|
||||
List nsec3s = finishNSEC3s(proto_nsec3s);
|
||||
records.addAll(nsec3s);
|
||||
}
|
||||
|
||||
public static void generateOptInNSEC3Records(Name zonename, List records,
|
||||
List includedNames, byte[] salt, int iterations)
|
||||
throws NoSuchAlgorithmException
|
||||
{
|
||||
List proto_nsec3s = new ArrayList();
|
||||
NodeInfo current_node = null;
|
||||
NodeInfo last_node = null;
|
||||
// For detecting glue.
|
||||
Name last_cut = null;
|
||||
|
||||
HashSet includeSet = null;
|
||||
if (includedNames != null)
|
||||
{
|
||||
includeSet = new HashSet(includedNames);
|
||||
}
|
||||
|
||||
for (Iterator i = records.iterator(); i.hasNext();)
|
||||
{
|
||||
Record r = (Record) i.next();
|
||||
Name r_name = r.getName();
|
||||
int r_type = r.getType();
|
||||
|
||||
// Classify this record so we know if we can skip it.
|
||||
int r_sectype = recordSecType(zonename, r_name, r_type, last_cut);
|
||||
|
||||
// skip irrelevant records
|
||||
if (r_sectype == RR_INVALID || r_sectype == RR_GLUE) continue;
|
||||
|
||||
// note our last delegation point so we can recognize glue.
|
||||
if (r_sectype == RR_DELEGATION) last_cut = r_name;
|
||||
|
||||
// For the first iteration, we create our current node.
|
||||
if (current_node == null)
|
||||
{
|
||||
current_node = new NodeInfo(r);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are at the same name, we are on the same node.
|
||||
if (r_name.equals(current_node.name))
|
||||
{
|
||||
current_node.addType(r_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (includeSet != null && includeSet.contains(current_node.name))
|
||||
{
|
||||
current_node.isSecureNode = true;
|
||||
}
|
||||
|
||||
// At this point, r represents the start of a new node.
|
||||
// So we move current_node to last_node and generate a new current node.
|
||||
// But first, we need to do something with the last node.
|
||||
generateNSEC3ForNode(last_node,
|
||||
zonename,
|
||||
salt,
|
||||
iterations,
|
||||
true,
|
||||
proto_nsec3s);
|
||||
|
||||
if (current_node.isSecureNode)
|
||||
{
|
||||
last_node = current_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_node.hasOptInSpan = true;
|
||||
}
|
||||
|
||||
current_node = new NodeInfo(r);
|
||||
}
|
||||
|
||||
// process last two nodes.
|
||||
generateNSEC3ForNode(last_node,
|
||||
zonename,
|
||||
salt,
|
||||
iterations,
|
||||
true,
|
||||
proto_nsec3s);
|
||||
generateNSEC3ForNode(current_node,
|
||||
zonename,
|
||||
salt,
|
||||
iterations,
|
||||
true,
|
||||
proto_nsec3s);
|
||||
|
||||
List nsec3s = finishNSEC3s(proto_nsec3s);
|
||||
records.addAll(nsec3s);
|
||||
}
|
||||
|
||||
private static void generateNSEC3ForNode(NodeInfo node, Name zonename,
|
||||
byte[] salt, int iterations, boolean optIn, List nsec3s)
|
||||
throws NoSuchAlgorithmException
|
||||
{
|
||||
if (node == null) return;
|
||||
if (optIn && !node.isSecureNode) return;
|
||||
|
||||
// Add our default types.
|
||||
node.addType(Type.RRSIG);
|
||||
node.addType(Type.NSEC3);
|
||||
|
||||
// Check for ENTs -- note this will generate duplicate ENTs because it
|
||||
// doesn't use any context.
|
||||
int ldiff = node.name.labels() - zonename.labels();
|
||||
for (int i = 1; i < ldiff; i++)
|
||||
{
|
||||
Name n = new Name(node.name, i);
|
||||
log.info("Generating ENT NSEC3 for " + n);
|
||||
ProtoNSEC3 nsec3 = generateNSEC3(n,
|
||||
zonename,
|
||||
node.ttl,
|
||||
salt,
|
||||
iterations,
|
||||
optIn,
|
||||
null);
|
||||
nsec3s.add(nsec3);
|
||||
}
|
||||
|
||||
ProtoNSEC3 nsec3 = generateNSEC3(node.name,
|
||||
zonename,
|
||||
node.ttl,
|
||||
salt,
|
||||
iterations,
|
||||
optIn,
|
||||
node.getTypes());
|
||||
nsec3s.add(nsec3);
|
||||
}
|
||||
|
||||
private static ProtoNSEC3 generateNSEC3(Name name, Name zonename, long ttl,
|
||||
byte[] salt, int iterations, boolean optIn, int[] types)
|
||||
throws NoSuchAlgorithmException
|
||||
{
|
||||
byte[] hash = NSEC3Record.hash(name,
|
||||
NSEC3Record.SHA1_DIGEST_ID,
|
||||
iterations,
|
||||
salt);
|
||||
|
||||
if (types == null)
|
||||
{
|
||||
types = ENT_NSEC3_TYPES;
|
||||
}
|
||||
ProtoNSEC3 r = new ProtoNSEC3(hash, zonename, DClass.IN, ttl, optIn,
|
||||
NSEC3Record.SHA1_DIGEST_ID, iterations, salt, null, types);
|
||||
|
||||
log.finer("Generated: " + r);
|
||||
return r;
|
||||
}
|
||||
|
||||
private static List finishNSEC3s(List nsec3s)
|
||||
{
|
||||
if (nsec3s == null) return null;
|
||||
Collections.sort(nsec3s, new ProtoNSEC3.Comparator());
|
||||
|
||||
ProtoNSEC3 prev_nsec3 = null;
|
||||
ProtoNSEC3 cur_nsec3 = null;
|
||||
byte[] first_nsec3_hash = null;
|
||||
|
||||
for (ListIterator i = nsec3s.listIterator(); i.hasNext();)
|
||||
{
|
||||
cur_nsec3 = (ProtoNSEC3) i.next();
|
||||
|
||||
// log.fine("finishNSEC3s: processing " + cur_nsec3);
|
||||
// check to see if cur is a duplicate (by name)
|
||||
if (prev_nsec3 != null
|
||||
&& Arrays.equals(prev_nsec3.getOwner(), cur_nsec3.getOwner()))
|
||||
{
|
||||
log.fine("found duplicate NSEC3 (by name) -- merging type maps: "
|
||||
+ prev_nsec3.getTypemap() + " and " + cur_nsec3.getTypemap());
|
||||
i.remove();
|
||||
prev_nsec3.mergeTypes(cur_nsec3.getTypemap());
|
||||
log.fine("merged type map: " + prev_nsec3.getTypemap());
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] next = cur_nsec3.getOwner();
|
||||
|
||||
if (prev_nsec3 == null)
|
||||
{
|
||||
prev_nsec3 = cur_nsec3;
|
||||
first_nsec3_hash = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
prev_nsec3.setNext(next);
|
||||
prev_nsec3 = cur_nsec3;
|
||||
}
|
||||
|
||||
// Handle last NSEC3.
|
||||
cur_nsec3.setNext(first_nsec3_hash);
|
||||
|
||||
List res = new ArrayList(nsec3s.size());
|
||||
for (Iterator i = nsec3s.iterator(); i.hasNext();)
|
||||
{
|
||||
ProtoNSEC3 p = (ProtoNSEC3) i.next();
|
||||
res.add(p.getNSEC3Record());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a canonical (by name) ordered list of records in a zone, generate
|
||||
* the NXT records in place.
|
||||
* the NSEC records in place.
|
||||
*
|
||||
* Note thatthe list that the records are stored in must support the
|
||||
* Note that the list that the records are stored in must support the
|
||||
* <code>listIterator.add</code> operation.
|
||||
*
|
||||
* @param zonename the name of the zone apex, used to distinguish between
|
||||
@ -838,7 +1109,8 @@ public class SignUtils
|
||||
{
|
||||
Record r = (Record) i.next();
|
||||
|
||||
if (r.getType() == Type.RRSIG || r.getType() == Type.NSEC)
|
||||
if (r.getType() == Type.RRSIG || r.getType() == Type.NSEC
|
||||
|| r.getType() == Type.NSEC3)
|
||||
{
|
||||
i.remove();
|
||||
}
|
||||
@ -898,9 +1170,9 @@ public class SignUtils
|
||||
|
||||
byte[] digest = md.digest(os.toByteArray());
|
||||
|
||||
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl,
|
||||
keyrec.getFootprint(), keyrec.getAlgorithm(),
|
||||
DSRecord.SHA1_DIGEST_ID, digest);
|
||||
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl, keyrec
|
||||
.getFootprint(), keyrec.getAlgorithm(), DSRecord.SHA1_DIGEST_ID,
|
||||
digest);
|
||||
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
|
Loading…
Reference in New Issue
Block a user