12 Commits
v0.11 ... 0.13

Author SHA1 Message Date
David Blacka
fb689c046f Update changelog, set this as a release. 2017-01-06 13:01:05 -05:00
David Blacka
8d3746fc22 Validate the the RRset TTL is <= the OrigTTL. 2017-01-06 12:54:16 -05:00
David Blacka
444601fb2a Detect duplicate RRSIGs as well. 2017-01-06 12:53:57 -05:00
David Blacka
c5896495c7 Either R or S could end up being shorter than the expected length, so adjust for that. 2017-01-05 13:50:48 -05:00
David Blacka
c13d9379b3 Update version, convert readme to markdown. 2016-12-09 17:57:09 -05:00
David Blacka
f170bd170a Elliptic curve support.
Improve usage, unknown algorithm error handling in jdnssec-keygen
Use the bouncycastle crypto provider for ECCGOST if available
2016-12-09 17:52:10 -05:00
David Blacka
6bbcf38fe1 update the embedded dnsjava version to 2.1.7 (jdnssec-dnsjava) 2016-08-22 09:40:47 -04:00
David Blacka
15cb5e2ab7 Fix issue in jdnssec-verifyzone (and ZoneVerifier) where junk in the zone wouldn't be handled correctly (that is, ignored.) 2014-04-22 16:39:00 -04:00
David Blacka
9fad4941a6 Make jdnssec-zoneformat -N also compute NSEC3 original owner names for ENTs 2014-04-22 16:37:58 -04:00
David Blacka
18df8a8d9e update version to prep for an official release. 2012-07-16 14:20:14 -04:00
David Blacka
a45f5d1df7 use the perfectly OK (now) TypeMap.toString() method. 2012-07-16 14:16:42 -04:00
David Blacka
3da308c4b9 Fix TypeMap.fromBytes() and add a TypeMap.fromString() method. 2012-07-16 14:16:13 -04:00
18 changed files with 908 additions and 399 deletions

View File

@@ -1,3 +1,43 @@
2017-01-06 David Blacka <davidb@verisign.com>
* Released version 0.13
* ZoneVerifier: detect duplicate RRSIGs as well as other duplicate
RRs.
* DnsSecVerifier: check that the RRset's TTL <= OrigTTL.
2016-12-09 David Blacka <davidb@verisign.com>
* Add key generation, signing, verification support for elliptic
curve algorithms: ECDSA P-256 (13) and ECDSA P-384 (15).
- Opportunistically load the bouncycastle provider for ECCGOST
support.
* DnsKeyAlgorithms: refactoring, new methods to better support
elliptic curve, alias, knowing what algorithms are supported.
* KeyGen: do not display unsupported algorithms.
2016-08-22 David Blacka <davidb@verisign.com>
* Update internal dnsjava to 2.1.7-vrsn-1.
2014-04-22 David Blacka <davidb@verisign.com>
* ZoneFormat: Make -N also compute original ownernames for empty
non-terminal NSEC3 records.
* ZoneVerifier: Improve the zone verifiers handling of "junk" in a
zone (i.e., ignore resource records that aren't actually in the
zone itself.)
2012-07-16 David Blacka <davidb@verisign.com>
* Released version 0.12.
* TypeMap: fix the fromBytes() method, which was incorrect and add
a static fromString() method.
* ProtoNSEC3: use TypeMap's toString method, rather than fetching
the array of types and rendering them directly.
2012-05-29 David Blacka <davidb@verisign.com>
* Released version 0.11.

63
README
View File

@@ -1,63 +0,0 @@
jdnssec-tools
http://www.verisignlabs.com/jdnssec-tools/
https://github.com/dblacka/jdnssec-tools/wiki
Author: David Blacka (davidb@verisign.com)
This is a collection of DNSSEC tools written in Java. They are
intended to be an addition or replacement for the DNSSEC tools that
are part of BIND 9.
These tools depend upon DNSjava (http://www.xbill.org/dnsjava), the
Jakarta Commons CLI and Logging libraries (http://jakarta.apache.org),
and Sun's Java Cryptography extensions. A copy of each of these
libraries is included in the distribution. Currently, these tools use
a custom version of the DNSjava library with minor modifications,
which is provided.
See the "licenses" directory for the licensing information of this
package and the other packages that are distributed with it.
Getting started:
1. Unpack the binary distribution:
% tar zxvf java-dnssec-tools-x.x.x.tar.gz
2. Run the various tools from their unpacked location:
% cd java-dnssec-tools-x.x.x
% ./bin/jdnssec-signzone -h
Building from source:
1. Unpack the source distribution, preferably into the same directory
that the binary distribution was unpacked.
% tar zxvf java-dnssec-tools-x.x.x-src.tar.gz
2. Edit the build.properties file to suit your environment.
3. Run Ant (see http://ant.apache.org for information about the Ant
build tool).
% ant
4. You can build the distribution tarballs with 'ant dist'. You can
run the tools directly from the build area (without building the
jdnssec-tools.jar file) by using the ./bin/_jdnssec_* wrappers.
The source for this project is available in git on github:
https://github.com/dblacka/jdnssec-tools
Source for the modified DNSjava library can be found on github as well:
https://github.com/dblacka/jdnssec-dnsjava
---
Questions or comments may be directed to the author
(mailto:davidb@verisign.com) or sent to the
dnssec@verisignlabs.com mailing list
(https://lists.verisignlabs.com/mailman/listinfo/dnssec).

46
README.md Normal file
View File

@@ -0,0 +1,46 @@
# jdnssec-tools
* http://www.verisignlabs.com/jdnssec-tools/
* https://github.com/dblacka/jdnssec-tools/wiki
Author: David Blacka (davidb@verisign.com)
This is a collection of DNSSEC tools written in Java. They are intended to be an addition or replacement for the DNSSEC tools that are part of BIND 9.
These tools depend upon DNSjava (http://www.xbill.org/dnsjava), the Jakarta Commons CLI and Logging libraries (https://commons.apache.org/proper/commons-cli), and Sun's Java Cryptography extensions. A copy of each of these libraries is included in the distribution. Currently, these tools use a custom version of the DNSjava library with minor modifications, which is provided.
See the "licenses" directory for the licensing information of this package and the other packages that are distributed with it.
Getting started:
1. Unpack the binary distribution:
tar zxvf java-dnssec-tools-x.x.x.tar.gz
2. Run the various tools from their unpacked location:
cd java-dnssec-tools-x.x.x
./bin/jdnssec-signzone -h
Building from source:
1. Unpack the source distribution, preferably into the same directory that the binary distribution was unpacked.
tar zxvf java-dnssec-tools-x.x.x-src.tar.gz
2. Edit the build.properties file to suit your environment.
3. Run Ant (see http://ant.apache.org for information about the Ant build tool).
ant
4. You can build the distribution tarballs with 'ant dist'. You can run the tools directly from the build area (without building the jdnssec-tools.jar file) by using the ./bin/_jdnssec_* wrappers.
The source for this project is available in git on github: https://github.com/dblacka/jdnssec-tools
Source for the modified DNSjava library can be found on github as well: https://github.com/dblacka/jdnssec-dnsjava
---
Questions or comments may be directed to the author (mailto:davidb@verisign.com) or sent to the dnssec@verisignlabs.com mailing list (https://lists.verisignlabs.com/mailman/listinfo/dnssec).

View File

@@ -1 +1 @@
version=0.11
version=0.13

View File

@@ -44,8 +44,6 @@
<javac srcdir="${build.src}"
destdir="${build.dest}"
classpathref="project.classpath"
source="1.5"
target="1.5"
deprecation="true"
includeantruntime="false"
includes="com/verisignlabs/dnssec/" />
@@ -70,7 +68,7 @@
verbose="true" author="true"
windowtitle="jdnssec-tools-${version}"
use="true">
<link href="http://java.sun.com/j2se/1.4.2/docs/api/" />
<link href="https://docs.oracle.com/javase/8/docs/api/" />
<link href="http://www.xbill.org/dnsjava/doc/" />
</javadoc>
</target>
@@ -172,4 +170,3 @@
</target>
</project>

Binary file not shown.

Binary file not shown.

View File

@@ -74,17 +74,18 @@ public class KeyGen extends CLBase
OptionBuilder.withDescription("ZONE | OTHER (default ZONE)");
opts.addOption(OptionBuilder.create('n'));
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
OptionBuilder.hasArg();
OptionBuilder.withArgName("algorithm");
OptionBuilder.withDescription("RSA | RSASHA1 | RSAMD5 | DH | DSA "
+ "| RSA-NSEC3-SHA1 | DSA-NSEC3-SHA1 "
+ "| RSASHA256 | RSASHA512 | alias, RSASHA1 is default.");
OptionBuilder.withDescription(String.join(" | ", algStrings) +
" | alias, RSASHA256 is default.");
opts.addOption(OptionBuilder.create('a'));
OptionBuilder.hasArg();
OptionBuilder.withArgName("size");
OptionBuilder.withDescription("key size, in bits. (default = 1024)\n"
+ "RSA: [512..4096]\n" + "DSA: [512..1024]\n" + "DH: [128..4096]");
OptionBuilder.withDescription("key size, in bits. default is 1024. "
+ "RSA: [512..4096], DSA: [512..1024], DH: [128..4096], "
+ "ECDSA: ignored");
opts.addOption(OptionBuilder.create('b'));
OptionBuilder.hasArg();
@@ -98,7 +99,6 @@ public class KeyGen extends CLBase
OptionBuilder.withArgName("dir");
OptionBuilder.withDescription("place generated key files in this " + "directory");
opts.addOption(OptionBuilder.create('d'));
opts.addOption(OptionBuilder.create('A'));
}
protected void processOptions(CommandLine cli)
@@ -136,6 +136,11 @@ public class KeyGen extends CLBase
if ((optstr = cli.getOptionValue('a')) != null)
{
algorithm = parseAlg(optstr);
if (algorithm < 0)
{
System.err.println("DNSSEC algorithm " + optstr + " is not supported");
usage();
}
}
if ((optstr = cli.getOptionValue('b')) != null)
@@ -166,7 +171,11 @@ public class KeyGen extends CLBase
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
int alg = parseInt(s, -1);
if (alg > 0) return alg;
if (alg > 0)
{
if (algs.supportedAlgorithm(alg)) return alg;
return -1;
}
return algs.stringToAlgorithm(s);
}

View File

@@ -546,6 +546,7 @@ public class SignZone extends CLBase
}
}
br.close();
if (res.size() == 0) return null;
return res;
}

View File

@@ -126,13 +126,20 @@ public class ZoneFormat extends CLBase
Collections.sort(zone, new RecordComparator());
// first, find the NSEC3PARAM record -- this is an inefficient linear
// search.
// search, although it should be near the head of the list.
NSEC3PARAMRecord nsec3param = null;
HashMap<String, String> map = new HashMap<String, String>();
base32 b32 = new base32(base32.Alphabet.BASE32HEX, false, true);
Name zonename = null;
for (Record r : zone)
{
if (r.getType() == Type.SOA)
{
zonename = r.getName();
continue;
}
if (r.getType() == Type.NSEC3PARAM)
{
nsec3param = (NSEC3PARAMRecord) r;
@@ -140,6 +147,8 @@ public class ZoneFormat extends CLBase
}
}
// If we couldn't determine a zone name, we have an issue.
if (zonename == null) return;
// If there wasn't one, we have nothing to do.
if (nsec3param == null) return;
@@ -150,10 +159,23 @@ public class ZoneFormat extends CLBase
if (r.getName().equals(last_name)) continue;
if (r.getType() == Type.NSEC3) continue;
byte[] hash = nsec3param.hashName(r.getName());
Name n = r.getName();
byte[] hash = nsec3param.hashName(n);
String hashname = b32.toString(hash);
map.put(hashname, r.getName().toString().toLowerCase());
last_name = r.getName();
map.put(hashname, n.toString().toLowerCase());
last_name = n;
// inefficiently create hashes for the possible ancestor ENTs
for (int i = zonename.labels() + 1; i < n.labels(); ++i)
{
Name parent = new Name(n, n.labels() - i);
byte[] parent_hash = nsec3param.hashName(parent);
String parent_hashname = b32.toString(parent_hash);
if (!map.containsKey(parent_hashname))
{
map.put(parent_hashname, parent.toString().toLowerCase());
}
}
}
// Final pass, assign the names if we can

View File

@@ -29,13 +29,12 @@
package com.verisignlabs.dnssec.security;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.spec.RSAKeyGenParameterSpec;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.logging.Logger;
import org.xbill.DNS.DNSSEC;
@@ -56,28 +55,46 @@ import org.xbill.DNS.DNSSEC;
public class DnsKeyAlgorithm
{
// Our base algorithm numbers. This is a normalization of the DNSSEC
// algorithms (which are really signature algorithms). Thus RSASHA1,
// RSASHA256, etc. all boil down to 'RSA' here.
public static final int UNKNOWN = -1;
public static final int RSA = 1;
public static final int DH = 2;
public static final int DSA = 3;
public static final int ECC_GOST = 4;
public static final int ECDSA = 5;
private static class Entry
private static class AlgEntry
{
public int dnssecAlgorithm;
public String sigName;
public int baseType;
public Entry(String sigName, int baseType)
public AlgEntry(int algorithm, String sigName, int baseType)
{
this.dnssecAlgorithm = algorithm;
this.sigName = sigName;
this.baseType = baseType;
}
}
private static class ECAlgEntry extends AlgEntry
{
public ECParameterSpec ec_spec;
public ECAlgEntry(int algorithm, String sigName, int baseType, ECParameterSpec spec)
{
super(algorithm, sigName, baseType);
this.ec_spec = spec;
}
}
/**
* This is a mapping of algorithm identifier to Entry. The Entry contains the
* data needed to map the algorithm to the various crypto implementations.
*/
private HashMap<Integer, Entry> mAlgorithmMap;
private HashMap<Integer, AlgEntry> mAlgorithmMap;
/**
* This is a mapping of algorithm mnemonics to algorithm identifiers.
*/
@@ -90,8 +107,12 @@ public class DnsKeyAlgorithm
/** This is a cached key pair generator for RSA keys. */
private KeyPairGenerator mRSAKeyGenerator;
/** This is a cache key pair generator for DSA keys. */
/** This is a cached key pair generator for DSA keys. */
private KeyPairGenerator mDSAKeyGenerator;
/** This is a cached key pair generator for ECC GOST keys. */
private KeyPairGenerator mECGOSTKeyGenerator;
/** This is a cached key pair generator for ECDSA_P256 keys. */
private KeyPairGenerator mECKeyGenerator;
private Logger log = Logger.getLogger(this.getClass().toString());
@@ -100,21 +121,37 @@ public class DnsKeyAlgorithm
public DnsKeyAlgorithm()
{
mAlgorithmMap = new HashMap<Integer, Entry>();
// Attempt to add the bouncycastle provider.
// This is so we can use this provider if it is available, but not require
// the user to add it as one of the java.security providers.
try
{
Class<?> bc_provider_class = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
Provider bc_provider = (Provider) bc_provider_class.newInstance();
Security.addProvider(bc_provider);
}
catch (ReflectiveOperationException e) { }
initialize();
}
private void initialize()
{
mAlgorithmMap = new HashMap<Integer, AlgEntry>();
mMnemonicToIdMap = new HashMap<String, Integer>();
mIdToMnemonicMap = new HashMap<Integer, String>();
// Load the standard DNSSEC algorithms.
addAlgorithm(DNSSEC.Algorithm.RSAMD5, new Entry("MD5withRSA", RSA));
addAlgorithm(DNSSEC.Algorithm.RSAMD5, "MD5withRSA", RSA);
addMnemonic("RSAMD5", DNSSEC.Algorithm.RSAMD5);
addAlgorithm(DNSSEC.Algorithm.DH, new Entry("", DH));
addAlgorithm(DNSSEC.Algorithm.DH, "", DH);
addMnemonic("DH", DNSSEC.Algorithm.DH);
addAlgorithm(DNSSEC.Algorithm.DSA, new Entry("SHA1withDSA", DSA));
addAlgorithm(DNSSEC.Algorithm.DSA, "SHA1withDSA", DSA);
addMnemonic("DSA", DNSSEC.Algorithm.DSA);
addAlgorithm(DNSSEC.Algorithm.RSASHA1, new Entry("SHA1withRSA", RSA));
addAlgorithm(DNSSEC.Algorithm.RSASHA1, "SHA1withRSA", RSA);
addMnemonic("RSASHA1", DNSSEC.Algorithm.RSASHA1);
addMnemonic("RSA", DNSSEC.Algorithm.RSASHA1);
@@ -126,22 +163,56 @@ public class DnsKeyAlgorithm
addMnemonic("NSEC3RSASHA1", DNSSEC.Algorithm.RSA_NSEC3_SHA1);
// Algorithms added by RFC 5702.
// NOTE: these algorithms aren't available in Java 1.4's sunprovider
// implementation (but are in java 1.5's and later).
addAlgorithm(8, new Entry("SHA256withRSA", RSA));
addMnemonic("RSASHA256", 8);
addAlgorithm(DNSSEC.Algorithm.RSASHA256, "SHA256withRSA", RSA);
addMnemonic("RSASHA256", DNSSEC.Algorithm.RSASHA256);
addAlgorithm(10, new Entry("SHA512withRSA", RSA));
addMnemonic("RSASHA512", 10);
addAlgorithm(DNSSEC.Algorithm.RSASHA512, "SHA512withRSA", RSA);
addMnemonic("RSASHA512", DNSSEC.Algorithm.RSASHA512);
// ECC-GOST is not supported by Java 1.8's Sun crypto provider. The
// bouncycastle.org provider, however, does.
// GostR3410-2001-CryptoPro-A is the named curve in the BC provider, but we
// will get the parameters directly.
addAlgorithm(DNSSEC.Algorithm.ECC_GOST, "GOST3411withECGOST3410", ECC_GOST, null);
addMnemonic("ECCGOST", DNSSEC.Algorithm.ECC_GOST);
addMnemonic("ECC-GOST", DNSSEC.Algorithm.ECC_GOST);
addAlgorithm(DNSSEC.Algorithm.ECDSAP256SHA256, "SHA256withECDSA", ECDSA, "secp256r1");
addMnemonic("ECDSAP256SHA256", DNSSEC.Algorithm.ECDSAP256SHA256);
addMnemonic("ECDSA-P256", DNSSEC.Algorithm.ECDSAP256SHA256);
addAlgorithm(DNSSEC.Algorithm.ECDSAP384SHA384, "SHA384withECDSA", ECDSA, "secp384r1");
addMnemonic("ECDSAP384SHA384", DNSSEC.Algorithm.ECDSAP384SHA384);
addMnemonic("ECDSA-P384", DNSSEC.Algorithm.ECDSAP384SHA384);
}
private void addAlgorithm(int algorithm, Entry entry)
private void addAlgorithm(int algorithm, String sigName, int baseType)
{
mAlgorithmMap.put(algorithm, new AlgEntry(algorithm, sigName, baseType));
}
private void addAlgorithm(int algorithm, String sigName, int baseType, String curveName)
{
ECParameterSpec ec_spec = ECSpecFromAlgorithm(algorithm);
if (ec_spec == null) ec_spec = ECSpecFromName(curveName);
if (ec_spec == null) return;
// Check to see if we can get a Signature object for this algorithm.
try {
Signature.getInstance(sigName);
} catch (NoSuchAlgorithmException e) {
// If not, do not add the algorithm.
return;
}
ECAlgEntry entry = new ECAlgEntry(algorithm, sigName, baseType, ec_spec);
mAlgorithmMap.put(algorithm, entry);
}
private void addMnemonic(String m, int alg)
{
// Do not add mnemonics for algorithms that ended up not actually being supported.
if (!mAlgorithmMap.containsKey(alg)) return;
mMnemonicToIdMap.put(m.toUpperCase(), alg);
if (!mIdToMnemonicMap.containsKey(alg))
{
@@ -172,14 +243,77 @@ public class DnsKeyAlgorithm
}
}
private Entry getEntry(int alg)
private AlgEntry getEntry(int alg)
{
return mAlgorithmMap.get(alg);
}
// For curves where we don't (or can't) get the parameters from a standard
// name, we can construct the parameters here. For now, we only do this for
// the ECC-GOST curve.
private ECParameterSpec ECSpecFromAlgorithm(int algorithm)
{
switch (algorithm)
{
case DNSSEC.Algorithm.ECC_GOST:
{
// From RFC 4357 Section 11.4
BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", 16);
BigInteger a = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94", 16);
BigInteger b = new BigInteger("A6", 16);
BigInteger gx = new BigInteger("1", 16);
BigInteger gy = new BigInteger("8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14", 16);
BigInteger n = new BigInteger( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893", 16);
EllipticCurve curve = new EllipticCurve(new ECFieldFp(p), a, b);
return new ECParameterSpec(curve, new ECPoint(gx, gy), n, 1);
}
default:
return null;
}
}
// Fetch the curve parameters from a named curve.
private ECParameterSpec ECSpecFromName(String stdName)
{
try
{
AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");
ECGenParameterSpec ecg_spec = new ECGenParameterSpec(stdName);
ap.init(ecg_spec);
return ap.getParameterSpec(ECParameterSpec.class);
}
catch (NoSuchAlgorithmException e) {
log.info("Elliptic Curve not supported by any crypto provider: " + e.getMessage());
}
catch (InvalidParameterSpecException e) {
log.info("Elliptic Curve " + stdName + " not supported");
}
return null;
}
public String[] supportedAlgMnemonics()
{
Set<Integer> keyset = mAlgorithmMap.keySet();
Integer[] algs = keyset.toArray(new Integer[keyset.size()]);
Arrays.sort(algs);
String[] result = new String[algs.length];
for (int i = 0; i < algs.length; i++)
{
result[i] = mIdToMnemonicMap.get(algs[i]);
}
return result;
}
/**
* Return a Signature object for the specified DNSSEC algorithm.
* @param algorithm The DNSSEC algorithm (by number).
* @return a Signature object.
*/
public Signature getSignature(int algorithm)
{
Entry entry = getEntry(algorithm);
AlgEntry entry = getEntry(algorithm);
if (entry == null) return null;
Signature s = null;
@@ -197,6 +331,62 @@ public class DnsKeyAlgorithm
return s;
}
/**
* Given one of the ECDSA algorithms (ECDSAP256SHA256, etc.) return
* the elliptic curve parameters.
*
* @param algorithm
* The DNSSEC algorithm number.
* @return The calculated JCA ECParameterSpec for that DNSSEC algorithm, or
* null if not a recognized/supported EC algorithm.
*/
public ECParameterSpec getEllipticCurveParams(int algorithm)
{
AlgEntry entry = getEntry(algorithm);
if (entry == null) return null;
if (!(entry instanceof ECAlgEntry)) return null;
ECAlgEntry ec_entry = (ECAlgEntry) entry;
return ec_entry.ec_spec;
}
/**
* Translate a possible algorithm alias back to the original DNSSEC algorithm
* number
*
* @param algorithm
* a DNSSEC algorithm that may be an alias.
* @return -1 if the algorithm isn't recognised, the orignal algorithm number
* if it is.
*/
public int originalAlgorithm(int algorithm)
{
AlgEntry entry = getEntry(algorithm);
if (entry == null) return -1;
return entry.dnssecAlgorithm;
}
/**
* Test if a given algorithm is supported.
*
* @param algorithm The DNSSEC algorithm number.
* @return true if the algorithm is a recognized and supported algorithm or alias.
*/
public boolean supportedAlgorithm(int algorithm)
{
if (mAlgorithmMap.containsKey(algorithm)) return true;
return false;
}
/**
* Given an algorithm mnemonic, convert the mnemonic to a DNSSEC algorithm
* number.
*
* @param s
* The mnemonic string. This is case-insensitive.
* @return -1 if the mnemonic isn't recognized or supported, the algorithm
* number if it is.
*/
public int stringToAlgorithm(String s)
{
Integer alg = mMnemonicToIdMap.get(s.toUpperCase());
@@ -204,6 +394,14 @@ public class DnsKeyAlgorithm
return -1;
}
/**
* Given a DNSSEC algorithm number, return the "preferred" mnemonic.
*
* @param algorithm
* A DNSSEC algorithm number.
* @return The preferred mnemonic string, or null if not supported or
* recognized.
*/
public String algToString(int algorithm)
{
return mIdToMnemonicMap.get(algorithm);
@@ -211,26 +409,11 @@ public class DnsKeyAlgorithm
public int baseType(int algorithm)
{
Entry entry = getEntry(algorithm);
AlgEntry entry = getEntry(algorithm);
if (entry != null) return entry.baseType;
return UNKNOWN;
}
public int standardAlgorithm(int algorithm)
{
switch (baseType(algorithm))
{
case RSA:
return DNSSEC.Algorithm.RSASHA1;
case DSA:
return DNSSEC.Algorithm.DSA;
case DH:
return DNSSEC.Algorithm.DH;
default:
return UNKNOWN;
}
}
public boolean isDSA(int algorithm)
{
return (baseType(algorithm) == DSA);
@@ -243,6 +426,7 @@ public class DnsKeyAlgorithm
switch (baseType(algorithm))
{
case RSA:
{
if (mRSAKeyGenerator == null)
{
mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA");
@@ -270,7 +454,9 @@ public class DnsKeyAlgorithm
pair = mRSAKeyGenerator.generateKeyPair();
break;
}
case DSA:
{
if (mDSAKeyGenerator == null)
{
mDSAKeyGenerator = KeyPairGenerator.getInstance("DSA");
@@ -278,6 +464,49 @@ public class DnsKeyAlgorithm
mDSAKeyGenerator.initialize(keysize);
pair = mDSAKeyGenerator.generateKeyPair();
break;
}
case ECC_GOST:
{
if (mECGOSTKeyGenerator == null)
{
mECGOSTKeyGenerator = KeyPairGenerator.getInstance("ECGOST3410");
}
ECParameterSpec ec_spec = getEllipticCurveParams(algorithm);
try
{
mECGOSTKeyGenerator.initialize(ec_spec);
}
catch (InvalidAlgorithmParameterException e)
{
// Fold the InvalidAlgorithmParameterException into our existing
// thrown exception. Ugly, but requires less code change.
throw new NoSuchAlgorithmException("invalid key parameter spec");
}
pair = mECGOSTKeyGenerator.generateKeyPair();
break;
}
case ECDSA:
{
if (mECKeyGenerator == null)
{
mECKeyGenerator = KeyPairGenerator.getInstance("EC");
}
ECParameterSpec ec_spec = getEllipticCurveParams(algorithm);
try
{
mECKeyGenerator.initialize(ec_spec);
}
catch (InvalidAlgorithmParameterException e)
{
// Fold the InvalidAlgorithmParameterException into our existing
// thrown exception. Ugly, but requires less code change.
throw new NoSuchAlgorithmException("invalid key parameter spec");
}
pair = mECKeyGenerator.generateKeyPair();
break;
}
default:
throw new NoSuchAlgorithmException("Alg " + algorithm);
}

View File

@@ -27,15 +27,8 @@ import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.interfaces.*;
import java.security.spec.*;
import java.util.StringTokenizer;
import javax.crypto.interfaces.DHPrivateKey;
@@ -44,6 +37,7 @@ import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPrivateKeySpec;
import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.DNSSEC.DNSSECException;
import org.xbill.DNS.Name;
import org.xbill.DNS.utils.base64;
@@ -61,9 +55,12 @@ public class DnsKeyConverter
private KeyFactory mRSAKeyFactory;
private KeyFactory mDSAKeyFactory;
private KeyFactory mDHKeyFactory;
private KeyFactory mECKeyFactory;
private DnsKeyAlgorithm mAlgorithms;
public DnsKeyConverter()
{
mAlgorithms = DnsKeyAlgorithm.getInstance();
}
/**
@@ -76,23 +73,19 @@ public class DnsKeyConverter
{
if (pKeyRecord.getKey() == null) return null;
// For now, instead of re-implementing parseRecord (or adding this stuff
// to DNSjava), we will just translate the algorithm back to a standard
// algorithm. Note that this will unnecessarily convert RSAMD5 to RSASHA1.
// Because we have arbitrarily aliased algorithms, we need to possibly
// translate the aliased algorithm back to the actual algorithm.
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
int standard_alg = algs.standardAlgorithm(pKeyRecord.getAlgorithm());
int originalAlgorithm = mAlgorithms.originalAlgorithm(pKeyRecord.getAlgorithm());
if (standard_alg <= 0)
throw new NoSuchAlgorithmException("DNSKEY algorithm "
if (originalAlgorithm <= 0) throw new NoSuchAlgorithmException("DNSKEY algorithm "
+ pKeyRecord.getAlgorithm() + " is unrecognized");
if (pKeyRecord.getAlgorithm() != standard_alg)
if (pKeyRecord.getAlgorithm() != originalAlgorithm)
{
pKeyRecord = new DNSKEYRecord(pKeyRecord.getName(),
pKeyRecord.getDClass(),
pKeyRecord = new DNSKEYRecord(pKeyRecord.getName(), pKeyRecord.getDClass(),
pKeyRecord.getTTL(), pKeyRecord.getFlags(),
pKeyRecord.getProtocol(), standard_alg,
pKeyRecord.getProtocol(), originalAlgorithm,
pKeyRecord.getKey());
}
@@ -132,11 +125,9 @@ public class DnsKeyConverter
public PrivateKey convertEncodedPrivateKey(byte[] key, int algorithm)
{
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
try
{
switch (algs.baseType(algorithm))
switch (mAlgorithms.baseType(algorithm))
{
case DnsKeyAlgorithm.RSA:
return mRSAKeyFactory.generatePrivate(spec);
@@ -146,12 +137,17 @@ public class DnsKeyConverter
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
return null;
}
private int parseInt(String s, int def)
/**
* A simple wrapper for parsing integers; parse failures result in the
* supplied default.
*/
private static int parseInt(String s, int def)
{
try
{
@@ -195,9 +191,8 @@ public class DnsKeyConverter
String[] toks = val.split("\\s", 2);
val = toks[0];
int alg = parseInt(val, -1);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
switch (algs.baseType(alg))
switch (mAlgorithms.baseType(alg))
{
case DnsKeyAlgorithm.RSA:
return parsePrivateRSA(lines);
@@ -205,6 +200,10 @@ public class DnsKeyConverter
return parsePrivateDSA(lines);
case DnsKeyAlgorithm.DH:
return parsePrivateDH(lines);
case DnsKeyAlgorithm.ECC_GOST:
return parsePrivateECDSA(lines, alg);
case DnsKeyAlgorithm.ECDSA:
return parsePrivateECDSA(lines, alg);
default:
throw new IOException("unsupported private key algorithm: " + val);
}
@@ -216,7 +215,7 @@ public class DnsKeyConverter
/**
* @return the value part of an "attribute:value" pair. The value is trimmed.
*/
private String value(String av)
private static String value(String av)
{
if (av == null) return null;
@@ -434,6 +433,60 @@ public class DnsKeyConverter
}
}
/**
* Given the remaining lines in a BIND9-style ECDSA private key, parse the key
* info and translate it into a JCA private key object.
* @param lines The remaining lines in a private key file (after
* @throws NoSuchAlgorithmException
* If elliptic curve is not available.
*/
private PrivateKey parsePrivateECDSA(StringTokenizer lines, int algorithm)
throws NoSuchAlgorithmException
{
BigInteger s = null;
while (lines.hasMoreTokens())
{
String line = lines.nextToken();
if (line == null) continue;
if (line.startsWith("#")) continue;
String val = value(line);
if (val == null) continue;
byte[] data = base64.fromString(val);
if (line.startsWith("PrivateKey: "))
{
s = new BigInteger(1, data);
}
}
if (mECKeyFactory == null)
{
mECKeyFactory = KeyFactory.getInstance("EC");
}
ECParameterSpec ec_spec = mAlgorithms.getEllipticCurveParams(algorithm);
if (ec_spec == null)
{
throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm +
" is not a recognized Elliptic Curve algorithm");
}
KeySpec spec = new ECPrivateKeySpec(s, ec_spec);
try
{
return mECKeyFactory.generatePrivate(spec);
}
catch (InvalidKeySpecException e)
{
e.printStackTrace();
return null;
}
}
/**
* Given a private key and public key, generate the BIND9 style private key
* format.
@@ -452,14 +505,17 @@ public class DnsKeyConverter
{
return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub, alg);
}
else if (priv instanceof ECPrivateKey && pub instanceof ECPublicKey)
{
return generatePrivateEC((ECPrivateKey) priv, (ECPublicKey) pub, alg);
}
return null;
}
/**
* Convert from 'unsigned' big integer to original 'signed format' in Base64
*/
private String b64BigInt(BigInteger i)
private static String b64BigInt(BigInteger i)
{
byte[] orig_bytes = i.toByteArray();
@@ -482,10 +538,9 @@ public class DnsKeyConverter
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
out.println("Private-key-format: v1.2");
out.println("Algorithm: " + algorithm + " (" + algs.algToString(algorithm)
out.println("Algorithm: " + algorithm + " (" + mAlgorithms.algToString(algorithm)
+ ")");
out.print("Modulus: ");
out.println(b64BigInt(key.getModulus()));
@@ -513,12 +568,11 @@ public class DnsKeyConverter
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
DHParameterSpec p = key.getParams();
out.println("Private-key-format: v1.2");
out.println("Algorithm: " + algorithm + " (" + algs.algToString(algorithm)
out.println("Algorithm: " + algorithm + " (" + mAlgorithms.algToString(algorithm)
+ ")");
out.print("Prime(p): ");
out.println(b64BigInt(p.getP()));
@@ -538,12 +592,11 @@ public class DnsKeyConverter
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
DSAParams p = key.getParams();
out.println("Private-key-format: v1.2");
out.println("Algorithm: " + algorithm + " (" + algs.algToString(algorithm)
out.println("Algorithm: " + algorithm + " (" + mAlgorithms.algToString(algorithm)
+ ")");
out.print("Prime(p): ");
out.println(b64BigInt(p.getP()));
@@ -558,4 +611,23 @@ public class DnsKeyConverter
return sw.toString();
}
/**
* Given an elliptic curve key pair, and the actual algorithm (which will
* describe the curve used), return the BIND9-style text encoding.
*/
private String generatePrivateEC(ECPrivateKey priv, ECPublicKey pub, int alg)
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
out.println("Private-key-format: v1.2");
out.println("Algorithm: " + alg + " (" + mAlgorithms.algToString(alg)
+ ")");
out.print("PrivateKey: ");
out.println(b64BigInt(priv.getS()));
return sw.toString();
}
}

View File

@@ -211,6 +211,13 @@ public class DnsSecVerifier
}
}
if (rrset.getTTL() > sigrec.getOrigTTL())
{
log.fine("RRset's TTL is greater than the Signature's orignal TTL");
if (reasons != null) reasons.add("RRset TTL greater than RRSIG origTTL");
return false;
}
return true;
}
@@ -256,6 +263,12 @@ public class DnsSecVerifier
sig = SignUtils.convertDSASignature(sig);
}
if (sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP256SHA256 ||
sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP384SHA384)
{
sig = SignUtils.convertECDSASignature(sig);
}
if (!signer.verify(sig))
{
if (reasons != null) reasons.add("Signature failed to verify cryptographically");
@@ -283,7 +296,6 @@ public class DnsSecVerifier
*
* @return true if the set verified, false if it did not.
*/
@SuppressWarnings("unchecked")
public boolean verify(RRset rrset)
{
boolean result = mVerifyAllSigs ? true : false;

View File

@@ -33,12 +33,7 @@ import java.util.List;
import java.util.ListIterator;
import java.util.logging.Logger;
import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.Name;
import org.xbill.DNS.RRSIGRecord;
import org.xbill.DNS.RRset;
import org.xbill.DNS.Record;
import org.xbill.DNS.Type;
import org.xbill.DNS.*;
import org.xbill.DNS.utils.hexdump;
/**
@@ -58,7 +53,7 @@ public class JCEDnsSecSigner
private DnsKeyConverter mKeyConverter;
private boolean mVerboseSigning = false;
private Logger log;
private Logger log = Logger.getLogger(this.getClass().toString());
public JCEDnsSecSigner()
{
@@ -197,6 +192,12 @@ public class JCEDnsSecSigner
DSAPublicKey pk = (DSAPublicKey) pair.getPublic();
sig = SignUtils.convertDSASignature(pk.getParams(), sig);
}
// Convert to RFC 6605, etc format
if (pair.getDNSKEYAlgorithm() == DNSSEC.Algorithm.ECDSAP256SHA256 ||
pair.getDNSKEYAlgorithm() == DNSSEC.Algorithm.ECDSAP384SHA384)
{
sig = SignUtils.convertECDSASignature(pair.getDNSKEYAlgorithm(), sig);
}
RRSIGRecord sigrec = SignUtils.generateRRSIG(sig, presig);
if (mVerboseSigning)
{

View File

@@ -242,14 +242,8 @@ public class ProtoNSEC3
sb.append(' ');
String nextstr = (next == null) ? "(null)" : b32.toString(next);
sb.append(nextstr);
int[] types = getTypes();
for (int i = 0; i < types.length; i++)
{
sb.append(" ");
sb.append(Type.string(types[i]));
}
if (originalOwner != null) sb.append(" ; " + originalOwner);
sb.append(' ');
sb.append(typemap.toString());
return sb.toString();
}

View File

@@ -429,6 +429,128 @@ public class SignUtils
return sig;
}
// Given one of the ECDSA algorithms determine the "length", which is the
// length, in bytes, of both 'r' and 's' in the ECDSA signature.
private static int ecdsaLength(int algorithm) throws SignatureException
{
switch (algorithm)
{
case DNSSEC.Algorithm.ECDSAP256SHA256: return 32;
case DNSSEC.Algorithm.ECDSAP384SHA384: return 48;
default:
throw new SignatureException("Algorithm " + algorithm +
" is not a supported ECDSA signature algorithm.");
}
}
/**
* Convert a JCE standard ECDSA signature (which is a ASN.1 encoding) into a
* standard DNS signature.
*
* The format of the ASN.1 signature is
*
* ASN1_SEQ . seq_length . ASN1_INT . r_length . R . ANS1_INT . s_length . S
*
* where R and S may have a leading zero byte if without it the values would
* be negative.
*
* The format of the DNSSEC signature is just R . S where R and S are both
* exactly "length" bytes.
*
* @param signature
* The output of a ECDSA signature object.
* @return signature data formatted for use in DNSSEC.
* @throws SignatureException if the ASN.1 encoding appears to be corrupt.
*/
public static byte[] convertECDSASignature(int algorithm, byte[] signature)
throws SignatureException
{
int exp_length = ecdsaLength(algorithm);
byte[] sig = new byte[exp_length * 2];
if (signature[0] != ASN1_SEQ || signature[2] != ASN1_INT)
{
throw new SignatureException("Invalid ASN.1 signature format: expected SEQ, INT");
}
int r_len = signature[3];
int r_pos = 4;
if (signature[r_pos + r_len] != ASN1_INT)
{
throw new SignatureException("Invalid ASN.1 signature format: expected SEQ, INT, INT");
}
int s_pos = r_pos + r_len + 2;
int s_len = signature[r_pos + r_len + 1];
// Adjust for leading zeros on both R and S
if (signature[r_pos] == 0) {
r_pos++;
r_len--;
}
if (signature[s_pos] == 0) {
s_pos++;
s_len--;
}
System.arraycopy(signature, r_pos, sig, 0 + (exp_length - r_len), r_len);
System.arraycopy(signature, s_pos, sig, exp_length + (exp_length - s_len), s_len);
return sig;
}
/**
* Convert a DNS standard ECDSA signature (defined in RFC 6605) into a
* JCE standard ECDSA signature, which is encoded in ASN.1.
*
* The format of the ASN.1 signature is
*
* ASN1_SEQ . seq_length . ASN1_INT . r_length . R . ANS1_INT . s_length . S
*
* where R and S may have a leading zero byte if without it the values would
* be negative.
*
* The format of the DNSSEC signature is just R . S where R and S are both
* exactly "length" bytes.
*
* @param signature
* The binary signature data from an RRSIG record.
* @return signature data that may be used in a JCE Signature object for
* verification purposes.
*/
public static byte[] convertECDSASignature(byte[] signature)
{
byte r_src_pos, r_src_len, r_pad, s_src_pos, s_src_len, s_pad, len;
r_src_len = s_src_len = (byte) (signature.length / 2);
r_src_pos = 0; r_pad = 0;
s_src_pos = (byte) (r_src_pos + r_src_len); s_pad = 0;
len = (byte) (6 + r_src_len + s_src_len);
if (signature[r_src_pos] < 0) {
r_pad = 1; len++;
}
if (signature[s_src_pos] < 0) {
s_pad = 1; len++;
}
byte[] sig = new byte[len];
byte pos = 0;
sig[pos++] = ASN1_SEQ;
sig[pos++] = (byte) (len - 2);
sig[pos++] = ASN1_INT;
sig[pos++] = (byte) (r_src_len + r_pad);
pos += r_pad;
System.arraycopy(signature, r_src_pos, sig, pos, r_src_len);
pos += r_src_len;
sig[pos++] = ASN1_INT;
sig[pos++] = (byte) (s_src_len + s_pad);
pos += s_pad;
System.arraycopy(signature, s_src_pos, sig, pos, s_src_len);
return sig;
}
/**
* This is a convenience routine to help us classify records/RRsets.
*

View File

@@ -21,6 +21,7 @@ import org.xbill.DNS.Type;
public class TypeMap
{
private static final Integer[] integerArray = new Integer[0];
private static final byte[] emptyBitmap = new byte[0];
private Set<Integer> typeSet;
@@ -47,6 +48,9 @@ public class TypeMap
return typeSet.contains(type);
}
/**
* Given an array of DNS type code, construct a TypeMap object.
*/
public static TypeMap fromTypes(int[] types)
{
TypeMap m = new TypeMap();
@@ -68,12 +72,12 @@ public class TypeMap
int m = 0;
TypeMap typemap = new TypeMap();
int map_number;
int page;
int byte_length;
while (m < map.length)
{
map_number = map[m++];
page = map[m++];
byte_length = map[m++];
for (int i = 0; i < byte_length; i++)
@@ -82,7 +86,7 @@ public class TypeMap
{
if ((map[m + i] & (1 << (7 - j))) != 0)
{
typemap.set(map_number * 8 + j);
typemap.set((page << 8) + (i * 8) + j);
}
}
}
@@ -92,6 +96,21 @@ public class TypeMap
return typemap;
}
/**
* Given list of type mnemonics, construct a TypeMap object.
*/
public static TypeMap fromString(String types)
{
TypeMap typemap = new TypeMap();
for (String type : types.split("\\s+"))
{
typemap.set(Type.value(type));
}
return typemap;
}
/** @return the normal string representation of the typemap. */
public String toString()
{
@@ -102,7 +121,7 @@ public class TypeMap
for (int i = 0; i < types.length; i++)
{
sb.append(" ");
if (i > 0) sb.append(" ");
sb.append(Type.string(types[i]));
}
@@ -140,6 +159,8 @@ public class TypeMap
{
int[] types = getTypes();
if (types.length == 0) return emptyBitmap;
Arrays.sort(types);
int mapbase = -1;
@@ -149,7 +170,7 @@ public class TypeMap
for (int i = 0; i < types.length; i++)
{
int base = types[i] >> 8;
int base = (types[i] >> 8) & 0xFF;
if (base == mapbase) continue;
if (mapstart >= 0)
{

View File

@@ -136,7 +136,8 @@ public class ZoneVerifier
return true;
}
for (Iterator i = rrset.rrs(); i.hasNext(); )
Iterator i = (rr instanceof RRSIGRecord) ? rrset.sigs() : rrset.rrs();
for ( ; i.hasNext(); )
{
Record record = (Record) i.next();
if (rr.equals(record)) return false;
@@ -276,6 +277,11 @@ public class ZoneVerifier
// All RRs at the zone apex are normal
if (n.equals(mZoneName)) return NodeType.NORMAL;
// If the node is not below the zone itself, we will treat it as glue (it is really junk).
if (!n.subdomain(mZoneName))
{
return NodeType.GLUE;
}
// If the node is below a zone cut (either a delegation or DNAME), it is
// glue.
if (last_cut != null && n.subdomain(last_cut) && !n.equals(last_cut))