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.security.GeneralSecurityException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
import java.util.logging.Handler;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ListIterator;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
@ -47,6 +42,7 @@ import org.xbill.DNS.RRset;
|
|||||||
import org.xbill.DNS.Record;
|
import org.xbill.DNS.Record;
|
||||||
import org.xbill.DNS.TextParseException;
|
import org.xbill.DNS.TextParseException;
|
||||||
import org.xbill.DNS.Type;
|
import org.xbill.DNS.Type;
|
||||||
|
import org.xbill.DNS.utils.base16;
|
||||||
|
|
||||||
import com.verisignlabs.dnssec.security.BINDKeyUtils;
|
import com.verisignlabs.dnssec.security.BINDKeyUtils;
|
||||||
import com.verisignlabs.dnssec.security.DnsKeyPair;
|
import com.verisignlabs.dnssec.security.DnsKeyPair;
|
||||||
@ -68,8 +64,7 @@ public class SignZone
|
|||||||
private static Logger log;
|
private static Logger log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a small inner class used to hold all of the command line option
|
* This is an inner class used to hold all of the command line option state.
|
||||||
* state.
|
|
||||||
*/
|
*/
|
||||||
private static class CLIState
|
private static class CLIState
|
||||||
{
|
{
|
||||||
@ -85,9 +80,11 @@ public class SignZone
|
|||||||
public boolean verifySigs = false;
|
public boolean verifySigs = false;
|
||||||
public boolean selfSignKeys = true;
|
public boolean selfSignKeys = true;
|
||||||
public boolean useOptIn = false;
|
public boolean useOptIn = false;
|
||||||
public boolean optInConserve = false;
|
|
||||||
public boolean fullySignKeyset = false;
|
public boolean fullySignKeyset = false;
|
||||||
public List includeNames = null;
|
public List includeNames = null;
|
||||||
|
public boolean useNsec3 = false;
|
||||||
|
public byte[] salt = null;
|
||||||
|
public int iterations = 0;
|
||||||
|
|
||||||
public CLIState()
|
public CLIState()
|
||||||
{
|
{
|
||||||
@ -115,23 +112,25 @@ public class SignZone
|
|||||||
case 0 :
|
case 0 :
|
||||||
rootLogger.setLevel(Level.OFF);
|
rootLogger.setLevel(Level.OFF);
|
||||||
break;
|
break;
|
||||||
case 5 :
|
case 4 :
|
||||||
default :
|
default :
|
||||||
|
rootLogger.setLevel(Level.INFO);
|
||||||
|
break;
|
||||||
|
case 5 :
|
||||||
rootLogger.setLevel(Level.FINE);
|
rootLogger.setLevel(Level.FINE);
|
||||||
break;
|
break;
|
||||||
case 6 :
|
case 6 :
|
||||||
rootLogger.setLevel(Level.ALL);
|
rootLogger.setLevel(Level.ALL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Handler[] handlers = rootLogger.getHandlers();
|
||||||
|
for (int i = 0; i < handlers.length; i++)
|
||||||
|
handlers[i].setLevel(rootLogger.getLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cli.hasOption('a')) verifySigs = true;
|
if (cli.hasOption('a')) verifySigs = true;
|
||||||
|
if (cli.hasOption('3')) useNsec3 = true;
|
||||||
if (cli.hasOption('O')) useOptIn = true;
|
if (cli.hasOption('O')) useOptIn = true;
|
||||||
if (cli.hasOption('C'))
|
|
||||||
{
|
|
||||||
useOptIn = true;
|
|
||||||
optInConserve = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cli.hasOption('F')) fullySignKeyset = true;
|
if (cli.hasOption('F')) fullySignKeyset = true;
|
||||||
|
|
||||||
@ -185,6 +184,21 @@ public class SignZone
|
|||||||
includeNames = getNameList(includeNamesFile);
|
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();
|
String[] files = cli.getArgs();
|
||||||
|
|
||||||
if (files.length < 2)
|
if (files.length < 2)
|
||||||
@ -209,73 +223,60 @@ public class SignZone
|
|||||||
|
|
||||||
// boolean options
|
// boolean options
|
||||||
opts.addOption("h", "help", false, "Print this message.");
|
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",
|
opts.addOption("F",
|
||||||
"fully-sign-keyset",
|
"fully-sign-keyset",
|
||||||
false,
|
false,
|
||||||
"sign the zone apex keyset with all "
|
"sign the zone apex keyset with all available keys.");
|
||||||
+ "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);
|
|
||||||
|
|
||||||
// Argument options
|
// Argument options
|
||||||
OptionBuilder.hasOptionalArg();
|
opts.addOption(OptionBuilder.hasOptionalArg().withLongOpt("verbose")
|
||||||
OptionBuilder.withLongOpt("verbose");
|
.withArgName("level").withDescription("verbosity level")
|
||||||
OptionBuilder.withArgName("level");
|
.create('v'));
|
||||||
OptionBuilder.withDescription("verbosity level -- 0 is silence, "
|
opts.addOption(OptionBuilder.hasArg().withArgName("dir")
|
||||||
+ "5 is debug information, " + "6 is trace information. "
|
.withLongOpt("keyset-directory")
|
||||||
+ "No argument means 5.");
|
.withDescription("directory to find keyset files (default '.').")
|
||||||
opts.addOption(OptionBuilder.create('v'));
|
.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();
|
// NSEC3 options
|
||||||
OptionBuilder.withArgName("dir");
|
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
|
||||||
OptionBuilder.withLongOpt("keyset-directory");
|
opts.addOption("O",
|
||||||
OptionBuilder.withDescription("directory to find keyset files (default '.').");
|
"use-opt-in",
|
||||||
opts.addOption(OptionBuilder.create('d'));
|
false,
|
||||||
|
"generate a fully Opt-In zone.");
|
||||||
|
|
||||||
OptionBuilder.hasArg();
|
opts.addOption(OptionBuilder.hasArg().withLongOpt("salt")
|
||||||
OptionBuilder.withArgName("dir");
|
.withArgName("hex value").withDescription("supply a salt value.")
|
||||||
OptionBuilder.withLongOpt("key-directory");
|
.create());
|
||||||
OptionBuilder.withDescription("directory to find key files (default '.').");
|
opts.addOption(OptionBuilder.hasArg().withLongOpt("random-salt")
|
||||||
opts.addOption(OptionBuilder.create('D'));
|
.withArgName("length").withDescription("generate a random salt.")
|
||||||
|
.create());
|
||||||
OptionBuilder.hasArg();
|
opts.addOption(OptionBuilder.hasArg().withLongOpt("iterations")
|
||||||
OptionBuilder.withArgName("time/offset");
|
.withArgName("value")
|
||||||
OptionBuilder.withLongOpt("start-time");
|
.withDescription("use this value for the iterations in NSEC3.")
|
||||||
OptionBuilder.withDescription("signature starting time (default is now - 1 hour)");
|
.create());
|
||||||
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'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Print out the usage and help statements, then quit. */
|
/** Print out the usage and help statements, then quit. */
|
||||||
@ -286,7 +287,8 @@ public class SignZone
|
|||||||
PrintWriter out = new PrintWriter(System.err);
|
PrintWriter out = new PrintWriter(System.err);
|
||||||
|
|
||||||
// print our own usage statement:
|
// print our own usage statement:
|
||||||
out.println("usage: signZone.sh [..options..] zone_file [key_file ...] ");
|
out.println("usage: signZone.sh [..options..] "
|
||||||
|
+ "zone_file [key_file ...] ");
|
||||||
f.printHelp(out,
|
f.printHelp(out,
|
||||||
75,
|
75,
|
||||||
"signZone.sh",
|
"signZone.sh",
|
||||||
@ -572,10 +574,8 @@ public class SignZone
|
|||||||
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.
|
||||||
@ -595,7 +595,8 @@ public class SignZone
|
|||||||
// otherwise we will just sign them with the zonesigning keys.
|
// otherwise we will just sign them with the zonesigning keys.
|
||||||
if (keysigningkeypairs != null && keysigningkeypairs.size() > 0)
|
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);
|
toList.addAll(sigs);
|
||||||
|
|
||||||
// If we aren't going to sign with all the keys, bail out now.
|
// 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,
|
private static List signZone(JCEDnsSecSigner signer, Name zonename,
|
||||||
List records, List keysigningkeypairs, List zonekeypairs, Date start,
|
List records, List keysigningkeypairs, List zonekeypairs, Date start,
|
||||||
Date expire, boolean useOptIn, boolean useConservativeOptIn,
|
Date expire, boolean fullySignKeyset)
|
||||||
boolean fullySignKeyset, List NSECIncludeNames) throws IOException,
|
throws IOException, GeneralSecurityException
|
||||||
GeneralSecurityException
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// Remove any existing DNSSEC records (NSEC, RRSIG)
|
// Remove any existing DNSSEC records (NSEC, NSEC3, RRSIG)
|
||||||
SignUtils.removeGeneratedRecords(zonename, records);
|
SignUtils.removeGeneratedRecords(zonename, records);
|
||||||
|
|
||||||
// Sort the zone
|
// Sort the zone
|
||||||
Collections.sort(records, new RecordComparator());
|
Collections.sort(records, new RecordComparator());
|
||||||
|
|
||||||
@ -655,18 +656,8 @@ public class SignZone
|
|||||||
// Generate DS records
|
// Generate DS records
|
||||||
SignUtils.generateDSRecords(zonename, records);
|
SignUtils.generateDSRecords(zonename, records);
|
||||||
|
|
||||||
// Generate NXT records
|
// Generate the NSEC records
|
||||||
if (useOptIn)
|
SignUtils.generateNSECRecords(zonename, records);
|
||||||
{
|
|
||||||
SignUtils.generateOptInNSECRecords(zonename,
|
|
||||||
records,
|
|
||||||
NSECIncludeNames,
|
|
||||||
useConservativeOptIn);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SignUtils.generateNSECRecords(zonename, records);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble into RRsets and sign.
|
// Assemble into RRsets and sign.
|
||||||
RRset rrset = new RRset();
|
RRset rrset = new RRset();
|
||||||
@ -678,7 +669,102 @@ public class SignZone
|
|||||||
Record r = (Record) i.next();
|
Record r = (Record) i.next();
|
||||||
|
|
||||||
// First record
|
// 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);
|
rrset.addRR(r);
|
||||||
continue;
|
continue;
|
||||||
@ -823,17 +909,34 @@ public class SignZone
|
|||||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||||
|
|
||||||
// Sign the zone.
|
// Sign the zone.
|
||||||
List signed_records = signZone(signer,
|
List signed_records;
|
||||||
zonename,
|
|
||||||
records,
|
if (state.useNsec3)
|
||||||
kskpairs,
|
{
|
||||||
keypairs,
|
signed_records = signZoneNSEC3(signer,
|
||||||
state.start,
|
zonename,
|
||||||
state.expire,
|
records,
|
||||||
state.useOptIn,
|
kskpairs,
|
||||||
state.optInConserve,
|
keypairs,
|
||||||
state.fullySignKeyset,
|
state.start,
|
||||||
state.includeNames);
|
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
|
// write out the signed zone
|
||||||
// force multiline mode for now
|
// 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.*;
|
||||||
import org.xbill.DNS.utils.base64;
|
import org.xbill.DNS.utils.base64;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains a bunch of utility methods that are generally useful in
|
* This class contains a bunch of utility methods that are generally useful in
|
||||||
* signing zones.
|
* signing zones.
|
||||||
@ -43,15 +42,16 @@ import org.xbill.DNS.utils.base64;
|
|||||||
|
|
||||||
public class SignUtils
|
public class SignUtils
|
||||||
{
|
{
|
||||||
private static final int ASN1_INT = 0x02;
|
private static final int ASN1_INT = 0x02;
|
||||||
private static final int ASN1_SEQ = 0x30;
|
private static final int ASN1_SEQ = 0x30;
|
||||||
|
|
||||||
public static final int RR_NORMAL = 0;
|
public static final int RR_NORMAL = 0;
|
||||||
public static final int RR_DELEGATION = 1;
|
public static final int RR_DELEGATION = 1;
|
||||||
public static final int RR_GLUE = 2;
|
public static final int RR_GLUE = 2;
|
||||||
public static final int RR_INVALID = 3;
|
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
|
static
|
||||||
{
|
{
|
||||||
@ -78,9 +78,9 @@ public class SignUtils
|
|||||||
public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
|
public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
|
||||||
Date start, Date expire, long sig_ttl)
|
Date start, Date expire, long sig_ttl)
|
||||||
{
|
{
|
||||||
return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl,
|
return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl, rrset
|
||||||
rrset.getType(), key.getAlgorithm(), (int) rrset.getTTL(), expire,
|
.getType(), key.getAlgorithm(), (int) rrset.getTTL(), expire, start,
|
||||||
start, key.getFootprint(), key.getName(), null);
|
key.getFootprint(), key.getName(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,9 +97,9 @@ public class SignUtils
|
|||||||
public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key,
|
public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key,
|
||||||
Date start, Date expire, long sig_ttl)
|
Date start, Date expire, long sig_ttl)
|
||||||
{
|
{
|
||||||
return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl,
|
return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl, rec
|
||||||
rec.getType(), key.getAlgorithm(), rec.getTTL(), expire, start,
|
.getType(), key.getAlgorithm(), rec.getTTL(), expire, start, key
|
||||||
key.getFootprint(), key.getName(), null);
|
.getFootprint(), key.getName(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,10 +229,10 @@ public class SignUtils
|
|||||||
*/
|
*/
|
||||||
public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig)
|
public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig)
|
||||||
{
|
{
|
||||||
return new RRSIGRecord(presig.getName(), presig.getDClass(),
|
return new RRSIGRecord(presig.getName(), presig.getDClass(), presig
|
||||||
presig.getTTL(), presig.getTypeCovered(), presig.getAlgorithm(),
|
.getTTL(), presig.getTypeCovered(), presig.getAlgorithm(), presig
|
||||||
presig.getOrigTTL(), presig.getExpire(), presig.getTimeSigned(),
|
.getOrigTTL(), presig.getExpire(), presig.getTimeSigned(), presig
|
||||||
presig.getFootprint(), presig.getSigner(), signature);
|
.getFootprint(), presig.getSigner(), signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -479,7 +479,8 @@ public class SignUtils
|
|||||||
// Current record is part of the current RRset.
|
// Current record is part of the current RRset.
|
||||||
if (rrset.getName().equals(r.getName())
|
if (rrset.getName().equals(r.getName())
|
||||||
&& rrset.getDClass() == r.getDClass()
|
&& 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);
|
rrset.addRR(r);
|
||||||
continue;
|
continue;
|
||||||
@ -650,11 +651,281 @@ public class SignUtils
|
|||||||
log.finer("Generated: " + nsec);
|
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
|
* 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.
|
* <code>listIterator.add</code> operation.
|
||||||
*
|
*
|
||||||
* @param zonename the name of the zone apex, used to distinguish between
|
* @param zonename the name of the zone apex, used to distinguish between
|
||||||
@ -838,7 +1109,8 @@ public class SignUtils
|
|||||||
{
|
{
|
||||||
Record r = (Record) i.next();
|
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();
|
i.remove();
|
||||||
}
|
}
|
||||||
@ -898,9 +1170,9 @@ public class SignUtils
|
|||||||
|
|
||||||
byte[] digest = md.digest(os.toByteArray());
|
byte[] digest = md.digest(os.toByteArray());
|
||||||
|
|
||||||
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl,
|
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl, keyrec
|
||||||
keyrec.getFootprint(), keyrec.getAlgorithm(),
|
.getFootprint(), keyrec.getAlgorithm(), DSRecord.SHA1_DIGEST_ID,
|
||||||
DSRecord.SHA1_DIGEST_ID, digest);
|
digest);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (NoSuchAlgorithmException e)
|
catch (NoSuchAlgorithmException e)
|
||||||
|
Loading…
Reference in New Issue
Block a user