Compare commits

...

5 Commits

Author SHA1 Message Date
1406cd2e68
remove ECC-GOST, add new DS algorithms (#18) 2024-04-13 11:39:18 -04:00
1727d7c7d8
Cli improvements (#17)
* add config file processing, refactor CLIBase some

* fix algorithm aliases with key generation

* Refactor to remove CLIState et al, move CLI common statics to new Utils

* only use usage() for help, otherwise fail()

* add a universal command line client, build a one-jar to use it.

* bump the version

* update ChangeLog, README, README.TODO, minor fixes

* undo overzealous find/replace. sigh.

* fix use_large_exponent logic in KeyGen

* more fixes, minor improvements
2024-04-07 21:12:56 -04:00
2876649a4e
EdDSA, sonarlint fixes, and cleanup (#16)
* update commons-cli, dnsjava, remove gradle, set to java 17

* use baseAlgorithm enum instead of static ints

* sonarlint changes

* sonarlint/formatting for SignUtils

* sonarlint, formatting for RecordComparitor and JCEDnsSecSigner

* update a few defaults

* redo the SignZone logic around finding keys; more output for it.

* refactor getVerifier()

* sonarlint and formatting for the rest

* use SunEC for the algs 15, 16; support alg 16 finally

* address my self-review comments
2024-03-30 22:21:32 -04:00
6118ae718e
Fix issue 14 (#15)
handle duplicate key tags, gen duplicate key tags, other minor cleanup
2024-03-25 00:38:47 -04:00
5fef1dcf24 forgot to commit the version update 2023-07-24 08:22:28 -04:00
50 changed files with 2581 additions and 2809 deletions

6
.gitattributes vendored
View File

@ -1,6 +0,0 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
build build
bin/main bin/main
dist/
.classpath .classpath
.project .project
.gradle .gradle

View File

@ -1,3 +1,46 @@
2024-04-13 David Blacka <david@blacka.com>
* Remove support for ECC_GOST
* Create a new DSAlgorithm class, move DS creation into that
* Add support for DS algorithms 3 and 4 -- bouncycastle crypto
provider used for DS algoirthm 3 (GOST R 34.11-94)
* Moved support for loading the bouncycastle provider to the new
DSAlgorithm class
2024-04-07 David Blacka <david@blacka.com>
* Released version 0.20
* Removed support for Gradle builds since the gradle config was
out-of-date
* Requires Java 15 or later for EdDSA support
* Supports DNSSEC algorithm 16 using the SunEC provider
* Supports a java properties-formatted config file. See
jdnssec-tools.properties.example for an example
* Updated to dnsjava 3.5.3
* Updated to commons-cli 1.6.0
* Added a "one-jar" distribution method, and a "univeral" CLI to
use with it.
* Formatting and linter suggestions
2024-03-25 David Blacka <davidb@verisign.com>
* Released version 0.19
* Handle duplicate key tags
* jdnssec-keygen can now attempt to generate keys with specified key tags
2023-07-24 David Blacka <davidb@verisign.com>
* Released version 0.17.1
* Add a `-t` option to jdnssec-verifyzone: verify using specified time.
2022-09-21 David Blacka <davidb@verisign.com>
* Released version 0.17
* Updated to dnsjava 3.5.1
* Formatting and linter suggestions
* Use slf4j instead of log4j.
* jdnssec-dstool can now generate CDS records
2019-07-23 David Blacka <davidb@verisign.com> 2019-07-23 David Blacka <davidb@verisign.com>
* Released version 0.16 * Released version 0.16

View File

@ -11,5 +11,3 @@ This bit of code has been around since approximately 2005, and has been in "mini
* allowing for an external sort once the data is shown to be larger than X, and/or * allowing for an external sort once the data is shown to be larger than X, and/or
* allowing for a memory-constrained internal sort that uses disk, and/or, * allowing for a memory-constrained internal sort that uses disk, and/or,
* figuring out how to let the JVM use *a lot* of memory. * figuring out how to let the JVM use *a lot* of memory.
* Add support for algorithm 16, perhaps refactor algorithm 15 support using bouncycastle.
* Note that our current dnsjava version, 3.5.1 has some support, although it isn't clear if it has sign/verify support.

View File

@ -2,15 +2,19 @@
* <https://github.com/dblacka/jdnssec-tools/wiki> * <https://github.com/dblacka/jdnssec-tools/wiki>
Author: David Blacka (davidb@verisign.com) 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. 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 (<https://github.com/dnsjava/dnsjava>), the Jakarta Commons CLI and Logging libraries (<https://commons.apache.org/proper/commons-cli>), slf4j (<https://www.slf4j.org>), and Sun's Java Cryptography extensions. A copy of each of these libraries is included in the distribution. These tools depend upon DNSjava (<https://github.com/dnsjava/dnsjava>), the Jakarta Commons CLI and Logging libraries (<https://commons.apache.org/proper/commons-cli>), slf4j (<https://www.slf4j.org>), and Sun's Java Cryptography extensions. A copy of each of these libraries is included in the distribution.
See the "licenses" directory for the licensing information of this package and the other packages that are distributed with it. See the "[licenses](https://github.com/dblacka/jdnssec-tools/tree/master/licenses)" directory for the licensing information of this package and the other packages that are distributed with it.
Getting started: ## Getting Started
### Using the binary distribution
The binary distributions can be downloaded from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page. To use it;
1. Unpack the binary distribution: 1. Unpack the binary distribution:
@ -21,9 +25,11 @@ Getting started:
cd java-dnssec-tools-x.x.x cd java-dnssec-tools-x.x.x
./bin/jdnssec-signzone -h ./bin/jdnssec-signzone -h
Building from source: ### Building from source
1. Unpack the source distribution, preferably into the same directory that the binary distribution was unpacked. There is a source distribution also downloadable from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page, but this should work with a clone of this repo.
1. (If downloaded) 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 tar zxvf java-dnssec-tools-x.x.x-src.tar.gz
@ -32,17 +38,19 @@ Building from source:
ant 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. 4. You can build the distribution tarballs with 'ant dist', although the main `ant` build command will have built the primary jar file.
5. Alternatively, build the project using gradle:
gradlew clean
gradlew assemble -i
The resulting jar file gets generated in build/libs.
The source for this project is available in git on github: <https://github.com/dblacka/jdnssec-tools> The source for this project is available in git on github: <https://github.com/dblacka/jdnssec-tools>
### Using the one-jar distribution
As of version 0.20, there is a one-jar (aka an executable jar) as part of the distribution. It can also be downloaded from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page.
1. Fetch the one-jar distribution.
2. Invoke with `java -jar jdnssec-tools-x.x.x.jar`
java -jar jdnssec-tools-x.x.x.jar signzone -h
--- ---
Questions or comments may be directed to the author (mailto:davidb@verisign.com), or by creating issues in the [github issue tracker](https://github.com/dblacka/jdnssec-tools/issues). Questions or comments may be directed to the author (<mailto:davidb@verisign.com>), or by creating issues in the [github issue tracker](https://github.com/dblacka/jdnssec-tools/issues).

View File

@ -1 +1 @@
version=0.17 version=0.20

View File

@ -1,19 +0,0 @@
#! /bin/sh
thisdir=`dirname $0`
basedir=`cd $thisdir/..; pwd`
ulimit_max=`ulimit -H -n`
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
CLASSPATH=$CLASSPATH:$basedir/build/classes
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.DSTool "$@"

View File

@ -1,19 +0,0 @@
#! /bin/sh
thisdir=`dirname $0`
basedir=`cd $thisdir/..; pwd`
ulimit_max=`ulimit -H -n`
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
CLASSPATH=$CLASSPATH:$basedir/build/classes
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.KeyGen "$@"

View File

@ -1,19 +0,0 @@
#! /bin/sh
thisdir=`dirname $0`
basedir=`cd $thisdir/..; pwd`
ulimit_max=`ulimit -H -n`
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
CLASSPATH=$CLASSPATH:$basedir/build/classes
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.KeyInfoTool "$@"

View File

@ -1,19 +0,0 @@
#! /bin/sh
thisdir=`dirname $0`
basedir=`cd $thisdir/..; pwd`
ulimit_max=`ulimit -H -n`
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
CLASSPATH=$CLASSPATH:$basedir/build/classes
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.SignKeyset "$@"

View File

@ -1,19 +0,0 @@
#! /bin/sh
thisdir=`dirname $0`
basedir=`cd $thisdir/..; pwd`
ulimit_max=`ulimit -H -n`
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
CLASSPATH=$CLASSPATH:$basedir/build/classes
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.SignZone "$@"

View File

@ -1,19 +0,0 @@
#! /bin/sh
thisdir=`dirname $0`
basedir=`cd $thisdir/..; pwd`
ulimit_max=`ulimit -H -n`
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
CLASSPATH=$CLASSPATH:$basedir/build/classes
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.VerifyZone "$@"

View File

@ -1,19 +0,0 @@
#! /bin/sh
thisdir=`dirname $0`
basedir=`cd $thisdir/..; pwd`
ulimit_max=`ulimit -H -n`
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
CLASSPATH=$CLASSPATH:$basedir/build/classes
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.ZoneFormat "$@"

22
bin/jdnssec-tools Executable file
View File

@ -0,0 +1,22 @@
#! /bin/sh
thisdir=$(dirname $0)
basedir=$(cd $thisdir/.. || exit; pwd)
ulimit_max=$(ulimit -H -n)
if [ $ulimit_max != "unlimited" ]; then
ulimit -n $ulimit_max
fi
# set the classpath
for i in "$basedir"/lib/*.jar "$basedir"/lib/*.zip "$basedir"/build/libs/*.jar; do
if ! [ -f $i ]; then continue; fi
if [ -z "$CLASSPATH" ]; then
CLASSPATH=$i
else
CLASSPATH="$CLASSPATH":"$i"
fi
done
export CLASSPATH
exec java com.verisignlabs.dnssec.cl.CLI "$@"

View File

@ -1,25 +0,0 @@
/**
Declares dependencies for Jdnssec-tools
**/
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
jar {
baseName = 'jdnssec-tools'
version = '0.17'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile fileTree(dir: 'lib', include: '*.jar')
}

View File

@ -1,3 +1,3 @@
#build.compiler=jikes
#build.compiler=modern
build.deprecation=true build.deprecation=true
build.debug=false
build.java_version=17

View File

@ -10,18 +10,20 @@
--> -->
<project default="compile" basedir="."> <project default="build" basedir=".">
<property file="build.properties" /> <property file="build.properties" />
<property file="VERSION" /> <property file="VERSION" />
<property name="sectools-distname" value="jdnssec-tools-${version}" />
<property name="build.dir" value="build" /> <property name="build.dir" value="build" />
<property name="build.dest" value="${build.dir}/classes" /> <property name="build.dest" value="${build.dir}/classes" />
<property name="build.lib.dest" value="${build.dir}/libs" /> <property name="build.lib.dest" value="${build.dir}/libs" />
<property name="build.src" value="src/main/java" /> <property name="build.src" value="src/main/java" />
<property name="dist.dir" value="dist"/>
<property name="dist.name" value="jdnssec-tools-${version}" />
<property name="packages" value="com.verisignlabs.dnssec.*" /> <property name="packages" value="com.verisignlabs.dnssec.*" />
<property name="doc.dir" value="docs" /> <property name="doc.dir" value="docs" />
<property name="javadoc.dest" value="${doc.dir}/javadoc" /> <property name="javadoc.dest" value="${doc.dir}/javadoc" />
@ -33,6 +35,7 @@
<pathelement location="${build.dest}" /> <pathelement location="${build.dest}" />
<fileset dir="${lib.dir}" includes="*.jar,*.zip" /> <fileset dir="${lib.dir}" includes="*.jar,*.zip" />
</path> </path>
<property name="project.classpath" refid="project.classpath" /> <property name="project.classpath" refid="project.classpath" />
<target name="prepare-src"> <target name="prepare-src">
@ -40,25 +43,40 @@
<mkdir dir="${build.lib.dest}" /> <mkdir dir="${build.lib.dest}" />
</target> </target>
<target name="sectools" depends="prepare-src" > <target name="compile" depends="prepare-src" >
<javac srcdir="${build.src}" <javac srcdir="${build.src}"
destdir="${build.dest}" destdir="${build.dest}"
classpathref="project.classpath" classpathref="project.classpath"
deprecation="true" deprecation="${build.deprecation}"
includeantruntime="false" includeantruntime="false"
includes="com/verisignlabs/dnssec/" includes="com/verisignlabs/dnssec/"
source="11" debug="${build.debug}"
target="11" /> release="${build.java_version}" />
</target> </target>
<target name="sectools-jar" depends="usage,sectools"> <target name="build-jar" depends="usage, compile">
<jar jarfile="${build.lib.dest}/jdnssec-tools.jar" <jar jarfile="${build.lib.dest}/jdnssec-tools.jar"
basedir="${build.dest}" basedir="${build.dest}"
includes="com/verisignlabs/dnssec/" /> includes="com/verisignlabs/dnssec/" />
</target> </target>
<target name="compile" <target name="build"
depends="usage,sectools-jar"> depends="usage,build-jar">
</target>
<target name="build-onejar" depends="compile">
<jar destfile="${dist.dir}/${dist.name}.jar">
<zipfileset dir="${build.dest}" includes="**/*.class" />
<zipfileset src="${lib.dir}/dnsjava-3.5.3.jar" />
<zipfileset src="${lib.dir}/commons-cli-1.6.0.jar" />
<zipfileset src="${lib.dir}/slf4j-api-1.7.36.jar" />
<zipfileset src="${lib.dir}/slf4j-simple-1.7.36.jar" />
<manifest>
<attribute name="Main-Class"
value="com.verisignlabs.dnssec.cl.CLI" />
</manifest>
</jar>
</target> </target>
<target name="javadoc" depends="usage"> <target name="javadoc" depends="usage">
@ -75,16 +93,21 @@
</javadoc> </javadoc>
</target> </target>
<target name="clean" depends="usage"> <target name="clean" depends="usage">
<delete dir="${build.dest}" /> <delete dir="${build.dest}" />
<delete dir="${build.lib.dest}" /> <delete dir="${build.lib.dest}" />
<delete dir="${dist.dir}" />
</target> </target>
<target name="sectools-dist-prepare" depends="usage, compile, javadoc"> <target name="dist-clean" depends="usage">
<mkdir dir="${sectools-distname}" /> <delete dir="${dist.name}" />
</target>
<copy todir="${sectools-distname}"> <target name="dist-prepare" depends="usage, build, javadoc">
<mkdir dir="${dist.dir}" />
<mkdir dir="${dist.name}" />
<copy todir="${dist.name}">
<fileset dir="."> <fileset dir=".">
<include name="bin/jdnssec-*" /> <include name="bin/jdnssec-*" />
<include name="lib/*.jar" /> <include name="lib/*.jar" />
@ -99,37 +122,33 @@
</fileset> </fileset>
</copy> </copy>
<copy todir="${sectools-distname}/lib"> <copy todir="${dist.name}/lib">
<fileset dir="${build.lib.dest}"> <fileset dir="${build.lib.dest}">
<include name="*.jar" /> <include name="*.jar" />
</fileset> </fileset>
</copy> </copy>
</target> </target>
<target name="sectools-dist-clean">
<delete dir="${sectools-distname}" />
</target>
<patternset id="exec.files"> <patternset id="exec.files">
<include name="${sectools-distname}/bin/jdnssec-*" /> <include name="${dist.name}/bin/jdnssec-*" />
</patternset> </patternset>
<patternset id="src.files"> <patternset id="src.files">
<include name="${sectools-distname}/src/" /> <include name="${dist.name}/src/" />
<include name="${sectools-distname}/build.xml" /> <include name="${dist.name}/build.xml" />
<include name="${sectools-distname}/build.properties" /> <include name="${dist.name}/build.properties" />
</patternset> </patternset>
<patternset id="bin.files"> <patternset id="bin.files">
<include name="${sectools-distname}/doc/" /> <include name="${dist.name}/doc/" />
<include name="${sectools-distname}/lib/" /> <include name="${dist.name}/lib/" />
<include name="${sectools-distname}/licenses/" /> <include name="${dist.name}/licenses/" />
<include name="${sectools-distname}/VERSION" /> <include name="${dist.name}/VERSION" />
<include name="${sectools-distname}/README" /> <include name="${dist.name}/README" />
</patternset> </patternset>
<target name="sectools-bin-dist" depends="sectools-dist-prepare"> <target name="bin-dist" depends="dist-prepare">
<tar destfile="${sectools-distname}.tar.gz" compression="gzip"> <tar destfile="${dist.dir}/${dist.name}.tar.gz" compression="gzip">
<tarfileset mode="755" dir="."> <tarfileset mode="755" dir=".">
<patternset refid="exec.files" /> <patternset refid="exec.files" />
</tarfileset> </tarfileset>
@ -139,34 +158,28 @@
</tar> </tar>
</target> </target>
<target name="sectools-src-dist" depends="sectools-dist-prepare"> <target name="src-dist" depends="dist-prepare">
<tar destfile="${sectools-distname}-src.tar.gz" <tar destfile="${dist.dir}/${dist.name}-src.tar.gz" compression="gzip">
compression="gzip">
<tarfileset dir="."> <tarfileset dir=".">
<patternset refid="src.files" /> <patternset refid="src.files" />
</tarfileset> </tarfileset>
</tar> </tar>
</target> </target>
<target name="sectools-dist" <target name="dist"
depends="sectools-bin-dist,sectools-src-dist, sectools-dist-clean"> depends="bin-dist, src-dist, build-onejar, dist-clean">
</target>
<target name="dist" depends="sectools-dist">
<echo message="do not forget to tag the release:" />
<echo message=" svn-tag ${version}" />
</target> </target>
<target name="usage"> <target name="usage">
<echo message=" " /> <echo message=" " />
<echo message="SECTOOLS v. ${version} Build System" /> <echo message="jdnssec-tools v. ${version} Build System" />
<echo message="--------------------------------" /> <echo message="--------------------------------" />
<echo message="Available Targets:" /> <echo message="Available Targets:" />
<echo message=" compile (default) - compiles the source code, creates jar" /> <echo message=" build (default) - compiles the source code, creates main jar" />
<echo message=" javadoc - create javadoc from source" /> <echo message=" javadoc - create javadoc from source" />
<echo message=" clean - delete class files" /> <echo message=" clean - delete class files" />
<echo message=" dist - package it up" /> <echo message=" dist - package it up" />
<echo message=" onejar - build the executable jar" />
<echo message=" usage - this help message" /> <echo message=" usage - this help message" />
<echo message=" " /> <echo message=" " />
</target> </target>

Binary file not shown.

View File

@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
gradlew vendored
View File

@ -1,185 +0,0 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
gradlew.bat vendored
View File

@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,93 @@
# An example properties file for jdnssec-tools
# Properties may be be scoped by the tool name, which is the name minus "jdnssec-"
# If unscoped, the same named property will be used by multiple tools
# Common properties
# log_level = warning
# verbose = true # same as log_level = fine (true) or log_level = warning (false)
# multiline = false
# algorithm aliasing is <scope>.alias.<new-mnemonic> = <orig-alg-id>:<alias-alg-id>
# alias.NEWALG = 8:100
# jdnssec-dstool properties
## These are all equivalent. Unscoped properties might apply to other tools
# dstool.digest_algorithm = 4
# digest_algorithm = 4 # applies to jdnssec-signzone, too
# dstool.digest_id = 4
# jdnssec-keygen properties
# keygen.use_large_exponent = true
# keygen.key_directory = .
# key_directory = /path/to/dnskey_files # applies to jdnssec-sign*
# keygen.algorithm = ED448
# keygen.keylength = 2048
# keygen.keylen = 2048 # same thing
# keygen.ttl = 3600
# jdnssec-keyinfotool
# no additional keys
# jdnssec-signkeyset
# signkeyset.verify = false
# signkeyset.key_directory = .
# signkeyset.start = -300
# signkeyset.inception = 1712424863
# signkeyset.expire = +604800
# jdnssec-signrrset
# signrrset.verify_signatures = false
# signrrset.verify = false # same thing
# signrrset.key_directory = .
# signrrset.start = now
# signrrset.inception = now # same thing
# signrrset.expire = now+3600
# jdnssec-signzone
# signzone.verify_signatures = false
# signzone.verify = false # same thing
# signzone.use_nsec3 = false
# signzone.nsec3 = false # same thing
# signzone.use_opt_out = false
# signzone.opt_out = false # same thing
# signzone.verbose_signing = false
# signzone.fully_sign_keyset = false
# signzone.fully_sign = false # same thing
# signzone.key_directory = .
# signzone.keydir = . # same thing
# signzone.start = now
# signzone.inception = now
# signzone.expire = now+3600
# signzone.nsec3_salt = DEADBEEF
# signzone.salt = DEADBEEF # same thing
# signzone.nsec3_random_salt_length = 6
# signzone.nsec3_salt_length = 6 # same thing
# signzone.random_salt_length = 6 # same thing
# signzone.nsec3_iterations = 0
# signzone.iterations = 0 # same thing
# signzone.digest_algorithm = 4
# signzone.digest_id = 4 # same thing
# signzone.nsec3param_ttl = 86400
# signzone.include_names_file = /path/to/include-names
# signzone.include_names = /path/to/include-names # same thing
# jdnssec-verifyzone
# verifyzone.ignore_time = false
# verifyzone.ignore_duplicate_rrs = false
# verifyzone.ignore_duplicates = false # same thing
# verifyzone.start_fudge = 0
# verifyzone.expire_fudge = 0
# verifyzone.current_time = now
# jdnssec-zoneformat
# zoneformat.assign_nsec3_owners = false
# zoneformat.assign_owners = false # same thing

Binary file not shown.

BIN
lib/commons-cli-1.6.0.jar Normal file

Binary file not shown.

Binary file not shown.

View File

@ -17,11 +17,11 @@
package com.verisignlabs.dnssec.cl; package com.verisignlabs.dnssec.cl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.text.SimpleDateFormat; import java.util.Properties;
import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;
import java.util.logging.Formatter; import java.util.logging.Formatter;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.Level; import java.util.logging.Level;
@ -48,8 +48,19 @@ import com.verisignlabs.dnssec.security.DnsKeyAlgorithm;
* subclass variant of the CLIState and call run(). * subclass variant of the CLIState and call run().
*/ */
public abstract class CLBase { public abstract class CLBase {
protected static Logger staticLog = Logger.getLogger(CLBase.class.getName()); protected Logger log = Logger.getLogger(this.getClass().toString());
protected Logger log; protected Options opts;
protected String name;
protected String usageStr;
protected Properties props;
protected CommandLine cli;
protected CLBase(String name, String usageStr) {
this.name = name;
this.usageStr = usageStr;
setup();
}
/** /**
* This is a very simple log formatter that simply outputs the log level and * This is a very simple log formatter that simply outputs the log level and
@ -70,253 +81,319 @@ public abstract class CLBase {
} }
} }
/** This is the base set of command line options provided to all subclasses. */
private void setupCommonOptions() {
// Set up the standard set of options that all jdnssec command line tools will
// implement.
// boolean options
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("m", "multiline", false,
"Output DNS records using 'multiline' format");
opts.addOption(Option.builder("l").longOpt("log-level").argName("level").hasArg()
.desc("set the logging level with either java.util.logging levels, or 0-6").build());
opts.addOption(Option.builder("v").longOpt("verbose").desc(
"set as verbose (log-level = fine)").build());
opts.addOption(Option.builder("c").longOpt("config").argName("file").hasArg()
.desc("configuration file (format: java properties)").build());
opts.addOption(Option.builder("A").hasArg().argName("alias:original:mnemonic").longOpt("alg-alias")
.desc("Define an alias for an algorithm").build());
}
/** /**
* This is a base class for command line parsing state. Subclasses should * This is an overridable method for subclasses to add their own command line
* override setupOptions and processOptions. * options.
*/ */
public static class CLIStateBase { protected abstract void setupOptions();
protected Options opts;
protected String usageStr;
/** /**
* The base constructor. This will setup the command line options. * Initialize the command line options
* */
* @param usage public void setup() {
* The command line usage string (e.g., opts = new Options();
* "jdnssec-foo [..options..] zonefile") setupCommonOptions();
*/ setupOptions();
public CLIStateBase(String usage) { }
usageStr = usage;
setup(); /**
* This is the main method for parsing the command line arguments. Subclasses
* generally override processOptions() rather than this method. This method
* creates the parsing objects and processes the common options.
*
* @param args The command line arguments.
*/
public void parseCommandLine(String[] args) {
String[] logLevelOptionKeys = { "log_level", "log-level" };
String[] multilineOptionKeys = { "multiline" };
CommandLineParser parser = new DefaultParser();
try {
cli = parser.parse(opts, args);
} catch (UnrecognizedOptionException e) {
fail("unknown option encountered: " + e.getMessage());
} catch (AlreadySelectedException e) {
fail("mutually exclusive options have been selected:\n " + e.getMessage());
} catch (ParseException e) {
fail("unable to parse command line: " + e);
} }
/** This is the base set of command line options provided to all subclasses. */ if (cli.hasOption('h')) {
private void setup() { usage();
// Set up the standard set of options that all jdnssec command line tools will
// implement.
opts = new Options();
// boolean options
opts.addOption("h", "help", false, "Print this message.");
opts.addOption("m", "multiline", false,
"Output DNS records using 'multiline' format");
opts.addOption(Option.builder("v").longOpt("verbose").argName("level").optionalArg(true).desc(
"verbosity level -- 0 is silence, 3 is info, 5 is debug information, 6 is trace information. default is level 2 (warning)")
.build());
opts.addOption(Option.builder("A").hasArg().argName("alias:original:mnemonic").longOpt("alg-alias")
.desc("Define an alias for an algorithm").build());
setupOptions(opts);
} }
/** String loadedConfig = loadConfig(cli.getOptionValue('c'));
* This is an overridable method for subclasses to add their own command
* line options. Logger rootLogger = Logger.getLogger("");
*
* @param opts // we set log level with both --log-level and -v/--verbose.
* the options object to add (via OptionBuilder, typically) new String logLevel = cliOption("log-level", logLevelOptionKeys, null);
* options to. if (logLevel == null) {
*/ logLevel = cli.hasOption("v") ? "fine" : "warning";
protected void setupOptions(Options opts) { }
// Subclasses generally override this. setLogLevel(rootLogger, logLevel);
for (Handler h : rootLogger.getHandlers()) {
h.setLevel(rootLogger.getLevel());
h.setFormatter(new BareLogFormatter());
} }
/** if (loadedConfig != null) {
* This is the main method for parsing the command line arguments. log.info("Loaded config file: " + loadedConfig);
* Subclasses generally override processOptions() rather than this method. }
* This method create the parsing objects and processes the standard
* options.
*
* @param args
* The command line arguments.
* @throws ParseException
*/
public void parseCommandLine(String[] args) throws ParseException {
CommandLineParser parser = new DefaultParser();
CommandLine cli = parser.parse(opts, args);
if (cli.hasOption('h')) { if (cliBooleanOption("m", multilineOptionKeys, false)) {
usage(); org.xbill.DNS.Options.set("multiline");
}
processAliasOptions();
processOptions();
}
/**
* Process additional tool-specific options. Subclasses generally override
* this.
*/
protected abstract void processOptions();
/**
* Load a configuration (java properties) file for jdnssec-tools. Returns
* the path of the loaded file.
*
* @param configFile a given path to a config file. This will be considered
* first.
* @return The path of the file that was actually loaded, or null if no config
* file was loaded.
*/
protected String loadConfig(String configFile) {
// Do not load config files twice
if (props != null) {
return null;
}
props = new Properties();
String[] configFiles = { configFile, "jdnssec-tools.properties", ".jdnssec-tools.properties",
System.getProperty("user.home") + "/.jdnssec-tools.properties" };
File f = null;
for (String fname : configFiles) {
if (fname == null) {
continue;
}
f = new File(fname);
if (!f.canRead()) {
continue;
} }
Logger rootLogger = Logger.getLogger(""); try (FileInputStream stream = new FileInputStream(f)) {
int value = parseInt(cli.getOptionValue('v'), -1); props.load(stream);
break; // load the first config file found in our list
} catch (IOException e) {
log.warning("Could not read config file " + f.getName() + ": " + e);
}
}
switch (value) { if (f != null) {
return f.getPath();
}
return null;
}
protected void fail(String errorMessage) {
log.severe(errorMessage);
System.exit(64);
}
/** Print out the usage and help statements, then quit. */
public void usage() {
HelpFormatter f = new HelpFormatter();
PrintWriter out = new PrintWriter(System.err);
// print our own usage statement:
f.printHelp(out, 120, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
HelpFormatter.DEFAULT_DESC_PAD, null);
out.flush();
System.exit(0);
}
/**
* Set the logging level based on a string value
*
* @param logger The logger to set -- usually the rootLogger
* @param levelStr A level string that is either an integer from 0 to 6, or a
* java.util.logging log level string (severe, warning, info,
* fine, finer,
* finest).
*/
private void setLogLevel(Logger logger, String levelStr) {
Level level;
int internalLogLevel = Utils.parseInt(levelStr, -1);
if (internalLogLevel != -1) {
switch (internalLogLevel) {
case 0: case 0:
rootLogger.setLevel(Level.OFF); level = Level.OFF;
break; break;
case 1: case 1:
rootLogger.setLevel(Level.SEVERE); level = Level.SEVERE;
break; break;
case 2: case 2:
default: default:
rootLogger.setLevel(Level.WARNING); level = Level.WARNING;
break; break;
case 3: case 3:
rootLogger.setLevel(Level.INFO); level = Level.INFO;
break; break;
case 4: case 4:
rootLogger.setLevel(Level.CONFIG); level = Level.FINE;
break;
case 5: case 5:
rootLogger.setLevel(Level.FINE);
break;
case 6: case 6:
rootLogger.setLevel(Level.ALL); level = Level.ALL;
break;
} }
} else {
// I hate java.util.logging, btw. try {
for (Handler h : rootLogger.getHandlers()) { level = Level.parse(levelStr.toUpperCase());
h.setLevel(rootLogger.getLevel()); } catch (IllegalArgumentException e) {
h.setFormatter(new BareLogFormatter()); System.err.println("Verbosity level '" + levelStr + "' not recognized");
level = Level.WARNING;
} }
}
logger.setLevel(level);
}
if (cli.hasOption('m')) { /**
org.xbill.DNS.Options.set("multiline"); * Process both property file based alias definitions and command line alias
} * definitions
*/
protected void processAliasOptions() {
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
// First parse any command line options
// those look like '-A <alias-num>:<orig-num>:<mnemonic>', e.g., '-A
// 21:13:ECDSAP256-NSEC6'
String[] optstrs = null;
if ((optstrs = cli.getOptionValues('A')) != null) {
for (String value : optstrs) {
String[] valueComponents = value.split(":");
int aliasAlg = Utils.parseInt(valueComponents[0], -1);
int origAlg = Utils.parseInt(valueComponents[1], -1);
String mnemonic = valueComponents[2];
String[] optstrs = null; if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
if ((optstrs = cli.getOptionValues('A')) != null) { algs.addAlias(aliasAlg, mnemonic, origAlg);
for (int i = 0; i < optstrs.length; i++) {
addArgAlias(optstrs[i]);
} }
} }
processOptions(cli);
} }
/** // Next see if we have any alias options in properties
* Process additional tool-specific options. Subclasses generally override // Those look like 'signzone.alias.<alias-mnemonic> =
* this. // <orig-alg-num>:<alias-alg-num>'
* for (String key : props.stringPropertyNames()) {
* @param cli if (key.startsWith(name + ".alias.") || key.startsWith("alias.")) {
* The {@link CommandLine} object containing the parsed command String[] keyComponents = key.split("\\.");
* line state. String mnemonic = keyComponents[keyComponents.length - 1];
*/ String[] valueComponents = props.getProperty(key).split(":");
protected void processOptions(CommandLine cli) throws ParseException { int origAlg = Utils.parseInt(valueComponents[0], -1);
// Subclasses generally override this. int aliasAlg = Utils.parseInt(valueComponents[1], -1);
}
/** Print out the usage and help statements, then quit. */ if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
public void usage() { algs.addAlias(aliasAlg, mnemonic, origAlg);
HelpFormatter f = new HelpFormatter(); }
}
PrintWriter out = new PrintWriter(System.err);
// print our own usage statement:
f.printHelp(out, 75, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
HelpFormatter.DEFAULT_DESC_PAD, null);
out.flush();
System.exit(64);
}
protected void addArgAlias(String s) {
if (s == null)
return;
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
String[] v = s.split(":");
if (v.length < 2)
return;
int alias = parseInt(v[0], -1);
if (alias <= 0)
return;
int orig = parseInt(v[1], -1);
if (orig <= 0)
return;
String mn = null;
if (v.length > 2)
mn = v[2];
algs.addAlias(alias, mn, orig);
}
}
public static int parseInt(String s, int def) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return def;
}
}
public static long parseLong(String s, long def) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
return def;
} }
} }
/** /**
* Calculate a date/time from a command line time/offset duration string. * Given a parsed command line, option, and list of possible config
* properties, and a default value, determine value for the option
* *
* @param start * @param option The option name
* the start time to calculate offsets from. * @param properties A list of configuration parameters that we would like
* @param duration * to use for this option, from most preferred to least.
* the time/offset string to parse. * @param defaultValue A default value to return if either the option or
* @return the calculated time. * config value cannot be parsed, or neither are present.
* @return The found value, or the default value.
*/ */
public static Instant convertDuration(Instant start, String duration) throws ParseException { protected String cliOption(String option, String[] properties, String defaultValue) {
if (start == null) { if (cli.hasOption(option)) {
start = Instant.now(); return cli.getOptionValue(option);
} }
for (String property : properties) {
if (duration.startsWith("now")) { // first look up the scoped version of the property
start = Instant.now(); String value = props.getProperty(name + "." + property);
if (duration.indexOf("+") < 0) if (value != null) {
return start; return value;
}
duration = duration.substring(3); value = props.getProperty(property);
if (value != null) {
return value;
}
} }
return defaultValue;
}
if (duration.startsWith("+")) { /**
long offset = parseLong(duration.substring(1), 0); * Given a parsed command line, option, and list of possible config
return start.plusSeconds(offset); * properties, determine the value for the option, converting the value to
} * long.
*/
protected long cliLongOption(String option, String[] properties, long defaultValue) {
String value = cliOption(option, properties, Long.toString(defaultValue));
return Utils.parseLong(value, defaultValue);
}
// This is a heuristic to distinguish UNIX epoch times from the zone file /**
// format standard (which is length == 14) * Given a parsed command line, option, and list of possible config
if (duration.length() <= 10) { * properties, determine the value for the option, converting the value to
long epoch = parseLong(duration, 0); * int.
return Instant.ofEpochSecond(epoch); */
} protected int cliIntOption(String option, String[] properties, int defaultValue) {
String value = cliOption(option, properties, Integer.toString(defaultValue));
return Utils.parseInt(value, defaultValue);
}
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss"); /**
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); * Given a parsed command line, option, and list of possible config
try { * properties, determine the value for the option, converting the value to
Date parsedDate = dateFormatter.parse(duration); * a boolean.
return parsedDate.toInstant(); */
} catch (java.text.ParseException e) { protected boolean cliBooleanOption(String option, String[] properties, boolean defaultValue) {
throw new ParseException(e.getMessage()); if (cli.hasOption(option)) {
return true;
} }
String value = cliOption(option, properties, Boolean.toString(defaultValue));
return Boolean.parseBoolean(value);
} }
public abstract void execute() throws Exception; public abstract void execute() throws Exception;
public void run(CLIStateBase state, String[] args) { public void run(String[] args) {
try {
state.parseCommandLine(args);
} catch (UnrecognizedOptionException e) {
System.err.println("error: unknown option encountered: " + e.getMessage());
state.usage();
} catch (AlreadySelectedException e) {
System.err.println("error: mutually exclusive options have "
+ "been selected:\n " + e.getMessage());
state.usage();
} catch (Exception e) {
System.err.println("error: unknown command line parsing exception:");
e.printStackTrace();
state.usage();
}
parseCommandLine(args);
log = Logger.getLogger(this.getClass().toString()); log = Logger.getLogger(this.getClass().toString());
try { try {

View File

@ -0,0 +1,84 @@
package com.verisignlabs.dnssec.cl;
public class CLI {
private SubCommandType subCommand = null;
private String commandSetStr = null;
enum SubCommandType {
DSTOOL, KEYGEN, KEYINFO, SIGNKEYSET, SIGNRRSET, SIGNZONE, VERIFYZONE, ZONEFORMAT;
}
public CLI(String name, String usageStr) {
StringBuilder sb = new StringBuilder();
for (SubCommandType type : SubCommandType.class.getEnumConstants()) {
sb.append(type.toString().toLowerCase());
sb.append(" ");
}
commandSetStr = sb.toString().trim();
}
private void fail(String errorMessage){
System.err.println("ERROR: " + errorMessage);
System.exit(2);
}
public void run(String[] args) {
String[] subCommandArgs = null;
if (args.length < 1) {
fail("missing command: must be one of: " + commandSetStr);
}
String command = args[0];
if (command.equals("-h")) {
System.err.println("usage: jdnssec-tools <command> <command args..>");
System.err.println(" <command> is one of: " + commandSetStr);
System.exit(0);
}
try {
subCommand = SubCommandType.valueOf(command.toUpperCase());
} catch (IllegalArgumentException e) {
fail("unrecognized command '" + command + "': must be one of: " + commandSetStr);
}
subCommandArgs = new String[args.length - 1];
System.arraycopy(args, 1, subCommandArgs, 0, args.length - 1);
CLBase cmd = null;
switch(subCommand) {
case DSTOOL:
cmd = new DSTool("dstool", "jdnssec-tools dstool [..options..] keyfile [keyfile..]");
break;
case KEYGEN:
cmd = new KeyGen("keygen", "jdnssec-tools keygen [..options..] zonename");
break;
case KEYINFO:
cmd = new KeyInfoTool("keyinfotool", "jdnssec-tools keyinfo [..options..] keyfile");
break;
case SIGNKEYSET:
cmd = new SignKeyset("signkeyset", "jdnssec-tools signkeyset [..options..] dnskeyset_file [key_file ...]");
break;
case SIGNRRSET:
cmd = new SignRRset("signrrset", "jdnssec-tools signrrset [..options..] rrset_file key_file [key_file ...]");
break;
case SIGNZONE:
cmd = new SignZone("signzone", "jdnssec-tools signzone [..options..] zone_file [key_file ...]");
break;
case VERIFYZONE:
cmd = new VerifyZone("verifyzone", "jdnssec-tools verifyzone [..options..] zonefile");
break;
case ZONEFORMAT:
cmd = new ZoneFormat("zoneformat", "jdnssec-tools zoneformat [..options..] zonefile");
break;
default:
fail("commmand " + command + " has not been implemented.");
break;
}
cmd.run(subCommandArgs);
}
public static void main(String[] args) {
CLI cli = new CLI("cli", "jdnssec-tools <command> [..args..]");
cli.run(args);
}
}

View File

@ -18,21 +18,18 @@
package com.verisignlabs.dnssec.cl; package com.verisignlabs.dnssec.cl;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.xbill.DNS.CDSRecord;
import org.xbill.DNS.DLVRecord;
import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC; import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.DSRecord; import org.xbill.DNS.DSRecord;
import org.xbill.DNS.Record; import org.xbill.DNS.Record;
import com.verisignlabs.dnssec.security.BINDKeyUtils; import com.verisignlabs.dnssec.security.BINDKeyUtils;
import com.verisignlabs.dnssec.security.DSAlgorithm;
import com.verisignlabs.dnssec.security.DnsKeyPair; import com.verisignlabs.dnssec.security.DnsKeyPair;
import com.verisignlabs.dnssec.security.SignUtils;
/** /**
* This class forms the command line implementation of a DNSSEC DS/DLV generator * This class forms the command line implementation of a DNSSEC DS/DLV generator
@ -40,7 +37,15 @@ import com.verisignlabs.dnssec.security.SignUtils;
* @author David Blacka * @author David Blacka
*/ */
public class DSTool extends CLBase { public class DSTool extends CLBase {
private CLIState state; private dsType createType = dsType.DS;
private String outputfile = null;
private String[] keynames = null;
private int digestId = DNSSEC.Digest.SHA256;
private long dsTTL = -1;
public DSTool(String name, String usageStr) {
super(name, usageStr);
}
/** There are several records that are based on DS. */ /** There are several records that are based on DS. */
protected enum dsType { protected enum dsType {
@ -52,79 +57,76 @@ public class DSTool extends CLBase {
* state. * state.
*/ */
protected static class CLIState extends CLIStateBase { /**
public dsType createType = dsType.DS; * Set up the command line options.
public String outputfile = null; *
public String keyname = null; * @return a set of command line options.
public int digestId = DNSSEC.Digest.SHA1; */
protected void setupOptions() {
public CLIState() { opts.addOption(Option.builder("D").longOpt("dlv").desc("Generate a DLV record instead.").build());
super("jdnssec-dstool [..options..] keyfile"); opts.addOption(Option.builder("C").longOpt("cds").desc("Generate a CDS record instead").build());
} String[] algStrings = DSAlgorithm.getInstance().supportedAlgorithmMnemonics();
String algStringSet = String.join(" | ", algStrings);
/** opts.addOption(
* Set up the command line options. Option.builder("d").hasArg().argName("id").longOpt("digest").desc(algStringSet + ": default is SHA256")
* .build());
* @return a set of command line options. opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output").desc("output to file").build());
*/ opts.addOption(Option.builder("T").longOpt("ttl").hasArg().desc("TTL to use for generated DS/CDS record").build());
@Override
protected void setupOptions(Options opts) {
opts.addOption(Option.builder("D").longOpt("dlv").desc("Generate a DLV record instead.").build());
opts.addOption(Option.builder("C").longOpt("cds").desc("Generate a CDS record instead").build());
opts.addOption(Option.builder("d").hasArg().argName("id").longOpt("digest").desc("The digest algorithm to use").build());
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output").desc("output to file").build());
}
@Override
protected void processOptions(CommandLine cli)
throws org.apache.commons.cli.ParseException {
outputfile = cli.getOptionValue('f');
if (cli.hasOption("dlv")) {
createType = dsType.DLV;
} else if (cli.hasOption("cds")) {
createType = dsType.CDS;
}
String optstr = cli.getOptionValue('d');
if (optstr != null)
digestId = DNSSEC.Digest.value(optstr);
String[] args = cli.getArgs();
if (args.length < 1) {
System.err.println("error: missing key file ");
usage();
}
keyname = args[0];
}
} }
public void execute() throws Exception { protected void processOptions() {
DnsKeyPair key = BINDKeyUtils.loadKey(state.keyname, null); String[] digestAlgOptionKeys = { "digest_algorithm", "digest_id" };
String[] dsTTLOptionKeys = { "ds_ttl", "ttl" };
outputfile = cli.getOptionValue('f');
if (cli.hasOption("dlv")) {
createType = dsType.DLV;
} else if (cli.hasOption("cds")) {
createType = dsType.CDS;
}
String digestValue = cliOption("d", digestAlgOptionKeys, Integer.toString(digestId));
digestId = DNSSEC.Digest.value(digestValue);
dsTTL = cliLongOption("ttl", dsTTLOptionKeys, dsTTL);
String[] args = cli.getArgs();
if (args.length < 1) {
fail("missing key file");
}
keynames = args;
}
public void createDS(String keyname) throws IOException {
DSAlgorithm dsAlgorithm = DSAlgorithm.getInstance();
DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null);
DNSKEYRecord dnskey = key.getDNSKEYRecord(); DNSKEYRecord dnskey = key.getDNSKEYRecord();
if ((dnskey.getFlags() & DNSKEYRecord.Flags.SEP_KEY) == 0) { if ((dnskey.getFlags() & DNSKEYRecord.Flags.SEP_KEY) == 0) {
log.warning("DNSKEY is not an SEP-flagged key."); log.warning("DNSKEY " + keyname + " is not an SEP-flagged key.");
} }
DSRecord ds = SignUtils.calculateDSRecord(dnskey, state.digestId, dnskey.getTTL()); long ttl = dsTTL < 0 ? dnskey.getTTL() : dsTTL;
Record res = ds; DSRecord ds = dsAlgorithm.calculateDSRecord(dnskey, digestId, ttl);
Record res;
if (state.createType == dsType.DLV) { switch (createType) {
log.fine("creating DLV."); case DLV:
DLVRecord dlv = new DLVRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(), log.fine("creating DLV.");
ds.getDigestID(), ds.getDigest()); res = dsAlgorithm.dsToDLV(ds);
res = dlv; break;
} else if (state.createType == dsType.CDS) { case CDS:
log.fine("creating CDS."); log.fine("creating CDS.");
CDSRecord cds = new CDSRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(), res = dsAlgorithm.dstoCDS(ds);
ds.getDClass(), ds.getDigest()); break;
res = cds; default:
res = ds;
break;
} }
if (state.outputfile != null && !state.outputfile.equals("-")) { if (outputfile != null && !outputfile.equals("-")) {
try (PrintWriter out = new PrintWriter(new FileWriter(state.outputfile))) { try (PrintWriter out = new PrintWriter(new FileWriter(outputfile))) {
out.println(res); out.println(res);
} }
} else { } else {
@ -132,10 +134,15 @@ public class DSTool extends CLBase {
} }
} }
public static void main(String[] args) { public void execute() throws Exception {
DSTool tool = new DSTool(); for (String keyname : keynames) {
tool.state = new CLIState(); createDS(keyname);
}
}
tool.run(tool.state, args); public static void main(String[] args) {
DSTool tool = new DSTool("dstool", "jdnssec-dstool [..options..] keyfile [keyfile..]");
tool.run(args);
} }
} }

View File

@ -19,9 +19,7 @@ package com.verisignlabs.dnssec.cl;
import java.io.File; import java.io.File;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.xbill.DNS.DClass; import org.xbill.DNS.DClass;
import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
@ -37,163 +35,144 @@ import com.verisignlabs.dnssec.security.JCEDnsSecSigner;
* @author David Blacka * @author David Blacka
*/ */
public class KeyGen extends CLBase { public class KeyGen extends CLBase {
private CLIState state; private int algorithm = 13;
private int keylength = 2048;
private boolean useLargeE = true;
private String outputfile = null;
private File keydir = null;
private boolean zoneKey = true;
private boolean kskFlag = false;
private String owner = null;
private long ttl = 86400;
private int givenKeyTag = -1;
public KeyGen(String name, String usageStr) {
super(name, usageStr);
}
/** /**
* This is a small inner class used to hold all of the command line option * Set up the command line options.
* state.
*/ */
protected static class CLIState extends CLIStateBase { protected void setupOptions() {
public int algorithm = 8; // boolean options
public int keylength = 1024; opts.addOption("k", "kskflag", false,
public boolean useLargeE = true; "Key is a key-signing-key (sets the SEP flag).");
public String outputfile = null; opts.addOption("e", "large-exponent", false, "Use large RSA exponent (default)");
public File keydir = null; opts.addOption("E", "small-exponent", false, "Use small RSA exponent");
public boolean zoneKey = true;
public boolean kskFlag = false;
public String owner = null;
public long ttl = 86400;
public CLIState() { // Argument options
super("jdnssec-keygen [..options..] name"); opts.addOption(
Option.builder("n").longOpt("nametype").hasArg().argName("type").desc("ZONE | OTHER (default ZONE)").build());
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
String algStringSet = String.join(" | ", algStrings);
opts.addOption(Option.builder("a").hasArg().argName("algorithm")
.desc(algStringSet + " | aliases, ECDSAP256SHA256 is default.").build());
opts.addOption(Option.builder("b").hasArg().argName("size").desc(
"key size, in bits (default 2048). RSA: [512..4096], DSA: [512..1024], DH: [128..4096], ECDSA: ignored, EdDSA: ignored")
.build());
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output-file")
.desc("base filename from the public/private key files").build());
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keydir")
.desc("generated keyfiles are written to this directory").build());
opts.addOption(Option.builder("T").hasArg().argName("ttl").longOpt("ttl")
.desc("use this TTL for the generated DNSKEY records (default: 86400").build());
opts.addOption(Option.builder().hasArg().argName("tag").longOpt("with-tag")
.desc("Generate keys until tag is the given value.").build());
}
protected void processOptions() {
String[] useLargeEOptionKeys = { "use_large_exponent", "use_large_e" };
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
String[] algorithmOptionKeys = { "algorithm", "alg " };
String[] keyLengthOptionKeys = { "key_length", "keylen" };
String[] ttlOptionKeys = { "dnskey_ttl", "ttl" };
if (cli.hasOption('k')) {
kskFlag = true;
}
useLargeE = cli.hasOption('e'); // explicit command line option for the large exponent
useLargeE = !cli.hasOption('E'); // explicit command line option for the small exponent
String optstr = cliOption("e", useLargeEOptionKeys, Boolean.toString(useLargeE)); // get any config file properties
if (optstr != null) {
useLargeE = Boolean.parseBoolean(optstr);
} }
/** outputfile = cli.getOptionValue('f');
* Set up the command line options.
*/
@Override
protected void setupOptions(Options opts) {
// boolean options
opts.addOption("k", "kskflag", false,
"Key is a key-signing-key (sets the SEP flag).");
opts.addOption("e", "large-exponent", false, "Use large RSA exponent (default)");
opts.addOption("E", "small-exponent", false, "Use small RSA exponent");
// Argument options
opts.addOption(
Option.builder("n").longOpt("nametype").hasArg().argName("type").desc("ZONE | OTHER (default ZONE)").build());
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
String algStringSet = String.join(" | ", algStrings);
opts.addOption(Option.builder("a").hasArg().argName("algorithm")
.desc(algStringSet + " | alias, RSASHA256 is default.").build());
opts.addOption(Option.builder("b").hasArg().argName("size").desc(
"key size, in bits (default 1024). RSA: [512..4096], DSA: [512..1024], DH: [128..4096], ECDSA: ignored, EdDSA: ignored")
.build());
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output-file")
.desc("base filename from the public/private key files").build());
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keydir")
.desc("generated keyfiles are written to this directory").build());
opts.addOption(Option.builder("T").hasArg().argName("ttl").longOpt("ttl")
.desc("use this TTL for the generated DNSKEY records (default: 86400").build());
String keydirName = cliOption("d", keyDirectoryOptionKeys, null);
if (keydirName != null) {
keydir = new File(keydirName);
} }
@Override String algString = cliOption("a", algorithmOptionKeys, Integer.toString(algorithm));
protected void processOptions(CommandLine cli) algorithm = Utils.parseAlg(algString);
throws org.apache.commons.cli.ParseException { if (algorithm < 0) {
String optstr = null; fail("DNSSEC algorithm " + algString + " is not supported");
String[] optstrs = null;
if (cli.hasOption('k'))
kskFlag = true;
if (cli.hasOption('e'))
useLargeE = true;
outputfile = cli.getOptionValue('f');
if ((optstr = cli.getOptionValue('d')) != null) {
keydir = new File(optstr);
}
if ((optstr = cli.getOptionValue('n')) != null && !optstr.equalsIgnoreCase("ZONE")) {
zoneKey = false;
}
if ((optstrs = cli.getOptionValues('A')) != null) {
for (int i = 0; i < optstrs.length; i++) {
addArgAlias(optstrs[i]);
}
}
if ((optstr = cli.getOptionValue('a')) != null) {
algorithm = CLIState.parseAlg(optstr);
if (algorithm < 0) {
System.err.println("DNSSEC algorithm " + optstr + " is not supported");
usage();
}
}
if ((optstr = cli.getOptionValue('b')) != null) {
keylength = parseInt(optstr, 1024);
}
if ((optstr = cli.getOptionValue("ttl")) != null) {
ttl = parseInt(optstr, 86400);
}
String[] args = cli.getArgs();
if (args.length < 1) {
System.err.println("error: missing key owner name");
usage();
}
owner = args[0];
} }
private static int parseAlg(String s) { keylength = cliIntOption("b", keyLengthOptionKeys, keylength);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); ttl = cliLongOption("ttl", ttlOptionKeys, ttl);
givenKeyTag = Utils.parseInt(cli.getOptionValue("with-tag"), -1);
int alg = parseInt(s, -1); String[] args = cli.getArgs();
if (alg > 0) {
if (algs.supportedAlgorithm(alg))
return alg;
return -1;
}
return algs.stringToAlgorithm(s); if (args.length < 1) {
fail("missing key owner name");
} }
owner = args[0];
log.fine("keygen options => algorithm: " + algorithm + ", keylength: " + keylength +
", useLargeE: " + useLargeE + ", kskFlag: " + kskFlag + ", ttl: " + ttl + ", givenKeyTag: " + givenKeyTag);
} }
public void execute() throws Exception { public void execute() throws Exception {
JCEDnsSecSigner signer = new JCEDnsSecSigner(); JCEDnsSecSigner signer = new JCEDnsSecSigner();
// Minor hack to make the owner name absolute. // Minor hack to make the owner name absolute.
if (!state.owner.endsWith(".")) { if (!owner.endsWith(".")) {
state.owner = state.owner + "."; owner = owner + ".";
} }
Name ownerName = Name.fromString(state.owner); Name ownerName = Name.fromString(owner);
// Calculate our flags // Calculate our flags
int flags = 0; int flags = 0;
if (state.zoneKey) if (zoneKey) {
flags |= DNSKEYRecord.Flags.ZONE_KEY; flags |= DNSKEYRecord.Flags.ZONE_KEY;
if (state.kskFlag) }
if (kskFlag) {
flags |= DNSKEYRecord.Flags.SEP_KEY; flags |= DNSKEYRecord.Flags.SEP_KEY;
}
log.fine("create key pair with (name = " + ownerName + ", ttl = " + ttl
+ ", alg = " + algorithm + ", flags = " + flags + ", length = "
+ keylength + ")");
log.fine("create key pair with (name = " + ownerName + ", ttl = " + state.ttl DnsKeyPair pair = signer.generateKey(ownerName, ttl, DClass.IN,
+ ", alg = " + state.algorithm + ", flags = " + flags + ", length = " algorithm, flags, keylength,
+ state.keylength + ")"); useLargeE);
DnsKeyPair pair = signer.generateKey(ownerName, state.ttl, DClass.IN, // If we were asked to generate a duplicate keytag, keep trying until we get one
state.algorithm, flags, state.keylength, // This can take a long time, depending on our key generation speed
state.useLargeE); while (givenKeyTag >= 0 && pair.getDNSKEYFootprint() != givenKeyTag) {
pair = signer.generateKey(ownerName, ttl, DClass.IN, algorithm, flags, keylength,
useLargeE);
}
if (state.outputfile != null) { if (outputfile != null) {
BINDKeyUtils.writeKeyFiles(state.outputfile, pair, state.keydir); BINDKeyUtils.writeKeyFiles(outputfile, pair, keydir);
} else { } else {
BINDKeyUtils.writeKeyFiles(pair, state.keydir); BINDKeyUtils.writeKeyFiles(pair, keydir);
System.out.println(BINDKeyUtils.keyFileBase(pair)); System.out.println(BINDKeyUtils.keyFileBase(pair));
} }
} }
public static void main(String[] args) { public static void main(String[] args) {
KeyGen tool = new KeyGen(); KeyGen tool = new KeyGen("keygen", "jdnssec-keygen [..options..] zonename");
tool.state = new CLIState();
tool.run(tool.state, args); tool.run(args);
} }
} }

View File

@ -20,9 +20,6 @@ package com.verisignlabs.dnssec.cl;
import java.security.interfaces.DSAPublicKey; import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSKEYRecord;
import com.verisignlabs.dnssec.security.BINDKeyUtils; import com.verisignlabs.dnssec.security.BINDKeyUtils;
@ -35,41 +32,32 @@ import com.verisignlabs.dnssec.security.DnsKeyPair;
* @author David Blacka * @author David Blacka
*/ */
public class KeyInfoTool extends CLBase { public class KeyInfoTool extends CLBase {
private CLIState state; private String[] keynames = null;
public KeyInfoTool(String name, String usageStr) {
super(name, usageStr);
}
/** /**
* This is a small inner class used to hold all of the command line option * Set up the command line options.
* state.
*/ */
protected static class CLIState extends CLIStateBase { protected void setupOptions() {
public String[] keynames = null; // no special options at the moment.
}
public CLIState() {
super("jdnssec-keyinfo [..options..] keyfile");
}
/** protected void processOptions() {
* Set up the command line options.
*/
@Override
protected void setupOptions(Options opts) {
// no special options at the moment.
}
@Override
protected void processOptions(CommandLine cli) throws ParseException {
keynames = cli.getArgs(); keynames = cli.getArgs();
if (keynames.length < 1) { if (keynames.length < 1) {
System.err.println("error: missing key file "); fail("missing key file");
usage();
} }
} }
}
public void execute() throws Exception { public void execute() throws Exception {
for (int i = 0; i < state.keynames.length; ++i) { for (int i = 0; i < keynames.length; ++i) {
String keyname = state.keynames[i]; String keyname = keynames[i];
DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null); DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null);
DNSKEYRecord dnskey = key.getDNSKEYRecord(); DNSKEYRecord dnskey = key.getDNSKEYRecord();
DnsKeyAlgorithm dnskeyalg = DnsKeyAlgorithm.getInstance(); DnsKeyAlgorithm dnskeyalg = DnsKeyAlgorithm.getInstance();
@ -84,29 +72,28 @@ public class KeyInfoTool extends CLBase {
+ " (" + dnskey.getAlgorithm() + ")"); + " (" + dnskey.getAlgorithm() + ")");
System.out.println("ID: " + dnskey.getFootprint()); System.out.println("ID: " + dnskey.getFootprint());
System.out.println("KeyFileBase: " + BINDKeyUtils.keyFileBase(key)); System.out.println("KeyFileBase: " + BINDKeyUtils.keyFileBase(key));
int basetype = dnskeyalg.baseType(dnskey.getAlgorithm()); DnsKeyAlgorithm.BaseAlgorithm basetype = dnskeyalg.baseType(dnskey.getAlgorithm());
if (basetype == DnsKeyAlgorithm.RSA) { if (basetype == DnsKeyAlgorithm.BaseAlgorithm.RSA) {
RSAPublicKey pub = (RSAPublicKey) key.getPublic(); RSAPublicKey pub = (RSAPublicKey) key.getPublic();
System.out.println("RSA Public Exponent: " + pub.getPublicExponent()); System.out.println("RSA Public Exponent: " + pub.getPublicExponent());
System.out.println("RSA Modulus: " + pub.getModulus()); System.out.println("RSA Modulus: " + pub.getModulus());
} else if (basetype == DnsKeyAlgorithm.DSA) { } else if (basetype == DnsKeyAlgorithm.BaseAlgorithm.DSA) {
DSAPublicKey pub = (DSAPublicKey) key.getPublic(); DSAPublicKey pub = (DSAPublicKey) key.getPublic();
System.out.println("DSA base (G): " + pub.getParams().getG()); System.out.println("DSA base (G): " + pub.getParams().getG());
System.out.println("DSA prime (P): " + pub.getParams().getP()); System.out.println("DSA prime (P): " + pub.getParams().getP());
System.out.println("DSA subprime (Q): " + pub.getParams().getQ()); System.out.println("DSA subprime (Q): " + pub.getParams().getQ());
System.out.println("DSA public (Y): " + pub.getY()); System.out.println("DSA public (Y): " + pub.getY());
} }
if (state.keynames.length - i > 1) { if (keynames.length - i > 1) {
System.out.println(); System.out.println();
} }
} }
} }
public static void main(String[] args) { public static void main(String[] args) {
KeyInfoTool tool = new KeyInfoTool(); KeyInfoTool tool = new KeyInfoTool("keyinfotool", "jdnssec-keyinfo [..options..] keyfile");
tool.state = new CLIState();
tool.run(tool.state, args); tool.run(args);
} }
} }

View File

@ -24,9 +24,7 @@ import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
import org.xbill.DNS.RRSIGRecord; import org.xbill.DNS.RRSIGRecord;
import org.xbill.DNS.RRset; import org.xbill.DNS.RRset;
@ -48,66 +46,78 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
* @author David Blacka * @author David Blacka
*/ */
public class SignKeyset extends CLBase { public class SignKeyset extends CLBase {
private CLIState state; private File keyDirectory = null;
private String[] keyFiles = null;
private Instant start = null;
private Instant expire = null;
private String inputfile = null;
private String outputfile = null;
private boolean verifySigs = false;
public SignKeyset(String name, String usageStr) {
super(name, usageStr);
}
/** /**
* This is an inner class used to hold all of the command line option state. * Set up the command line options.
*/ */
protected static class CLIState extends CLIStateBase {
public File keyDirectory = null;
public String[] keyFiles = null;
public Instant start = null;
public Instant expire = null;
public String inputfile = null;
public String outputfile = null;
public boolean verifySigs = false;
public CLIState() { protected void setupOptions() {
super("jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]"); // boolean options
} opts.addOption("a", "verify", false, "verify generated signatures>");
/** // Argument options
* Set up the command line options. opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
*/ .desc("directory where key files are found (default '.').").build());
@Override opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
protected void setupOptions(Options opts) { .desc("signature starting time (default is now - 1 hour)").build());
// boolean options opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
opts.addOption("a", "verify", false, "verify generated signatures>"); .desc("signature expiration time (default is start-time + 30 days)").build());
opts.addOption(
Option.builder("f").hasArg().argName("outfile").desc("file the signed keyset is written to").build());
}
// Argument options
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory").desc("directory where key files are found (default '.').").build());
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time").desc("signature starting time (default is now - 1 hour)").build());
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time").desc("signature expiration time (default is start-time + 30 days)").build());
opts.addOption(Option.builder("f").hasArg().argName("outfile").desc("file the signed keyset is written to").build());
}
@Override protected void processOptions() {
protected void processOptions(CommandLine cli) String[] verifyOptionKeys = { "verify_signatures", "verify" };
throws org.apache.commons.cli.ParseException { String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
String[] inceptionOptionKeys = { "inception", "start" };
String[] expireOptionKeys = { "expire" };
String optstr = null; String optstr = null;
if (cli.hasOption('a')) verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
verifySigs = true;
String keyDirectoryName = cliOption("D", keyDirectoryOptionKeys, null);
if ((optstr = cli.getOptionValue('D')) != null) { if (keyDirectoryName != null) {
keyDirectory = new File(optstr); keyDirectory = new File(optstr);
if (!keyDirectory.isDirectory()) { if (!keyDirectory.isDirectory()) {
System.err.println("error: " + optstr + " is not a directory"); fail("key directory " + optstr + " is not a directory");
usage();
} }
} }
if ((optstr = cli.getOptionValue('s')) != null) { try {
start = convertDuration(null, optstr); optstr = cliOption("s", inceptionOptionKeys, null);
} else { if (optstr != null) {
// default is now - 1 hour. start = Utils.convertDuration(null, optstr);
start = Instant.now().minusSeconds(3600); } else {
// default is now - 1 hour.
start = Instant.now().minusSeconds(3600);
}
} catch (java.text.ParseException e) {
fail("Unable to parse start time specifiction: " + e);
} }
if ((optstr = cli.getOptionValue('e')) != null) { try {
expire = convertDuration(start, optstr); optstr = cliOption("e", expireOptionKeys, null);
} else { if (optstr != null) {
expire = convertDuration(start, "+2592000"); // 30 days expire = Utils.convertDuration(start, optstr);
} else {
expire = Utils.convertDuration(start, "+2592000"); // 30 days
}
} catch (java.text.ParseException e) {
fail("Unable to parse expire time specification: " + e);
} }
outputfile = cli.getOptionValue('f'); outputfile = cli.getOptionValue('f');
@ -115,8 +125,7 @@ public class SignKeyset extends CLBase {
String[] files = cli.getArgs(); String[] files = cli.getArgs();
if (files.length < 1) { if (files.length < 1) {
System.err.println("error: missing zone file and/or key files"); fail("missing zone file and/or key files");
usage();
} }
inputfile = files[0]; inputfile = files[0];
@ -125,18 +134,15 @@ public class SignKeyset extends CLBase {
System.arraycopy(files, 1, keyFiles, 0, files.length - 1); System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
} }
} }
}
/** /**
* Verify the generated signatures. * Verify the generated signatures.
* *
* @param records * @param records a list of {@link org.xbill.DNS.Record}s.
* a list of {@link org.xbill.DNS.Record}s. * @param keypairs a list of keypairs used the sign the zone.
* @param keypairs
* a list of keypairs used the sign the zone.
* @return true if all of the signatures validated. * @return true if all of the signatures validated.
*/ */
private static boolean verifySigs(List<Record> records, private boolean verifySigs(List<Record> records,
List<DnsKeyPair> keypairs) { List<DnsKeyPair> keypairs) {
boolean secure = true; boolean secure = true;
@ -158,7 +164,7 @@ public class SignKeyset extends CLBase {
boolean result = verifier.verify(rrset); boolean result = verifier.verify(rrset);
if (!result) { if (!result) {
staticLog.fine("Signatures did not verify for RRset: " + rrset); log.fine("Signatures did not verify for RRset: " + rrset);
secure = false; secure = false;
} }
} }
@ -168,19 +174,16 @@ public class SignKeyset extends CLBase {
/** /**
* Load the key pairs from the key files. * Load the key pairs from the key files.
* *
* @param keyfiles * @param keyfiles a string array containing the base names or paths of the
* a string array containing the base names or paths of the * keys to be loaded.
* keys * @param startIndex the starting index of keyfiles string array to use. This
* to be loaded. * allows us to use the straight command line argument
* @param startIndex * array.
* the starting index of keyfiles string array to use. This * @param inDirectory the directory to look in (may be null).
* allows us to use the straight command line argument array.
* @param inDirectory
* the directory to look in (may be null).
* @return a list of keypair objects. * @return a list of keypair objects.
*/ */
private static List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex, private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
File inDirectory) throws IOException { File inDirectory) throws IOException {
if (keyfiles == null) if (keyfiles == null)
return new ArrayList<>(); return new ArrayList<>();
@ -237,15 +240,14 @@ public class SignKeyset extends CLBase {
public void execute() throws Exception { public void execute() throws Exception {
// Read in the zone // Read in the zone
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null); List<Record> records = ZoneUtils.readZoneFile(inputfile, null);
if (records == null || records.isEmpty()) { if (records == null || records.isEmpty()) {
System.err.println("error: empty keyset file"); fail("empty keyset file");
state.usage();
} }
// Make sure that all records are DNSKEYs with the same name. // Make sure that all records are DNSKEYs with the same name.
Name keysetName = null; Name keysetName = null;
RRset keyset = new RRset(); RRset keyset = new RRset();
for (Record r : records) { for (Record r : records) {
if (r.getType() != Type.DNSKEY) { if (r.getType() != Type.DNSKEY) {
@ -256,46 +258,42 @@ public class SignKeyset extends CLBase {
keysetName = r.getName(); keysetName = r.getName();
} }
if (!r.getName().equals(keysetName)) { if (!r.getName().equals(keysetName)) {
System.err.println("error: DNSKEY with a different name found!"); fail("DNSKEY with a different name found!");
state.usage();
} }
keyset.addRR(r); keyset.addRR(r);
} }
if (keyset.size() == 0) { if (keyset.size() == 0) {
System.err.println("error: No DNSKEYs found in keyset file"); fail("error: No DNSKEYs found in keyset file");
state.usage();
} }
// Load the key pairs. // Load the key pairs.
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory); List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, keyDirectory);
// If we *still* don't have any key pairs, look for keys the key // If we *still* don't have any key pairs, look for keys the key
// directory // directory
// that match // that match
if (keypairs == null) { if (keypairs == null) {
keypairs = findZoneKeys(state.keyDirectory, keysetName); keypairs = findZoneKeys(keyDirectory, keysetName);
} }
// If there *still* aren't any ZSKs defined, bail. // If there *still* aren't any ZSKs defined, bail.
if (keypairs == null || keypairs.isEmpty() || keysetName == null) { if (keypairs == null || keypairs.isEmpty() || keysetName == null) {
System.err.println("error: No signing keys could be determined."); fail("no signing keys could be determined.");
state.usage();
return;
} }
// default the output file, if not set. // default the output file, if not set.
if (state.outputfile == null) { if (outputfile == null) {
if (keysetName.isAbsolute()) { if (keysetName.isAbsolute()) {
state.outputfile = keysetName + "signed_keyset"; outputfile = keysetName + "signed_keyset";
} else { } else {
state.outputfile = keysetName + ".signed_keyset"; outputfile = keysetName + ".signed_keyset";
} }
} }
JCEDnsSecSigner signer = new JCEDnsSecSigner(); JCEDnsSecSigner signer = new JCEDnsSecSigner();
List<RRSIGRecord> sigs = signer.signRRset(keyset, keypairs, state.start, state.expire); List<RRSIGRecord> sigs = signer.signRRset(keyset, keypairs, start, expire);
for (RRSIGRecord s : sigs) { for (RRSIGRecord s : sigs) {
keyset.addRR(s); keyset.addRR(s);
} }
@ -310,9 +308,9 @@ public class SignKeyset extends CLBase {
} }
// write out the signed zone // write out the signed zone
ZoneUtils.writeZoneFile(signedRecords, state.outputfile); ZoneUtils.writeZoneFile(signedRecords, outputfile);
if (state.verifySigs) { if (verifySigs) {
log.fine("verifying generated signatures"); log.fine("verifying generated signatures");
boolean res = verifySigs(signedRecords, keypairs); boolean res = verifySigs(signedRecords, keypairs);
@ -326,9 +324,8 @@ public class SignKeyset extends CLBase {
} }
public static void main(String[] args) { public static void main(String[] args) {
SignKeyset tool = new SignKeyset(); SignKeyset tool = new SignKeyset("signkeyset", "jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]");
tool.state = new CLIState();
tool.run(tool.state, args); tool.run(args);
} }
} }

View File

@ -23,9 +23,7 @@ import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
import org.xbill.DNS.RRSIGRecord; import org.xbill.DNS.RRSIGRecord;
import org.xbill.DNS.RRset; import org.xbill.DNS.RRset;
@ -49,101 +47,108 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
* @author David Blacka * @author David Blacka
*/ */
public class SignRRset extends CLBase { public class SignRRset extends CLBase {
private CLIState state; private File keyDirectory = null;
private String[] keyFiles = null;
private Instant start = null;
private Instant expire = null;
private String inputfile = null;
private String outputfile = null;
private boolean verifySigs = false;
private boolean verboseSigning = false;
public SignRRset(String name, String usageStr) {
super(name, usageStr);
}
/** /**
* This is an inner class used to hold all of the command line option state. * Set up the command line options.
*/ */
protected static class CLIState extends CLIStateBase { protected void setupOptions() {
private File keyDirectory = null; // boolean options
public String[] keyFiles = null; opts.addOption("a", "verify", false, "verify generated signatures>");
public Instant start = null; opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
public Instant expire = null;
public String inputfile = null;
public String outputfile = null;
public boolean verifySigs = false;
public boolean verboseSigning = false;
public CLIState() { opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
super("jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]"); .desc("directory to find key files (default '.'").build());
} opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
.desc("signature starting time (default is now - 1 hour)").build());
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
.desc("signature expiration time (default is start-time + 30 days)").build());
opts.addOption(
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
}
/** protected void processOptions() {
* Set up the command line options. String[] verifyOptionKeys = { "verify_signatures", "verify" };
*/ String[] verboseSigningOptionKeys = { "verbose_signing" };
@Override String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
protected void setupOptions(Options opts) { String[] inceptionOptionKeys = { "inception", "start" };
// boolean options String[] expireOptionKeys = { "expire" };
opts.addOption("a", "verify", false, "verify generated signatures>");
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory") String optstr = null;
.desc("directory to find key files (default '.'").build());
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
.desc("signature starting time (default is now - 1 hour)").build());
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
.desc("signature expiration time (default is start-time + 30 days)").build());
opts.addOption(
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
}
@Override verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException {
String optstr = null;
if (cli.hasOption('a')) verboseSigning = cliBooleanOption("V", verboseSigningOptionKeys, false);
verifySigs = true;
if (cli.hasOption('V'))
verboseSigning = true;
if ((optstr = cli.getOptionValue('D')) != null) { optstr = cliOption("D", keyDirectoryOptionKeys, null);
keyDirectory = new File(optstr); if (optstr != null) {
if (!keyDirectory.isDirectory()) { keyDirectory = new File(optstr);
System.err.println("error: " + optstr + " is not a directory"); if (!keyDirectory.isDirectory()) {
usage(); fail("key directory " + optstr + " is not a directory");
}
} }
}
if ((optstr = cli.getOptionValue('s')) != null) { try {
start = convertDuration(null, optstr); optstr = cliOption("s", inceptionOptionKeys, null);
if (optstr != null) {
start = Utils.convertDuration(null, optstr);
} else { } else {
// default is now - 1 hour. // default is now - 1 hour.
start = Instant.now().minusSeconds(3600); start = Instant.now().minusSeconds(3600);
} }
} catch (java.text.ParseException e) {
fail("unable to parse start time specifiction: " + e);
}
if ((optstr = cli.getOptionValue('e')) != null) { try {
expire = convertDuration(start, optstr); optstr = cliOption("e", expireOptionKeys, null);
if (optstr != null) {
expire = Utils.convertDuration(start, optstr);
} else { } else {
expire = convertDuration(start, "+2592000"); // 30 days expire = Utils.convertDuration(start, "+2592000"); // 30 days
} }
} catch (java.text.ParseException e) {
fail("Unable to parse expire time specification: " + e);
}
outputfile = cli.getOptionValue('f'); outputfile = cli.getOptionValue('f');
String[] files = cli.getArgs(); String[] files = cli.getArgs();
if (files.length < 1) { if (files.length < 1) {
System.err.println("error: missing zone file and/or key files"); fail("missing zone file and/or key files");
usage(); }
}
inputfile = files[0]; inputfile = files[0];
if (files.length > 1) { if (files.length > 1) {
keyFiles = new String[files.length - 1]; keyFiles = new String[files.length - 1];
System.arraycopy(files, 1, keyFiles, 0, files.length - 1); System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
}
} }
} }
/** /**
* Verify the generated signatures. * Verify the generated signatures.
* *
* @param records * @param records a list of {@link org.xbill.DNS.Record}s.
* a list of {@link org.xbill.DNS.Record}s. * @param keypairs a list of keypairs used the sign the zone.
* @param keypairs
* a list of keypairs used the sign the zone.
* @return true if all of the signatures validated. * @return true if all of the signatures validated.
*/ */
private static boolean verifySigs(List<Record> records, List<DnsKeyPair> keypairs) { private boolean verifySigs(List<Record> records, List<DnsKeyPair> keypairs) {
boolean secure = true; boolean secure = true;
DnsSecVerifier verifier = new DnsSecVerifier(); DnsSecVerifier verifier = new DnsSecVerifier();
@ -165,7 +170,7 @@ public class SignRRset extends CLBase {
boolean result = verifier.verify(rrset); boolean result = verifier.verify(rrset);
if (!result) { if (!result) {
staticLog.fine("Signatures did not verify for RRset: " + rrset); log.fine("Signatures did not verify for RRset: " + rrset);
secure = false; secure = false;
} }
} }
@ -176,18 +181,15 @@ public class SignRRset extends CLBase {
/** /**
* Load the key pairs from the key files. * Load the key pairs from the key files.
* *
* @param keyfiles * @param keyfiles a string array containing the base names or paths of the
* a string array containing the base names or paths of the * keys to be loaded.
* keys * @param startIndex the starting index of keyfiles string array to use. This
* to be loaded. * allows us to use the straight command line argument
* @param startIndex * array.
* the starting index of keyfiles string array to use. This * @param inDirectory the directory to look in (may be null).
* allows us to use the straight command line argument array.
* @param inDirectory
* the directory to look in (may be null).
* @return a list of keypair objects. * @return a list of keypair objects.
*/ */
private static List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex, private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
File inDirectory) throws IOException { File inDirectory) throws IOException {
if (keyfiles == null) if (keyfiles == null)
return new ArrayList<>(); return new ArrayList<>();
@ -209,10 +211,9 @@ public class SignRRset extends CLBase {
public void execute() throws Exception { public void execute() throws Exception {
// Read in the zone // Read in the zone
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null); List<Record> records = ZoneUtils.readZoneFile(inputfile, null);
if (records == null || records.isEmpty()) { if (records == null || records.isEmpty()) {
System.err.println("error: empty RRset file"); fail("empty RRset file");
state.usage();
} }
// Construct the RRset. Complain if the records in the input file // Construct the RRset. Complain if the records in the input file
// consist of more than one RRset. // consist of more than one RRset.
@ -235,25 +236,21 @@ public class SignRRset extends CLBase {
&& rrset.getDClass() == r.getDClass()) { && rrset.getDClass() == r.getDClass()) {
rrset.addRR(r); rrset.addRR(r);
} else { } else {
System.err.println("Records do not all belong to the same RRset."); fail("records do not all belong to the same RRset");
state.usage();
} }
} }
if (rrset == null || rrset.size() == 0) { if (rrset == null || rrset.size() == 0) {
System.err.println("No records found in inputfile."); fail("no records found in inputfile");
state.usage();
return;
} }
// Load the key pairs. // Load the key pairs.
if (state.keyFiles.length == 0) { if (keyFiles.length == 0) {
System.err.println("error: at least one keyfile must be specified"); fail("at least one keyfile must be specified");
state.usage();
} }
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory); List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, keyDirectory);
// Make sure that all the keypairs have the same name. // Make sure that all the keypairs have the same name.
// This will be used as the zone name, too. // This will be used as the zone name, too.
@ -265,19 +262,18 @@ public class SignRRset extends CLBase {
continue; continue;
} }
if (!pair.getDNSKEYName().equals(keysetName)) { if (!pair.getDNSKEYName().equals(keysetName)) {
System.err.println("Keys do not all have the same name."); fail("keys do not all have the same name");
state.usage();
} }
} }
// default the output file, if not set. // default the output file, if not set.
if (state.outputfile == null && !state.inputfile.equals("-")) { if (outputfile == null && !inputfile.equals("-")) {
state.outputfile = state.inputfile + ".signed"; outputfile = inputfile + ".signed";
} }
JCEDnsSecSigner signer = new JCEDnsSecSigner(state.verboseSigning); JCEDnsSecSigner signer = new JCEDnsSecSigner(verboseSigning);
List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, state.start, state.expire); List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, start, expire);
for (RRSIGRecord s : sigs) { for (RRSIGRecord s : sigs) {
rrset.addRR(s); rrset.addRR(s);
} }
@ -292,9 +288,9 @@ public class SignRRset extends CLBase {
} }
// write out the signed zone // write out the signed zone
ZoneUtils.writeZoneFile(signedRecords, state.outputfile); ZoneUtils.writeZoneFile(signedRecords, outputfile);
if (state.verifySigs) { if (verifySigs) {
log.fine("verifying generated signatures"); log.fine("verifying generated signatures");
boolean res = verifySigs(signedRecords, keypairs); boolean res = verifySigs(signedRecords, keypairs);
@ -308,9 +304,8 @@ public class SignRRset extends CLBase {
} }
public static void main(String[] args) { public static void main(String[] args) {
SignRRset tool = new SignRRset(); SignRRset tool = new SignRRset("signrrset", "jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]");
tool.state = new CLIState();
tool.run(tool.state, args); tool.run(args);
} }
} }

View File

@ -28,10 +28,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC; import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
@ -54,234 +51,237 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
* @author David Blacka * @author David Blacka
*/ */
public class SignZone extends CLBase { public class SignZone extends CLBase {
private CLIState state; private File keyDirectory = null;
private File keysetDirectory = null;
private String[] kskFiles = null;
private String[] keyFiles = null;
private String zonefile = null;
private Instant start = null;
private Instant expire = null;
private String outputfile = null;
private boolean verifySigs = false;
private boolean useOptOut = false;
private boolean fullySignKeyset = false;
private List<Name> includeNames = null;
private boolean useNsec3 = false;
private byte[] salt = null;
private int iterations = 0;
private int digestId = DNSSEC.Digest.SHA256;
private long nsec3paramttl = -1;
private boolean verboseSigning = false;
/** private static final Random rand = new Random();
* This is an inner class used to hold all of the command line option state.
*/
private static class CLIState extends CLIStateBase {
public File keyDirectory = null;
public File keysetDirectory = null;
public String[] kskFiles = null;
public String[] keyFiles = null;
public String zonefile = null;
public Instant start = null;
public Instant expire = null;
public String outputfile = null;
public boolean verifySigs = false;
public boolean useOptOut = false;
public boolean fullySignKeyset = false;
public List<Name> includeNames = null;
public boolean useNsec3 = false;
public byte[] salt = null;
public int iterations = 0;
public int digestId = DNSSEC.Digest.SHA1;
public long nsec3paramttl = -1;
public boolean verboseSigning = false;
public CLIState() { public SignZone(String name, String usageStr) {
super("jdnssec-signzone [..options..] zone_file [key_file ...]"); super(name, usageStr);
}
protected void setupOptions() {
// boolean options
opts.addOption("a", "verify", false, "verify generated signatures>");
opts.addOption("F", "fully-sign-keyset", false,
"sign the zone apex keyset with all available keys.");
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keyset-directory")
.desc("directory to find keyset files (default '.')").build());
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
.desc("directory to find key files (default '.'").build());
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
.desc("signature starting time (default is now - 1 hour)").build());
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
.desc("signature expiration time (default is start-time + 30 days)").build());
opts.addOption(
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
opts.addOption(Option.builder("k").hasArgs().argName("KSK file").longOpt("ksk-file")
.desc("This key is a Key-Signing Key (may repeat)").build());
opts.addOption(Option.builder("I").hasArg().argName("file").longOpt("include-file")
.desc("include names in the file in the NSEC/NSEC3 chain").build());
// NSEC3 options
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
opts.addOption("O", "use-opt-out", false,
"generate a fully Opt-Out zone (only valid with NSEC3).");
opts.addOption(
Option.builder("S").hasArg().argName("hex value").longOpt("salt").desc("Supply a salt value").build());
opts.addOption(Option.builder("R").hasArg().argName("length").longOpt("random-salt")
.desc("Generate a random salt of <length>").build());
opts.addOption(Option.builder("H").hasArg().argName("count").longOpt("iterations")
.desc("Use this many addtional iterations in NSEC3 (default 0)").build());
opts.addOption(Option.builder().hasArg().longOpt("nsec3paramttl").argName("ttl")
.desc("Use this TTL for the NSEC3PARAM record (default is min(soa.min, soa.ttl))").build());
opts.addOption(Option.builder().hasArg().argName("id").longOpt("ds-digest")
.desc("Digest algorithm to use for generated DS records").build());
}
protected void processOptions() {
String[] verifyOptionKeys = { "verify_signatures", "verify" };
String[] nsec3OptionKeys = { "use_nsec3", "nsec3" };
String[] optOutOptionKeys = { "use_opt_out", "opt_out" };
String[] verboseSigningOptionKeys = { "verbose_signing" };
String[] fullySignKeysetOptionKeys = { "fully_sign_keyset", "fully_sign" };
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
String[] inceptionOptionKeys = { "inception", "start" };
String[] expireOptionKeys = { "expire" };
String[] nsec3SaltOptionKeys = { "nsec3_salt", "salt" };
String[] randomSaltOptionKeys = { "nsec3_random_salt_length", "nsec3_salt_length", "random_salt_length" };
String[] nsec3IterationsOptionKeys = { "nsec3_iterations", "iterations" };
String[] digestAlgOptionKeys = { "digest_algorithm", "digest_id" };
String[] nsec3paramTTLOptionKeys = { "nsec3param_ttl" };
String[] incudeNamesOptionKeys = { "include_names_file", "include_names" };
String optstr = null;
verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
useNsec3 = cliBooleanOption("3", nsec3OptionKeys, false);
useOptOut = cliBooleanOption("O", optOutOptionKeys, false);
verboseSigning = cliBooleanOption("V", verboseSigningOptionKeys, false);
if (useOptOut && !useNsec3) {
System.err.println("Opt-Out not supported without NSEC3 -- ignored.");
useOptOut = false;
} }
@Override fullySignKeyset = cliBooleanOption("F", fullySignKeysetOptionKeys, false);
protected void setupOptions(Options opts) {
// boolean options
opts.addOption("a", "verify", false, "verify generated signatures>");
opts.addOption("F", "fully-sign-keyset", false,
"sign the zone apex keyset with all available keys.");
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keyset-directory")
.desc("directory to find keyset files (default '.')").build());
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
.desc("directory to find key files (default '.'").build());
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
.desc("signature starting time (default is now - 1 hour)").build());
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
.desc("signature expiration time (default is start-time + 30 days)").build());
opts.addOption(
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
opts.addOption(Option.builder("k").hasArgs().argName("KSK file").longOpt("ksk-file")
.desc("This key is a Key-Signing Key (may repeat)").build());
opts.addOption(Option.builder("I").hasArg().argName("file").longOpt("include-file")
.desc("include names in the file in the NSEC/NSEC3 chain").build());
// NSEC3 options
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
opts.addOption("O", "use-opt-out", false,
"generate a fully Opt-Out zone (only valid with NSEC3).");
opts.addOption(
Option.builder("S").hasArg().argName("hex value").longOpt("salt").desc("Supply a salt value").build());
opts.addOption(Option.builder("R").hasArg().argName("length").longOpt("random-salt")
.desc("Generate a random salt of <length>").build());
opts.addOption(Option.builder("H").hasArg().argName("count").longOpt("iterations")
.desc("Use this many addtional iterations in NSEC3 (default 0)").build());
opts.addOption(Option.builder().hasArg().longOpt("nsec3paramttl").argName("ttl")
.desc("Use this TTL for the NSEC3PARAM record (default is min(soa.min, soa.ttl))").build());
opts.addOption(Option.builder().hasArg().argName("id").longOpt("ds-digest")
.desc("Digest algorithm to use for generated DS records").build());
optstr = cliOption("D", keyDirectoryOptionKeys, null);
if (optstr != null) {
keyDirectory = new File(optstr);
if (!keyDirectory.isDirectory()) {
fail("key directory " + optstr + " is not a directory");
}
} }
@Override try {
protected void processOptions(CommandLine cli) throws ParseException { optstr = cliOption("s", inceptionOptionKeys, null);
String optstr = null; if (optstr != null) {
start = Utils.convertDuration(null, optstr);
if (cli.hasOption('a'))
verifySigs = true;
if (cli.hasOption('3'))
useNsec3 = true;
if (cli.hasOption('O'))
useOptOut = true;
if (cli.hasOption('V'))
verboseSigning = true;
if (useOptOut && !useNsec3) {
System.err.println("Opt-Out not supported without NSEC3 -- ignored.");
useOptOut = false;
}
if (cli.hasOption('F'))
fullySignKeyset = true;
if ((optstr = cli.getOptionValue('d')) != null) {
keysetDirectory = new File(optstr);
if (!keysetDirectory.isDirectory()) {
System.err.println("error: " + optstr + " is not a directory");
usage();
}
}
if ((optstr = cli.getOptionValue('D')) != null) {
keyDirectory = new File(optstr);
if (!keyDirectory.isDirectory()) {
System.err.println("error: " + optstr + " is not a directory");
usage();
}
}
if ((optstr = cli.getOptionValue('s')) != null) {
start = CLBase.convertDuration(null, optstr);
} else { } else {
// default is now - 1 hour. // default is now - 1 hour.
start = Instant.now().minusSeconds(3600); start = Instant.now().minusSeconds(3600);
} }
} catch (java.text.ParseException e) {
fail("unable to parse start time specifiction: " + e);
}
if ((optstr = cli.getOptionValue('e')) != null) { try {
expire = CLBase.convertDuration(start, optstr); optstr = cliOption("e", expireOptionKeys, null);
if (optstr != null) {
expire = Utils.convertDuration(start, optstr);
} else { } else {
expire = CLBase.convertDuration(start, "+2592000"); // 30 days expire = Utils.convertDuration(start, "+2592000"); // 30 days
} }
} catch (java.text.ParseException e) {
fail("missing zone file and/or key files");
}
outputfile = cli.getOptionValue('f'); outputfile = cli.getOptionValue('f');
kskFiles = cli.getOptionValues('k'); kskFiles = cli.getOptionValues('k');
if ((optstr = cli.getOptionValue('I')) != null) { optstr = cliOption("S", nsec3SaltOptionKeys, null);
File includeNamesFile = new File(optstr); if (optstr != null) {
try { salt = base16.fromString(optstr);
includeNames = CLIState.getNameList(includeNamesFile); if (salt == null && !optstr.equals("-")) {
} catch (IOException e) { fail("salt is not valid hexidecimal");
throw new ParseException(e.getMessage());
}
}
if ((optstr = cli.getOptionValue('S')) != null) {
salt = base16.fromString(optstr);
if (salt == null && !optstr.equals("-")) {
System.err.println("error: salt is not valid hexidecimal.");
usage();
}
}
if ((optstr = cli.getOptionValue('R')) != null) {
int length = parseInt(optstr, 0);
if (length > 0 && length <= 255) {
Random random = new Random();
salt = new byte[length];
random.nextBytes(salt);
}
}
if ((optstr = cli.getOptionValue("iterations")) != null) {
iterations = parseInt(optstr, iterations);
if (iterations < 0 || iterations > 8388607) {
System.err.println("error: iterations value is invalid");
usage();
}
}
if ((optstr = cli.getOptionValue("ds-digest")) != null) {
digestId = parseInt(optstr, -1);
if (digestId < 0) {
System.err.println("error: DS digest ID is not a valid identifier");
usage();
}
}
if ((optstr = cli.getOptionValue("nsec3paramttl")) != null) {
nsec3paramttl = parseInt(optstr, -1);
}
String[] files = cli.getArgs();
if (files.length < 1) {
System.err.println("error: missing zone file and/or key files");
usage();
}
zonefile = files[0];
if (files.length > 1) {
keyFiles = new String[files.length - 1];
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
} }
} }
/** optstr = cliOption("R", randomSaltOptionKeys, null);
* Load a list of DNS names from a file. if (optstr != null) {
* int length = Utils.parseInt(optstr, 0);
* @param nameListFile if (length > 0 && length <= 255) {
* the path of a file containing a bare list of DNS names. salt = new byte[length];
* @return a list of {@link org.xbill.DNS.Name} objects. rand.nextBytes(salt);
*/
private static List<Name> getNameList(File nameListFile) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(nameListFile))) {
List<Name> res = new ArrayList<>();
String line = null;
while ((line = br.readLine()) != null) {
try {
Name n = Name.fromString(line);
// force the name to be absolute.
// FIXME: we should probably get some fancy logic here to
// detect if the name needs the origin appended, or just the
// root.
if (!n.isAbsolute())
n = Name.concatenate(n, Name.root);
res.add(n);
} catch (TextParseException e) {
staticLog.severe("DNS Name parsing error:" + e);
}
}
return res;
} }
} }
iterations = cliIntOption("iterations", nsec3IterationsOptionKeys, 0);
if (iterations > 150) {
log.warning("NSEC3 iterations value is too high for normal use: " + iterations
+ " is greater than current accepted threshold of 150");
}
optstr = cliOption("ds-digest", digestAlgOptionKeys, Integer.toString(digestId));
digestId = DNSSEC.Digest.value(optstr);
nsec3paramttl = cliIntOption("nsec3paramttl", nsec3paramTTLOptionKeys, -1);
optstr = cliOption("I", incudeNamesOptionKeys, null);
if (optstr != null) {
File includeNamesFile = new File(optstr);
try {
includeNames = getNameList(includeNamesFile);
} catch (IOException e) {
fail("unable to load include-names file: " + e);
}
}
String[] files = cli.getArgs();
if (files.length < 1) {
fail("missing zone file and/or key files");
}
zonefile = files[0];
if (files.length > 1) {
keyFiles = new String[files.length - 1];
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
}
log.fine("SignZone settings => key_directory: " + keyDirectory +
", keyset_directory: " + keysetDirectory +
", start: " + start.getEpochSecond() +
", expire: " + expire.getEpochSecond() +
", verify_sigs: " + verifySigs +
", use_nsec3: " + useNsec3 +
", use_opt_out = " + useOptOut +
", salt: " + DnsKeyPair.toHex(salt) +
", iterations: " + iterations +
", nsec3param_ttl: " + nsec3paramttl +
", fully_sign_keyset: " + fullySignKeyset +
", digest_id: " + digestId +
", verbose_signing: " + verboseSigning);
}
/**
* Load a list of DNS names from a file.
*
* @param nameListFile the path of a file containing a bare list of DNS
* names.
* @return a list of {@link org.xbill.DNS.Name} objects.
*/
private List<Name> getNameList(File nameListFile) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(nameListFile))) {
List<Name> res = new ArrayList<>();
String line = null;
while ((line = br.readLine()) != null) {
try {
Name n = Name.fromString(line);
// force the name to be absolute.
if (!n.isAbsolute())
n = Name.concatenate(n, Name.root);
res.add(n);
} catch (TextParseException e) {
log.severe("DNS Name parsing error:" + e);
}
}
return res;
}
} }
/** /**
* Verify the generated signatures. * Verify the generated signatures.
* *
* @param records * @param records a list of {@link org.xbill.DNS.Record}s.
* a list of {@link org.xbill.DNS.Record}s. * @param keypairs a list of keypairs used the sign the zone.
* @param keypairs
* a list of keypairs used the sign the zone.
* @return true if all of the signatures validated. * @return true if all of the signatures validated.
*/ */
private static boolean verifyZoneSigs(List<Record> records, private boolean verifyZoneSigs(List<Record> records,
List<DnsKeyPair> keypairs) { List<DnsKeyPair> keypairs, List<DnsKeyPair> kskpairs) {
boolean secure = true; boolean secure = true;
DnsSecVerifier verifier = new DnsSecVerifier(); DnsSecVerifier verifier = new DnsSecVerifier();
@ -289,7 +289,9 @@ public class SignZone extends CLBase {
for (DnsKeyPair pair : keypairs) { for (DnsKeyPair pair : keypairs) {
verifier.addTrustedKey(pair); verifier.addTrustedKey(pair);
} }
for (DnsKeyPair pair : kskpairs) {
verifier.addTrustedKey(pair);
}
verifier.setVerifyAllSigs(true); verifier.setVerifyAllSigs(true);
List<RRset> rrsets = SignUtils.assembleIntoRRsets(records); List<RRset> rrsets = SignUtils.assembleIntoRRsets(records);
@ -303,7 +305,8 @@ public class SignZone extends CLBase {
boolean result = verifier.verify(rrset); boolean result = verifier.verify(rrset);
if (!result) { if (!result) {
staticLog.fine("Signatures did not verify for RRset: " + rrset); System.err.println("Signatures did not verify for RRset: " + rrset);
log.fine("Signatures did not verify for RRset: " + rrset);
secure = false; secure = false;
} }
} }
@ -314,20 +317,15 @@ public class SignZone extends CLBase {
/** /**
* Load the key pairs from the key files. * Load the key pairs from the key files.
* *
* @param keyfiles * @param keyfiles a string array containing the base names or paths of the
* a string array containing the base names or paths of the * keys to be loaded.
* keys to * @param startIndex the starting index of keyfiles string array to use. This
* be loaded. * allows us to use the straight command line argument
* @param startIndex * array.
* the starting index of keyfiles string array to use. This * @param inDirectory the directory to look in (may be null).
* allows
* us
* to use the straight command line argument array.
* @param inDirectory
* the directory to look in (may be null).
* @return a list of keypair objects. * @return a list of keypair objects.
*/ */
private static List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex, private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
File inDirectory) throws IOException { File inDirectory) throws IOException {
if (keyfiles == null) if (keyfiles == null)
return new ArrayList<>(); return new ArrayList<>();
@ -348,7 +346,7 @@ public class SignZone extends CLBase {
return keys; return keys;
} }
private static List<DnsKeyPair> getKeys(List<Record> dnskeyrrs, File inDirectory) private List<DnsKeyPair> getKeys(List<Record> dnskeyrrs, File inDirectory)
throws IOException { throws IOException {
List<DnsKeyPair> res = new ArrayList<>(); List<DnsKeyPair> res = new ArrayList<>();
for (Record r : dnskeyrrs) { for (Record r : dnskeyrrs) {
@ -382,7 +380,7 @@ public class SignZone extends CLBase {
} }
} }
private static List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename) private List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename)
throws IOException { throws IOException {
if (inDirectory == null) { if (inDirectory == null) {
inDirectory = new File("."); inDirectory = new File(".");
@ -418,21 +416,14 @@ public class SignZone extends CLBase {
/** /**
* Load keysets (which contain delegation point security info). * Load keysets (which contain delegation point security info).
* *
* @param inDirectory * @param inDirectory the directory to look for the keyset files (may be null,
* the directory to look for the keyset files (may be null, * in which case it defaults to looking in the current
* in * working directory).
* which * @param zonename the name of the zone we are signing, so we can ignore
* case it defaults to looking in the current working * keysets that do not belong in the zone.
* directory). * @return a list of {@link org.xbill.DNS.Record}s found in the keyset files.
* @param zonename
* the name of the zone we are signing, so we can ignore
* keysets
* that
* do not belong in the zone.
* @return a list of {@link org.xbill.DNS.Record}s found in the keyset
* files.
*/ */
private static List<Record> getKeysets(File inDirectory, Name zonename) private List<Record> getKeysets(File inDirectory, Name zonename)
throws IOException { throws IOException {
if (inDirectory == null) { if (inDirectory == null) {
inDirectory = new File("."); inDirectory = new File(".");
@ -463,13 +454,9 @@ public class SignZone extends CLBase {
/** /**
* Determine if the given keypairs can be used to sign the zone. * Determine if the given keypairs can be used to sign the zone.
* *
* @param zonename * @param zonename the zone origin.
* the zone origin. * @param keypairs a list of {@link DnsKeyPair} objects that will be used to
* @param keypairs * sign the zone.
* a list of {@link DnsKeyPair} objects that will be used to
* sign
* the
* zone.
* @return true if the keypairs valid. * @return true if the keypairs valid.
*/ */
private static boolean keyPairsValidForZone(Name zonename, List<DnsKeyPair> keypairs) { private static boolean keyPairsValidForZone(Name zonename, List<DnsKeyPair> keypairs) {
@ -487,136 +474,149 @@ public class SignZone extends CLBase {
} }
public void execute() throws Exception { public void execute() throws Exception {
// Do a basic existence check for the zonefile first.
if (!zonefile.equals("-")) {
File f = new File(zonefile);
if (!f.exists()) {
fail("zonefile '" + zonefile + "' does not exist");
}
}
// Read in the zone // Read in the zone
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null); List<Record> records = ZoneUtils.readZoneFile(zonefile, null);
if (records == null || records.isEmpty()) { if (records == null || records.isEmpty()) {
System.err.println("error: empty zone file"); fail("empty zone file");
state.usage();
return;
} }
// calculate the zone name. // calculate the zone name.
Name zonename = ZoneUtils.findZoneName(records); Name zonename = ZoneUtils.findZoneName(records);
if (zonename == null) { if (zonename == null) {
System.err.println("error: invalid zone file - no SOA"); fail("invalid zone file - no SOA");
state.usage();
return;
} }
// Load the key pairs. // Load the key pairs. Note that getKeys() always returns an ArrayList,
// which may be empty.
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory); List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, keyDirectory);
List<DnsKeyPair> kskpairs = getKeys(state.kskFiles, 0, state.keyDirectory); List<DnsKeyPair> kskpairs = getKeys(kskFiles, 0, keyDirectory);
// If we didn't get any keys on the command line, look at the zone apex for // If we didn't get any keys on the command line, look at the zone apex for
// any public keys. // any public keys.
if (keypairs == null && kskpairs == null) { if (keypairs.isEmpty()) {
List<Record> dnskeys = ZoneUtils.findRRs(records, zonename, Type.DNSKEY); List<Record> dnskeys = ZoneUtils.findRRs(records, zonename, Type.DNSKEY);
keypairs = getKeys(dnskeys, state.keyDirectory); keypairs = getKeys(dnskeys, keyDirectory);
} }
// If we *still* don't have any key pairs, look for keys the key directory // If we *still* don't have any key pairs, look for keys the key directory
// that match // that match
if (keypairs == null && kskpairs == null) { if (keypairs.isEmpty()) {
keypairs = findZoneKeys(state.keyDirectory, zonename); keypairs = findZoneKeys(keyDirectory, zonename);
} }
// If we don't have any KSKs, but we do have more than one zone // If we don't have any KSKs, but we do have more than one zone
// signing key (presumably), presume that the zone signing keys // signing key (presumably), presume that the zone signing keys
// are just not differentiated and try to figure out which keys // are just not differentiated and try to figure out which keys
// are actually ksks by looking at the SEP flag. // are actually KSKs by looking at the SEP flag.
if ((kskpairs == null || kskpairs.isEmpty()) && keypairs != null if (kskpairs.isEmpty() && !keypairs.isEmpty()) {
&& keypairs.size() > 1) {
for (Iterator<DnsKeyPair> i = keypairs.iterator(); i.hasNext();) { for (Iterator<DnsKeyPair> i = keypairs.iterator(); i.hasNext();) {
DnsKeyPair pair = i.next(); DnsKeyPair pair = i.next();
DNSKEYRecord kr = pair.getDNSKEYRecord(); DNSKEYRecord kr = pair.getDNSKEYRecord();
if ((kr.getFlags() & DNSKEYRecord.Flags.SEP_KEY) != 0) { if ((kr.getFlags() & DNSKEYRecord.Flags.SEP_KEY) != 0) {
if (kskpairs == null)
kskpairs = new ArrayList<>();
kskpairs.add(pair); kskpairs.add(pair);
i.remove(); i.remove();
} }
} }
} }
// If there are no ZSKs defined at this point (yet there are KSKs // If we have zero keypairs at all, we are stuck.
// provided), all KSKs will be treated as ZSKs, as well. if (keypairs.isEmpty() && kskpairs.isEmpty()) {
if (keypairs == null || keypairs.isEmpty()) { fail("no zone signing keys could be determined");
keypairs = kskpairs;
} }
// If there *still* aren't any ZSKs defined, bail. // If we only have one type of key (all ZSKs or all KSKs), then these are
if (keypairs == null || keypairs.isEmpty()) { // "CSKs" -- Combined signing keys, so assign one set to the other.
System.err.println("No zone signing keys could be determined."); if (keypairs.isEmpty()) {
state.usage(); keypairs = kskpairs;
return; } else if (kskpairs.isEmpty()) {
kskpairs = keypairs;
}
// Output what keys we are using for what
if (keypairs == kskpairs) {
System.out.println("CSKs: ");
for (DnsKeyPair kp : keypairs) {
System.out.println(" - " + kp);
}
} else {
System.out.println("KSKs: ");
for (DnsKeyPair kp : kskpairs) {
System.out.println(" - " + kp);
}
System.out.println("ZSKs: ");
for (DnsKeyPair kp : keypairs) {
System.out.println(" - " + kp);
}
} }
// default the output file, if not set. // default the output file, if not set.
if (state.outputfile == null && !state.zonefile.equals("-")) { if (outputfile == null && !zonefile.equals("-")) {
if (zonename.isAbsolute()) { if (zonename.isAbsolute()) {
state.outputfile = zonename + "signed"; outputfile = zonename + "signed";
} else { } else {
state.outputfile = zonename + ".signed"; outputfile = zonename + ".signed";
} }
} }
// Verify that the keys can be in the zone. // Verify that the keys can be in the zone.
if (!keyPairsValidForZone(zonename, keypairs) if (!keyPairsValidForZone(zonename, keypairs)
|| !keyPairsValidForZone(zonename, kskpairs)) { || !keyPairsValidForZone(zonename, kskpairs)) {
System.err.println("error: specified keypairs are not valid for the zone."); fail("specified keypairs are not valid for the zone.");
state.usage();
} }
// We force the signing keys to be in the zone by just appending // We force the signing keys to be in the zone by just appending
// them to the zone here. Currently JCEDnsSecSigner.signZone // them to the zone here. Currently JCEDnsSecSigner.signZone
// removes duplicate records. // removes duplicate records.
if (kskpairs != null) { if (!kskpairs.isEmpty()) {
for (DnsKeyPair pair : kskpairs) { for (DnsKeyPair pair : kskpairs) {
records.add(pair.getDNSKEYRecord()); records.add(pair.getDNSKEYRecord());
} }
} }
if (keypairs != null) { if (!keypairs.isEmpty()) {
for (DnsKeyPair pair : keypairs) { for (DnsKeyPair pair : keypairs) {
records.add(pair.getDNSKEYRecord()); records.add(pair.getDNSKEYRecord());
} }
} }
// read in the keysets, if any. // read in the keysets, if any.
List<Record> keysetrecs = getKeysets(state.keysetDirectory, zonename); List<Record> keysetrecs = getKeysets(keysetDirectory, zonename);
if (keysetrecs != null) { if (keysetrecs != null) {
records.addAll(keysetrecs); records.addAll(keysetrecs);
} }
JCEDnsSecSigner signer = new JCEDnsSecSigner(state.verboseSigning); JCEDnsSecSigner signer = new JCEDnsSecSigner(verboseSigning);
// Sign the zone. // Sign the zone.
List<Record> signedRecords; List<Record> signedRecords;
if (state.useNsec3) { if (useNsec3) {
signedRecords = signer.signZoneNSEC3(zonename, records, kskpairs, keypairs, signedRecords = signer.signZoneNSEC3(zonename, records, kskpairs, keypairs,
state.start, state.expire, start, expire,
state.fullySignKeyset, state.useOptOut, fullySignKeyset, useOptOut,
state.includeNames, state.salt, includeNames, salt,
state.iterations, state.digestId, iterations, digestId,
state.nsec3paramttl); nsec3paramttl);
} else { } else {
signedRecords = signer.signZone(zonename, records, kskpairs, keypairs, signedRecords = signer.signZone(zonename, records, kskpairs, keypairs,
state.start, state.expire, state.fullySignKeyset, start, expire, fullySignKeyset,
state.digestId); digestId);
} }
// write out the signed zone // write out the signed zone
ZoneUtils.writeZoneFile(signedRecords, state.outputfile); ZoneUtils.writeZoneFile(signedRecords, outputfile);
System.out.println("zone signing complete");
if (state.verifySigs) {
// FIXME: ugh.
if (kskpairs != null) {
keypairs.addAll(kskpairs);
}
if (verifySigs) {
log.fine("verifying generated signatures"); log.fine("verifying generated signatures");
boolean res = verifyZoneSigs(signedRecords, keypairs); boolean res = verifyZoneSigs(signedRecords, keypairs, kskpairs);
if (res) { if (res) {
System.out.println("Generated signatures verified"); System.out.println("Generated signatures verified");
@ -624,13 +624,11 @@ public class SignZone extends CLBase {
System.out.println("Generated signatures did not verify."); System.out.println("Generated signatures did not verify.");
} }
} }
} }
public static void main(String[] args) { public static void main(String[] args) {
SignZone tool = new SignZone(); SignZone tool = new SignZone("signzone", "jdnssec-signzone [..options..] zone_file [key_file ...]");
tool.state = new CLIState();
tool.run(tool.state, args); tool.run(args);
} }
} }

View File

@ -0,0 +1,103 @@
package com.verisignlabs.dnssec.cl;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;
import com.verisignlabs.dnssec.security.DnsKeyAlgorithm;
public class Utils {
private Utils() {
}
/**
* Parse a string into an integer safely, using a default if the value does not
* parse cleanly
*
* @param s The string to parse
* @param def The default value
* @return either the parsed int or the default
*/
public static int parseInt(String s, int def) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return def;
}
}
/**
* Parse a string into a long safely, using a default if the value does not
* parse cleanly
*
* @param s The string to parse
* @param def The default value
* @return either the parsed long or the default
*/
public static long parseLong(String s, long def) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
return def;
}
}
/**
* Parse a DNSSEC algorithm number of mnemonic into the official algorithm number.
* @param s The arge value
* @return A DNSSEC algorithm number, or -1 if unrecognized.
*/
public static int parseAlg(String s) {
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
int alg = Utils.parseInt(s, -1);
if (alg > 0) {
if (algs.supportedAlgorithm(alg))
return alg;
return -1;
}
return algs.stringToAlgorithm(s);
}
/**
* Calculate a date/time from a command line time/offset duration string.
*
* @param start the start time to calculate offsets from.
* @param duration the time/offset string to parse.
* @return the calculated time.
*/
public static Instant convertDuration(Instant start, String duration) throws java.text.ParseException {
if (start == null) {
start = Instant.now();
}
if (duration.startsWith("now")) {
start = Instant.now();
if (duration.indexOf("+") < 0)
return start;
duration = duration.substring(3);
}
if (duration.startsWith("+")) {
long offset = parseLong(duration.substring(1), 0);
return start.plusSeconds(offset);
}
// This is a heuristic to distinguish UNIX epoch times from the zone file
// format standard (which is length == 14)
if (duration.length() <= 10) {
long epoch = parseLong(duration, 0);
return Instant.ofEpochSecond(epoch);
}
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
Date parsedDate = dateFormatter.parse(duration);
return parsedDate.toInstant();
}
}

View File

@ -20,10 +20,7 @@ package com.verisignlabs.dnssec.cl;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.ParseException;
import org.xbill.DNS.Record; import org.xbill.DNS.Record;
import com.verisignlabs.dnssec.security.ZoneUtils; import com.verisignlabs.dnssec.security.ZoneUtils;
@ -35,100 +32,75 @@ import com.verisignlabs.dnssec.security.ZoneVerifier;
* @author David Blacka * @author David Blacka
*/ */
public class VerifyZone extends CLBase { public class VerifyZone extends CLBase {
private String zonefile = null;
private String[] keyfiles = null;
private int startfudge = 0;
private int expirefudge = 0;
private boolean ignoreTime = false;
private boolean ignoreDups = false;
private Instant currentTime = null;
private CLIState state; public VerifyZone(String name, String usageStr) {
super(name, usageStr);
}
/** protected void setupOptions() {
* This is a small inner class used to hold all of the command line option opts.addOption(Option.builder("S").hasArg().argName("seconds").longOpt("sig-start-fudge")
* state. .desc("'fudge' RRSIG inception ties by 'seconds'").build());
*/ opts.addOption(Option.builder("E").hasArg().argName("seconds").longOpt("sig-expire-fudge")
protected static class CLIState extends CLIStateBase { .desc("'fudge' RRSIG expiration times by 'seconds'").build());
public String zonefile = null; opts.addOption(Option.builder("t").hasArg().argName("time").longOpt("use-time")
public String[] keyfiles = null; .desc("Use 'time' as the time for verification purposes.").build());
public int startfudge = 0;
public int expirefudge = 0;
public boolean ignoreTime = false;
public boolean ignoreDups = false;
public Instant currentTime = null;
public CLIState() { opts.addOption(
super("jdnssec-verifyzone [..options..] zonefile"); Option.builder().longOpt("ignore-time").desc("Ignore RRSIG inception and expiration time errors.").build());
opts.addOption(Option.builder().longOpt("ignore-duplicate-rrs").desc("Ignore duplicate record errors.").build());
}
protected void processOptions() {
String[] ignoreTimeOptionKeys = { "ignore_time" };
String[] ignoreDuplicateOptionKeys = { "ingore_duplicate_rrs", "ignore_duplicates" };
String[] startFudgeOptionKeys = { "start_fudge" };
String[] expireFudgeOptionKeys = { "expire_fudge" };
String[] currentTimeOptionKeys = { "current_time" };
ignoreTime = cliBooleanOption("ignore-time", ignoreTimeOptionKeys, false);
ignoreDups = cliBooleanOption("ignore-duplicate-rrs", ignoreDuplicateOptionKeys, false);
startfudge = cliIntOption("S", startFudgeOptionKeys, 0);
expirefudge = cliIntOption("E", expireFudgeOptionKeys, 0);
String optstr = cliOption("t", currentTimeOptionKeys, null);
if (optstr != null) {
try {
currentTime = Utils.convertDuration(null, optstr);
} catch (java.text.ParseException e) {
fail("could not parse timespec");
}
} }
@Override String[] args = cli.getArgs();
protected void setupOptions(Options opts) {
opts.addOption(Option.builder("S").hasArg().argName("seconds").longOpt("sig-start-fudge")
.desc("'fudge' RRSIG inception ties by 'seconds'").build());
opts.addOption(Option.builder("E").hasArg().argName("seconds").longOpt("sig-expire-fudge")
.desc("'fudge' RRSIG expiration times by 'seconds'").build());
opts.addOption(Option.builder("t").hasArg().argName("time").longOpt("use-time")
.desc("Use 'time' as the time for verification purposes.").build());
opts.addOption( if (args.length < 1) {
Option.builder().longOpt("ignore-time").desc("Ignore RRSIG inception and expiration time errors.").build()); fail("missing zone file");
opts.addOption(Option.builder().longOpt("ignore-duplicate-rrs").desc("Ignore duplicate record errors.").build());
} }
@Override zonefile = args[0];
protected void processOptions(CommandLine cli) {
if (cli.hasOption("ignore-time")) {
ignoreTime = true;
}
if (cli.hasOption("ignore-duplicate-rrs")) { if (args.length >= 2) {
ignoreDups = true; keyfiles = new String[args.length - 1];
} System.arraycopy(args, 1, keyfiles, 0, keyfiles.length);
String optstr = null;
if ((optstr = cli.getOptionValue('S')) != null) {
startfudge = parseInt(optstr, 0);
}
if ((optstr = cli.getOptionValue('E')) != null) {
expirefudge = parseInt(optstr, 0);
}
if ((optstr = cli.getOptionValue('t')) != null) {
try {
currentTime = convertDuration(null, optstr);
} catch (ParseException e) {
System.err.println("error: could not parse timespec");
usage();
}
}
String[] optstrs = null;
if ((optstrs = cli.getOptionValues('A')) != null) {
for (int i = 0; i < optstrs.length; i++) {
addArgAlias(optstrs[i]);
}
}
String[] args = cli.getArgs();
if (args.length < 1) {
System.err.println("error: missing zone file");
usage();
}
zonefile = args[0];
if (args.length >= 2) {
keyfiles = new String[args.length - 1];
System.arraycopy(args, 1, keyfiles, 0, keyfiles.length);
}
} }
} }
public void execute() throws Exception { public void execute() throws Exception {
ZoneVerifier zoneverifier = new ZoneVerifier(); ZoneVerifier zoneverifier = new ZoneVerifier();
zoneverifier.getVerifier().setStartFudge(state.startfudge); zoneverifier.getVerifier().setStartFudge(startfudge);
zoneverifier.getVerifier().setExpireFudge(state.expirefudge); zoneverifier.getVerifier().setExpireFudge(expirefudge);
zoneverifier.getVerifier().setIgnoreTime(state.ignoreTime); zoneverifier.getVerifier().setIgnoreTime(ignoreTime);
zoneverifier.getVerifier().setCurrentTime(state.currentTime); zoneverifier.getVerifier().setCurrentTime(currentTime);
zoneverifier.setIgnoreDuplicateRRs(state.ignoreDups); zoneverifier.setIgnoreDuplicateRRs(ignoreDups);
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null); List<Record> records = ZoneUtils.readZoneFile(zonefile, null);
log.fine("verifying zone..."); log.fine("verifying zone...");
int errors = zoneverifier.verifyZone(records); int errors = zoneverifier.verifyZone(records);
@ -144,9 +116,8 @@ public class VerifyZone extends CLBase {
} }
public static void main(String[] args) { public static void main(String[] args) {
VerifyZone tool = new VerifyZone(); VerifyZone tool = new VerifyZone("verifyzone", "jdnssec-verifyzone [..options..] zonefile");
tool.state = new CLIState();
tool.run(tool.state, args); tool.run(args);
} }
} }

View File

@ -24,9 +24,6 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.xbill.DNS.Master; import org.xbill.DNS.Master;
import org.xbill.DNS.NSEC3PARAMRecord; import org.xbill.DNS.NSEC3PARAMRecord;
import org.xbill.DNS.NSEC3Record; import org.xbill.DNS.NSEC3Record;
@ -45,43 +42,33 @@ import com.verisignlabs.dnssec.security.RecordComparator;
* @author David Blacka * @author David Blacka
*/ */
public class ZoneFormat extends CLBase { public class ZoneFormat extends CLBase {
private CLIState state; private String file;
private boolean assignNSEC3;
/** public ZoneFormat(String name, String usageStr) {
* This is a small inner class used to hold all of the command line option super(name, usageStr);
* state.
*/
protected static class CLIState extends CLIStateBase {
public String file;
public boolean assignNSEC3;
public CLIState() {
super("jdnssec-zoneformat [..options..] zonefile");
}
@Override
protected void setupOptions(Options opts) {
opts.addOption("N", "nsec3", false,
"attempt to determine the original ownernames for NSEC3 RRs.");
}
@Override
protected void processOptions(CommandLine cli) throws ParseException {
if (cli.hasOption('N'))
assignNSEC3 = true;
String[] args = cli.getArgs();
if (args.length < 1) {
System.err.println("error: must specify a zone file");
usage();
}
file = args[0];
}
} }
private static List<Record> readZoneFile(String filename) throws IOException { protected void setupOptions() {
opts.addOption("N", "nsec3", false,
"attempt to determine the original ownernames for NSEC3 RRs.");
}
protected void processOptions() {
String[] assignNsec3OwnersOptionKeys = { "assign_nsec3_owners", "assign_owners" };
assignNSEC3 = cliBooleanOption("N", assignNsec3OwnersOptionKeys, false);
String[] args = cli.getArgs();
if (args.length < 1) {
fail("must specify a zone file");
}
file = args[0];
}
private List<Record> readZoneFile(String filename) throws IOException {
try (Master master = new Master(filename)) { try (Master master = new Master(filename)) {
List<Record> res = new ArrayList<>(); List<Record> res = new ArrayList<>();
Record r = null; Record r = null;
@ -98,15 +85,14 @@ public class ZoneFormat extends CLBase {
} }
} }
private static void formatZone(List<Record> zone) { private void formatZone(List<Record> zone) {
for (Record r : zone) { for (Record r : zone) {
System.out.println(r.toString()); System.out.println(r.toString());
} }
} }
private static void determineNSEC3Owners(List<Record> zone) private void determineNSEC3Owners(List<Record> zone)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
// first, find the NSEC3PARAM record -- this is an inefficient linear // first, find the NSEC3PARAM record -- this is an inefficient linear
@ -174,11 +160,11 @@ public class ZoneFormat extends CLBase {
} }
public void execute() throws IOException, NoSuchAlgorithmException { public void execute() throws IOException, NoSuchAlgorithmException {
List<Record> z = readZoneFile(state.file); List<Record> z = readZoneFile(file);
// Put the zone into a consistent (name and RR type) order. // Put the zone into a consistent (name and RR type) order.
Collections.sort(z, new RecordComparator()); Collections.sort(z, new RecordComparator());
if (state.assignNSEC3) { if (assignNSEC3) {
determineNSEC3Owners(z); determineNSEC3Owners(z);
} else { } else {
formatZone(z); formatZone(z);
@ -186,10 +172,9 @@ public class ZoneFormat extends CLBase {
} }
public static void main(String[] args) { public static void main(String[] args) {
ZoneFormat tool = new ZoneFormat(); ZoneFormat tool = new ZoneFormat("zoneformat", "jdnssec-zoneformat [..options..] zonefile");
tool.state = new CLIState();
tool.run(tool.state, args); tool.run(args);
} }
} }

View File

@ -46,7 +46,8 @@ import org.xbill.DNS.utils.base64;
*/ */
public class BINDKeyUtils { public class BINDKeyUtils {
private BINDKeyUtils() { } private BINDKeyUtils() {
}
/** /**
* Calculate the BIND9 key file base name (i.e., without the ".key" or * Calculate the BIND9 key file base name (i.e., without the ".key" or
@ -60,18 +61,18 @@ public class BINDKeyUtils {
/** Reads in the DNSKEYRecord from the public key file */ /** Reads in the DNSKEYRecord from the public key file */
private static DNSKEYRecord loadPublicKeyFile(File publicKeyFile) private static DNSKEYRecord loadPublicKeyFile(File publicKeyFile)
throws IOException { throws IOException {
Master m = new Master(publicKeyFile.getAbsolutePath(), null, 600); try (Master m = new Master(publicKeyFile.getAbsolutePath(), null, 600)) {
Record r;
DNSKEYRecord result = null;
Record r; while ((r = m.nextRecord()) != null) {
DNSKEYRecord result = null; if (r.getType() == Type.DNSKEY) {
result = (DNSKEYRecord) r;
while ((r = m.nextRecord()) != null) { }
if (r.getType() == Type.DNSKEY) {
result = (DNSKEYRecord) r;
} }
}
return result; return result;
}
} }
/** Reads in the private key verbatim from the private key file */ /** Reads in the private key verbatim from the private key file */
@ -106,17 +107,12 @@ public class BINDKeyUtils {
* Given the information necessary to construct the path to a BIND9 generated * Given the information necessary to construct the path to a BIND9 generated
* key pair, load the key pair. * key pair, load the key pair.
* *
* @param signer * @param signer the DNS name of the key.
* the DNS name of the key. * @param algorithm the DNSSEC algorithm of the key.
* @param algorithm * @param keyid the DNSSEC key footprint.
* the DNSSEC algorithm of the key. * @param inDirectory the directory to look for the files (may be null).
* @param keyid
* the DNSSEC key footprint.
* @param inDirectory
* the directory to look for the files (may be null).
* @return the loaded key pair. * @return the loaded key pair.
* @throws IOException * @throws IOException if there was a problem reading the BIND9 files.
* if there was a problem reading the BIND9 files.
*/ */
public static DnsKeyPair loadKeyPair(Name signer, int algorithm, int keyid, public static DnsKeyPair loadKeyPair(Name signer, int algorithm, int keyid,
File inDirectory) throws IOException { File inDirectory) throws IOException {
@ -128,22 +124,17 @@ public class BINDKeyUtils {
/** /**
* Given a base path to a BIND9 key pair, load the key pair. * Given a base path to a BIND9 key pair, load the key pair.
* *
* @param keyFileBasePath * @param keyFileBasePath the base filename (or real filename for either the
* the base filename (or real filename for either the * public or private key) of the key.
* public or * @param inDirectory the directory to look in, if the keyFileBasePath is
* private key) of the key.
* @param inDirectory
* the directory to look in, if the keyFileBasePath is
* relative. * relative.
* @return the loaded key pair. * @return the loaded key pair.
* @throws IOException * @throws IOException if there was a problem reading the files
* if there was a problem reading the files
*/ */
public static DnsKeyPair loadKeyPair(String keyFileBasePath, File inDirectory) public static DnsKeyPair loadKeyPair(String keyFileBasePath, File inDirectory)
throws IOException { throws IOException {
keyFileBasePath = fixKeyFileBasePath(keyFileBasePath); keyFileBasePath = fixKeyFileBasePath(keyFileBasePath);
// FIXME: should we throw the IOException when one of the files
// cannot be found, or just when both cannot be found?
File publicKeyFile = new File(inDirectory, keyFileBasePath + ".key"); File publicKeyFile = new File(inDirectory, keyFileBasePath + ".key");
File privateKeyFile = new File(inDirectory, keyFileBasePath + ".private"); File privateKeyFile = new File(inDirectory, keyFileBasePath + ".private");
@ -162,15 +153,12 @@ public class BINDKeyUtils {
* Given a base path to a BIND9 key pair, load the public part (only) of the * Given a base path to a BIND9 key pair, load the public part (only) of the
* key pair * key pair
* *
* @param keyFileBasePath * @param keyFileBasePath the base or real path to the public part of a key
* the base or real path to the public part of a key
* pair. * pair.
* @param inDirectory * @param inDirectory the directory to look in if the path is relative
* the directory to look in if the path is relative (may * (may be null).
* be null).
* @return a {@link DnsKeyPair} containing just the public key information. * @return a {@link DnsKeyPair} containing just the public key information.
* @throws IOException * @throws IOException if there was a problem reading the public key file.
* if there was a problem reading the public key file.
*/ */
public static DnsKeyPair loadKey(String keyFileBasePath, File inDirectory) public static DnsKeyPair loadKey(String keyFileBasePath, File inDirectory)
throws IOException { throws IOException {
@ -190,15 +178,11 @@ public class BINDKeyUtils {
* "keyset-[signer]." where [signer] is the DNS owner name of the key. The * "keyset-[signer]." where [signer] is the DNS owner name of the key. The
* keyset may be signed, but doesn't have to be. * keyset may be signed, but doesn't have to be.
* *
* @param keysetFileName * @param keysetFileName the name of the keyset file.
* the name of the keyset file. * @param inDirectory the directory to look in if the path is relative (may
* @param inDirectory * be null, defaults to the current working directory).
* the directory to look in if the path is relative (may
* be null,
* defaults to the current working directory).
* @return a RRset contain the KEY records and any associated SIG records. * @return a RRset contain the KEY records and any associated SIG records.
* @throws IOException * @throws IOException if there was a problem reading the keyset file.
* if there was a problem reading the keyset file.
*/ */
public static RRset loadKeySet(String keysetFileName, File inDirectory) public static RRset loadKeySet(String keysetFileName, File inDirectory)
throws IOException { throws IOException {
@ -218,8 +202,8 @@ public class BINDKeyUtils {
/** /**
* Calculate the key file base for this key pair. * Calculate the key file base for this key pair.
* *
* @param pair * @param pair the {@link DnsKeyPair} to work from. It only needs a public
* the {@link DnsKeyPair} to work from. It only needs a public key. * key.
* @return the base name of the key files. * @return the base name of the key files.
*/ */
public static String keyFileBase(DnsKeyPair pair) { public static String keyFileBase(DnsKeyPair pair) {
@ -259,20 +243,17 @@ public class BINDKeyUtils {
* Given a the contents of a BIND9 private key file, convert it into a native * Given a the contents of a BIND9 private key file, convert it into a native
* {@link java.security.PrivateKey} object. * {@link java.security.PrivateKey} object.
* *
* @param privateKeyString * @param privateKeyString the contents of a BIND9 key file in string form.
* the contents of a BIND9 key file in string form.
* @return a {@link java.security.PrivateKey} * @return a {@link java.security.PrivateKey}
*/ */
public static PrivateKey convertPrivateKeyString(String privateKeyString) { public static PrivateKey convertPrivateKeyString(String privateKeyString) {
if (privateKeyString == null) if (privateKeyString == null)
return null; return null;
// FIXME: should this swallow exceptions or actually propagate
// them?
try { try {
DnsKeyConverter conv = new DnsKeyConverter(); DnsKeyConverter conv = new DnsKeyConverter();
return conv.parsePrivateKeyString(privateKeyString); return conv.parsePrivateKeyString(privateKeyString);
} catch (IOException|NoSuchAlgorithmException e) { } catch (IOException | NoSuchAlgorithmException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -283,10 +264,8 @@ public class BINDKeyUtils {
* Given a native private key, convert it into a BIND9 private key file * Given a native private key, convert it into a BIND9 private key file
* format. * format.
* *
* @param priv * @param priv the private key to convert.
* the private key to convert. * @param pub the private key's corresponding public key. Some algorithms
* @param pub
* the private key's corresponding public key. Some algorithms
* require information from both. * require information from both.
* @return a string containing the contents of a BIND9 private key file. * @return a string containing the contents of a BIND9 private key file.
*/ */
@ -327,16 +306,11 @@ public class BINDKeyUtils {
/** /**
* This routine will write out the BIND9 dnssec-* tool compatible files. * This routine will write out the BIND9 dnssec-* tool compatible files.
* *
* @param baseFileName * @param baseFileName use this base file name. If null, the standard BIND9
* use this base file name. If null, the standard BIND9 base * base file name will be computed.
* file * @param pair the keypair in question.
* name will be computed. * @param inDirectory the directory to write to (may be null).
* @param pair * @throws IOException if there is a problem writing the files.
* the keypair in question.
* @param inDirectory
* the directory to write to (may be null).
* @throws IOException
* if there is a problem writing the files.
*/ */
public static void writeKeyFiles(String baseFileName, DnsKeyPair pair, public static void writeKeyFiles(String baseFileName, DnsKeyPair pair,
File inDirectory) throws IOException { File inDirectory) throws IOException {
@ -369,10 +343,8 @@ public class BINDKeyUtils {
* This routine will write out the BIND9 dnssec-* tool compatible files to the * This routine will write out the BIND9 dnssec-* tool compatible files to the
* standard file names. * standard file names.
* *
* @param pair * @param pair the key pair in question.
* the key pair in question. * @param inDirectory the directory to write to (may be null).
* @param inDirectory
* the directory to write to (may be null).
*/ */
public static void writeKeyFiles(DnsKeyPair pair, File inDirectory) public static void writeKeyFiles(DnsKeyPair pair, File inDirectory)
throws IOException { throws IOException {

View File

@ -27,7 +27,7 @@ import java.util.logging.Logger;
* @author David Blacka * @author David Blacka
*/ */
public class ByteArrayComparator implements Comparator<byte[]> { public class ByteArrayComparator implements Comparator<byte[]> {
private int mOffset = 0; private int mOffset = 0;
private boolean mDebug = false; private boolean mDebug = false;
private Logger log; private Logger log;

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2006, 2022 Verisign. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. The name of the author may not
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package com.verisignlabs.dnssec.security;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.logging.Logger;
import org.xbill.DNS.CDSRecord;
import org.xbill.DNS.DLVRecord;
import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSOutput;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.DSRecord;
/**
* This class handles the implementation behind converting DNSKEYs into
* DSRecords. It primarily exists to bootstrap whatever crypto libraries we
* might need to do so.
*
* @author David Blacka
*/
public class DSAlgorithm {
private Logger log = Logger.getLogger(this.getClass().toString());
HashSet<Integer> mSupportedAlgorithms = null;
private static DSAlgorithm mInstance = null;
public DSAlgorithm() {
mSupportedAlgorithms = new HashSet<>();
mSupportedAlgorithms.add(DNSSEC.Digest.SHA1);
mSupportedAlgorithms.add(DNSSEC.Digest.SHA256);
mSupportedAlgorithms.add(DNSSEC.Digest.SHA384);
// 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<?> bcProviderClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
Provider bcProvider = (Provider) bcProviderClass.getDeclaredConstructor().newInstance();
Security.addProvider(bcProvider);
log.fine("bouncycastle crypto provider loaded");
mSupportedAlgorithms.add(DNSSEC.Digest.GOST3411);
} catch (ReflectiveOperationException e) {
// do nothing, this is the normal case
}
}
public String[] supportedAlgorithmMnemonics() {
ArrayList<String> algs = new ArrayList<>();
for (int digestId : mSupportedAlgorithms) {
algs.add(DNSSEC.Digest.string(digestId));
}
String[] result = new String[algs.size()];
return algs.toArray(result);
}
/**
* Given a DNSKEY record, generate the DS record from it.
*
* @param keyrec the KEY record in question.
* @param digestAlg The digest algorithm (SHA-1, SHA-256, etc.).
* @param ttl the desired TTL for the generated DS record. If zero, or
* negative, the original KEY RR's TTL will be used.
* @return the corresponding {@link org.xbill.DNS.DSRecord}
*/
public DSRecord calculateDSRecord(DNSKEYRecord keyrec, int digestAlg, long ttl) {
if (keyrec == null)
return null;
if (ttl <= 0)
ttl = keyrec.getTTL();
DNSOutput os = new DNSOutput();
os.writeByteArray(keyrec.getName().toWireCanonical());
os.writeByteArray(keyrec.rdataToWireCanonical());
try {
byte[] digest;
MessageDigest md;
switch (digestAlg) {
case DNSSEC.Digest.SHA1:
md = MessageDigest.getInstance("SHA");
digest = md.digest(os.toByteArray());
break;
case DNSSEC.Digest.SHA256:
md = MessageDigest.getInstance("SHA-256");
digest = md.digest(os.toByteArray());
break;
case DNSSEC.Digest.GOST3411:
// The standard Java crypto providers don't have this, but bouncycastle does
if (java.security.Security.getProviders("MessageDigest.GOST3411") != null) {
md = MessageDigest.getInstance("GOST3411");
digest = md.digest(os.toByteArray());
} else {
throw new IllegalArgumentException("Unsupported digest id: " + digestAlg);
}
break;
case DNSSEC.Digest.SHA384:
md = MessageDigest.getInstance("SHA-384");
digest = md.digest(os.toByteArray());
break;
default:
throw new IllegalArgumentException("Unknown digest id: " + digestAlg);
}
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl,
keyrec.getFootprint(), keyrec.getAlgorithm(), digestAlg,
digest);
} catch (NoSuchAlgorithmException e) {
log.severe(e.toString());
return null;
}
}
public DLVRecord dsToDLV(DSRecord ds) {
return new DLVRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
ds.getDigestID(), ds.getDigest());
}
public CDSRecord dstoCDS(DSRecord ds) {
return new CDSRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
ds.getDClass(), ds.getDigest());
}
public static DSAlgorithm getInstance() {
if (mInstance == null) {
mInstance = new DSAlgorithm();
}
return mInstance;
}
}

View File

@ -27,22 +27,16 @@
package com.verisignlabs.dnssec.security; package com.verisignlabs.dnssec.security;
import java.math.BigInteger;
import java.security.AlgorithmParameters; import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature; import java.security.Signature;
import java.security.spec.ECFieldFp;
import java.security.spec.ECGenParameterSpec; import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.security.spec.InvalidParameterSpecException; import java.security.spec.InvalidParameterSpecException;
import java.security.spec.NamedParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -51,12 +45,6 @@ import java.util.logging.Logger;
import org.xbill.DNS.DNSSEC; import org.xbill.DNS.DNSSEC;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
// for now, we need to import the EdDSA parameter spec classes
// because they have no generic form in java.security.spec.*
// sadly, this will currently fail if you don't have the lib.
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
/** /**
* This class handles translating DNS signing algorithm identifiers into various * This class handles translating DNS signing algorithm identifiers into various
* usable java implementations. * usable java implementations.
@ -72,21 +60,23 @@ public class DnsKeyAlgorithm {
// Our base algorithm numbers. This is a normalization of the DNSSEC // Our base algorithm numbers. This is a normalization of the DNSSEC
// algorithms (which are really signature algorithms). Thus RSASHA1, // algorithms (which are really signature algorithms). Thus RSASHA1,
// RSASHA256, etc. all boil down to 'RSA' here. // RSASHA256, etc. all boil down to 'RSA' here. Similarly, ECDSAP256SHA256 and
public static final int UNKNOWN = -1; // ECDSAP384SHA384 both become 'ECDSA'.
public static final int RSA = 1; public enum BaseAlgorithm {
public static final int DH = 2; UNKNOWN,
public static final int DSA = 3; RSA,
public static final int ECC_GOST = 4; DH,
public static final int ECDSA = 5; DSA,
public static final int EDDSA = 6; ECDSA,
EDDSA;
}
private static class AlgEntry { private static class AlgEntry {
public int dnssecAlgorithm; public int dnssecAlgorithm;
public String sigName; public String sigName;
public int baseType; public BaseAlgorithm baseType;
public AlgEntry(int algorithm, String sigName, int baseType) { public AlgEntry(int algorithm, String sigName, BaseAlgorithm baseType) {
this.dnssecAlgorithm = algorithm; this.dnssecAlgorithm = algorithm;
this.sigName = sigName; this.sigName = sigName;
this.baseType = baseType; this.baseType = baseType;
@ -96,18 +86,20 @@ public class DnsKeyAlgorithm {
private static class ECAlgEntry extends AlgEntry { private static class ECAlgEntry extends AlgEntry {
public ECParameterSpec ecSpec; public ECParameterSpec ecSpec;
public ECAlgEntry(int algorithm, String sigName, int baseType, ECParameterSpec spec) { public ECAlgEntry(int algorithm, String sigName, BaseAlgorithm baseType, ECParameterSpec spec) {
super(algorithm, sigName, baseType); super(algorithm, sigName, baseType);
this.ecSpec = spec; this.ecSpec = spec;
} }
} }
private static class EdAlgEntry extends AlgEntry { private static class EdAlgEntry extends AlgEntry {
public EdDSAParameterSpec edSpec; public String curveName;
public NamedParameterSpec paramSpec;
public EdAlgEntry(int algorithm, String sigName, int baseType, EdDSAParameterSpec spec) { public EdAlgEntry(int algorithm, String sigName, BaseAlgorithm baseType, String curveName) {
super(algorithm, sigName, baseType); super(algorithm, sigName, baseType);
this.edSpec = spec; this.curveName = curveName;
this.paramSpec = new NamedParameterSpec(curveName);
} }
} }
@ -130,12 +122,8 @@ public class DnsKeyAlgorithm {
private KeyPairGenerator mRSAKeyGenerator; private KeyPairGenerator mRSAKeyGenerator;
/** This is a cached key pair generator for DSA keys. */ /** This is a cached key pair generator for DSA keys. */
private KeyPairGenerator mDSAKeyGenerator; 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. */ /** This is a cached key pair generator for ECDSA_P256 keys. */
private KeyPairGenerator mECKeyGenerator; private KeyPairGenerator mECKeyGenerator;
/** This is a cached key pair generator for EdDSA keys. */
private KeyPairGenerator mEdKeyGenerator;
private Logger log = Logger.getLogger(this.getClass().toString()); private Logger log = Logger.getLogger(this.getClass().toString());
@ -143,27 +131,6 @@ public class DnsKeyAlgorithm {
private static DnsKeyAlgorithm mInstance = null; private static DnsKeyAlgorithm mInstance = null;
public DnsKeyAlgorithm() { public DnsKeyAlgorithm() {
// 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();
Provider bc_provider = (Provider) bc_provider_class.getDeclaredConstructor().newInstance();
Security.addProvider(bc_provider);
} catch (ReflectiveOperationException e) {
}
// Attempt to add the EdDSA-Java provider.
try {
Class<?> eddsa_provider_class = Class.forName("net.i2p.crypto.eddsa.EdDSASecurityProvider");
// Provider eddsa_provider = (Provider) eddsa_provider_class.newInstance();
Provider eddsa_provider = (Provider) eddsa_provider_class.getDeclaredConstructor().newInstance();
Security.addProvider(eddsa_provider);
} catch (ReflectiveOperationException e) {
log.warning("Unable to load EdDSA provider");
}
initialize(); initialize();
} }
@ -173,16 +140,16 @@ public class DnsKeyAlgorithm {
mIdToMnemonicMap = new HashMap<>(); mIdToMnemonicMap = new HashMap<>();
// Load the standard DNSSEC algorithms. // Load the standard DNSSEC algorithms.
addAlgorithm(DNSSEC.Algorithm.RSAMD5, "MD5withRSA", RSA); addAlgorithm(DNSSEC.Algorithm.RSAMD5, "MD5withRSA", BaseAlgorithm.RSA);
addMnemonic("RSAMD5", DNSSEC.Algorithm.RSAMD5); addMnemonic("RSAMD5", DNSSEC.Algorithm.RSAMD5);
addAlgorithm(DNSSEC.Algorithm.DH, "", DH); addAlgorithm(DNSSEC.Algorithm.DH, "", BaseAlgorithm.DH);
addMnemonic("DH", DNSSEC.Algorithm.DH); addMnemonic("DH", DNSSEC.Algorithm.DH);
addAlgorithm(DNSSEC.Algorithm.DSA, "SHA1withDSA", DSA); addAlgorithm(DNSSEC.Algorithm.DSA, "SHA1withDSA", BaseAlgorithm.DSA);
addMnemonic("DSA", DNSSEC.Algorithm.DSA); addMnemonic("DSA", DNSSEC.Algorithm.DSA);
addAlgorithm(DNSSEC.Algorithm.RSASHA1, "SHA1withRSA", RSA); addAlgorithm(DNSSEC.Algorithm.RSASHA1, "SHA1withRSA", BaseAlgorithm.RSA);
addMnemonic("RSASHA1", DNSSEC.Algorithm.RSASHA1); addMnemonic("RSASHA1", DNSSEC.Algorithm.RSASHA1);
addMnemonic("RSA", DNSSEC.Algorithm.RSASHA1); addMnemonic("RSA", DNSSEC.Algorithm.RSASHA1);
@ -194,79 +161,109 @@ public class DnsKeyAlgorithm {
addMnemonic("NSEC3RSASHA1", DNSSEC.Algorithm.RSA_NSEC3_SHA1); addMnemonic("NSEC3RSASHA1", DNSSEC.Algorithm.RSA_NSEC3_SHA1);
// Algorithms added by RFC 5702. // Algorithms added by RFC 5702.
addAlgorithm(DNSSEC.Algorithm.RSASHA256, "SHA256withRSA", RSA); addAlgorithm(DNSSEC.Algorithm.RSASHA256, "SHA256withRSA", BaseAlgorithm.RSA);
addMnemonic("RSASHA256", DNSSEC.Algorithm.RSASHA256); addMnemonic("RSASHA256", DNSSEC.Algorithm.RSASHA256);
addAlgorithm(DNSSEC.Algorithm.RSASHA512, "SHA512withRSA", RSA); addAlgorithm(DNSSEC.Algorithm.RSASHA512, "SHA512withRSA", BaseAlgorithm.RSA);
addMnemonic("RSASHA512", DNSSEC.Algorithm.RSASHA512); addMnemonic("RSASHA512", DNSSEC.Algorithm.RSASHA512);
// ECC-GOST is not supported by Java 1.8's Sun crypto provider. The addAlgorithm(DNSSEC.Algorithm.ECDSAP256SHA256, "SHA256withECDSA", BaseAlgorithm.ECDSA, "secp256r1");
// bouncycastle.org provider, however, does support it.
// 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("ECDSAP256SHA256", DNSSEC.Algorithm.ECDSAP256SHA256);
addMnemonic("ECDSA-P256", DNSSEC.Algorithm.ECDSAP256SHA256); addMnemonic("ECDSA-P256", DNSSEC.Algorithm.ECDSAP256SHA256);
addAlgorithm(DNSSEC.Algorithm.ECDSAP384SHA384, "SHA384withECDSA", ECDSA, "secp384r1"); addAlgorithm(DNSSEC.Algorithm.ECDSAP384SHA384, "SHA384withECDSA", BaseAlgorithm.ECDSA, "secp384r1");
addMnemonic("ECDSAP384SHA384", DNSSEC.Algorithm.ECDSAP384SHA384); addMnemonic("ECDSAP384SHA384", DNSSEC.Algorithm.ECDSAP384SHA384);
addMnemonic("ECDSA-P384", DNSSEC.Algorithm.ECDSAP384SHA384); addMnemonic("ECDSA-P384", DNSSEC.Algorithm.ECDSAP384SHA384);
// EdDSA is not supported by either the Java 1.8 Sun crypto // For the Edwards Curve implementations, we just initialize Signature and
// provider or bouncycastle. It is added by the Ed25519-Java // KeyPairGenerator with the curve name.
// library. We don't have a corresponding constant in addAlgorithm(15, "Ed25519", BaseAlgorithm.EDDSA, "Ed25519");
// org.xbill.DNS.DNSSEC yet, though.
addAlgorithm(15, "NONEwithEdDSA", EDDSA, "Ed25519");
addMnemonic("ED25519", 15); addMnemonic("ED25519", 15);
addAlgorithm(16, "Ed448", BaseAlgorithm.EDDSA, "Ed448");
addMnemonic(("ED448"), 16);
} }
private void addAlgorithm(int algorithm, String sigName, int baseType) { private void addAlgorithm(int algorithm, String sigName, BaseAlgorithm baseType) {
mAlgorithmMap.put(algorithm, new AlgEntry(algorithm, sigName, baseType)); mAlgorithmMap.put(algorithm, new AlgEntry(algorithm, sigName, baseType));
} }
private void addAlgorithm(int algorithm, String sigName, int baseType, String curveName) { /**
if (baseType == ECDSA) { * Add a ECDSA (algorithms 13/14) to the set, looking up the curve names.
ECParameterSpec ecSpec = ECSpecFromAlgorithm(algorithm); *
if (ecSpec == null) * @param algorithm the DNSSEC algorithm number.
ecSpec = ECSpecFromName(curveName); * @param sigName the name of the signature scheme.
if (ecSpec == null) * @param curveName the official name of the elliptic curve in our crypto
return; * library (SunEC).
*/
private void addECDSAAlgorithm(int algorithm, String sigName, String curveName) {
ECParameterSpec ecSpec = ECSpecFromName(curveName);
if (ecSpec == null)
return;
// Check to see if we can get a Signature object for this algorithm. // Check to see if we can get a Signature object for this algorithm.
try { try {
Signature.getInstance(sigName); Signature.getInstance(sigName);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
// for now, let's find out // for now, let's find out
log.severe("could not get signature for " + sigName + ": " + e.getMessage()); log.severe("could not get signature for " + sigName + ": " + e.getMessage());
// If not, do not add the algorithm. // If not, do not add the algorithm.
return; return;
}
ECAlgEntry entry = new ECAlgEntry(algorithm, sigName, baseType, ecSpec);
mAlgorithmMap.put(algorithm, entry);
} else if (baseType == EDDSA) {
EdDSAParameterSpec edSpec = EdDSASpecFromName(curveName);
if (edSpec == null)
return;
// Check to see if we can get a Signature object for this algorithm.
try {
Signature.getInstance(sigName);
} catch (NoSuchAlgorithmException e) {
// for now, let's find out
log.severe("could not get signature for " + sigName + ": " + e.getMessage());
// If not, do not add the algorithm.
return;
}
EdAlgEntry entry = new EdAlgEntry(algorithm, sigName, baseType, edSpec);
mAlgorithmMap.put(algorithm, entry);
} }
ECAlgEntry entry = new ECAlgEntry(algorithm, sigName, BaseAlgorithm.ECDSA, ecSpec);
mAlgorithmMap.put(algorithm, entry);
} }
/**
* Add an EdDSA (Edwards curve algorithms, DNSSEC algorithms 15/16), looking up
* the curve.
*
* @param algorithm the DNSSEC algorithm numer.
* @param sigName the name of the signing scheme. For EdDSA, this is the same
* as the curve.
* @param curveName the name of the curve.
*/
private void addEdDSAAlgorithm(int algorithm, String sigName, String curveName) {
// Check to see if we can get a Signature object for this algorithm.
try {
Signature.getInstance(sigName);
} catch (NoSuchAlgorithmException e) {
// for now, let's find out
log.severe("could not get signature for EdDSA curve" + curveName + ": " + e.getMessage());
// If not, do not add the algorithm.
return;
}
EdAlgEntry entry = new EdAlgEntry(algorithm, sigName, BaseAlgorithm.EDDSA, curveName);
mAlgorithmMap.put(algorithm, entry);
}
/**
* Add an Elliptic Curve algorithm given a signing scheme and curve name.
*
* @param algorithm the DNSSEC algorithm number
* @param sigName the signature scheme (e.g., which crypto hash function are
* we using?)
* @param baseType the base type (either ECDSA or EDDSA).
* @param curveName the name of the curve.
*/
private void addAlgorithm(int algorithm, String sigName, BaseAlgorithm baseType, String curveName) {
switch (baseType) {
case ECDSA:
addECDSAAlgorithm(algorithm, sigName, curveName);
break;
case EDDSA:
addEdDSAAlgorithm(algorithm, sigName, curveName);
break;
default:
throw new IllegalArgumentException("Non-Ellipic curve algorithm passed.");
}
}
/**
* Add an alternate mnemonic for an algorithm.
*
* @param m the new mnemonic.
* @param alg the DNSSEC algorithm number.
*/
private void addMnemonic(String m, int alg) { private void addMnemonic(String m, int alg) {
// Do not add mnemonics for algorithms that ended up not actually being // Do not add mnemonics for algorithms that ended up not actually being
// supported. // supported.
@ -277,19 +274,19 @@ public class DnsKeyAlgorithm {
mIdToMnemonicMap.computeIfAbsent(alg, k -> m); mIdToMnemonicMap.computeIfAbsent(alg, k -> m);
} }
public void addAlias(int alias, String mnemonic, int original_algorithm) { public void addAlias(int alias, String mnemonic, int origAlgorithm) {
if (mAlgorithmMap.containsKey(alias)) { if (mAlgorithmMap.containsKey(alias)) {
log.warning("Unable to alias algorithm " + alias + " because it already exists."); log.warning("Unable to alias algorithm " + alias + " because it already exists.");
return; return;
} }
if (!mAlgorithmMap.containsKey(original_algorithm)) { if (!mAlgorithmMap.containsKey(origAlgorithm)) {
log.warning("Unable to alias algorithm " + alias log.warning("Unable to alias algorithm " + alias
+ " to unknown algorithm identifier " + original_algorithm); + " to unknown algorithm identifier " + origAlgorithm);
return; return;
} }
mAlgorithmMap.put(alias, mAlgorithmMap.get(original_algorithm)); mAlgorithmMap.put(alias, mAlgorithmMap.get(origAlgorithm));
if (mnemonic != null) { if (mnemonic != null) {
addMnemonic(mnemonic, alias); addMnemonic(mnemonic, alias);
@ -300,34 +297,12 @@ public class DnsKeyAlgorithm {
return mAlgorithmMap.get(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 ECDSA curve. // Fetch the curve parameters from a named ECDSA curve.
private ECParameterSpec ECSpecFromName(String stdName) { private ECParameterSpec ECSpecFromName(String stdName) {
try { try {
AlgorithmParameters ap = AlgorithmParameters.getInstance("EC"); AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");
ECGenParameterSpec ecg_spec = new ECGenParameterSpec(stdName); ECGenParameterSpec ecgSpec = new ECGenParameterSpec(stdName);
ap.init(ecg_spec); ap.init(ecgSpec);
return ap.getParameterSpec(ECParameterSpec.class); return ap.getParameterSpec(ECParameterSpec.class);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
log.info("Elliptic Curve not supported by any crypto provider: " + e.getMessage()); log.info("Elliptic Curve not supported by any crypto provider: " + e.getMessage());
@ -337,24 +312,6 @@ public class DnsKeyAlgorithm {
return null; return null;
} }
// Fetch the curve parameters from a named EdDSA curve.
private EdDSAParameterSpec EdDSASpecFromName(String stdName) {
try {
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(stdName);
if (spec != null)
return spec;
throw new InvalidParameterSpecException("Edwards Curve " + stdName + " not found.");
}
// catch (NoSuchAlgorithmException e) {
// log.info("Edwards Curve not supported by any crypto provider: " +
// e.getMessage());
// }
catch (InvalidParameterSpecException e) {
log.info("Edwards Curve " + stdName + " not supported");
}
return null;
}
public String[] supportedAlgMnemonics() { public String[] supportedAlgMnemonics() {
Set<Integer> keyset = mAlgorithmMap.keySet(); Set<Integer> keyset = mAlgorithmMap.keySet();
Integer[] algs = keyset.toArray(new Integer[keyset.size()]); Integer[] algs = keyset.toArray(new Integer[keyset.size()]);
@ -392,11 +349,10 @@ public class DnsKeyAlgorithm {
} }
/** /**
* Given one of the ECDSA algorithms (ECDSAP256SHA256, etc.) return * Given one of the ECDSA algorithms (ECDSAP256SHA256, etc.) return the
* the elliptic curve parameters. * elliptic curve parameters.
* *
* @param algorithm * @param algorithm The DNSSEC algorithm number.
* The DNSSEC algorithm number.
* @return The calculated JCA ECParameterSpec for that DNSSEC algorithm, or * @return The calculated JCA ECParameterSpec for that DNSSEC algorithm, or
* null if not a recognized/supported EC algorithm. * null if not a recognized/supported EC algorithm.
*/ */
@ -406,37 +362,35 @@ public class DnsKeyAlgorithm {
return null; return null;
if (!(entry instanceof ECAlgEntry)) if (!(entry instanceof ECAlgEntry))
return null; return null;
ECAlgEntry ec_entry = (ECAlgEntry) entry; ECAlgEntry ecEntry = (ECAlgEntry) entry;
return ec_entry.ecSpec; return ecEntry.ecSpec;
} }
/** /**
* Given one of the EdDSA algorithms (Ed25519, Ed448) return the * Given one of the EdDSA algorithms (ED25519 or ED448), return the named
* elliptic curve parameters. * parameter spec.
* *
* @param algorithm * @param algorithm The DNSSEC algorithm number.
* The DNSSEC algorithm number. * @return The NamedParameterSpec for that DNSSEC algorithm, nor null if the
* @return The stored EdDSAParameterSpec for that algorithm, or * algorithm wasn't a supported EdDSA algorithm.
* null if not a recognized/supported EdDSA algorithm.
*/ */
public EdDSAParameterSpec getEdwardsCurveParams(int algorithm) { public NamedParameterSpec getEdwardsCurveSpec(int algorithm) {
AlgEntry entry = getEntry(algorithm); AlgEntry entry = getEntry(algorithm);
if (entry == null) if (entry == null)
return null; return null;
if (!(entry instanceof EdAlgEntry)) if (!(entry instanceof EdAlgEntry))
return null; return null;
EdAlgEntry ed_entry = (EdAlgEntry) entry; EdAlgEntry edEntry = (EdAlgEntry) entry;
return ed_entry.edSpec; return edEntry.paramSpec;
} }
/** /**
* Translate a possible algorithm alias back to the original DNSSEC algorithm * Translate a possible algorithm alias back to the original DNSSEC algorithm
* number * number
* *
* @param algorithm * @param algorithm a DNSSEC algorithm that may be an alias.
* a DNSSEC algorithm that may be an alias.
* @return -1 if the algorithm isn't recognised, the orignal algorithm number * @return -1 if the algorithm isn't recognised, the orignal algorithm number
* if it is. * if it is.
*/ */
@ -462,8 +416,7 @@ public class DnsKeyAlgorithm {
* Given an algorithm mnemonic, convert the mnemonic to a DNSSEC algorithm * Given an algorithm mnemonic, convert the mnemonic to a DNSSEC algorithm
* number. * number.
* *
* @param s * @param s The mnemonic string. This is case-insensitive.
* The mnemonic string. This is case-insensitive.
* @return -1 if the mnemonic isn't recognized or supported, the algorithm * @return -1 if the mnemonic isn't recognized or supported, the algorithm
* number if it is. * number if it is.
*/ */
@ -477,8 +430,7 @@ public class DnsKeyAlgorithm {
/** /**
* Given a DNSSEC algorithm number, return the "preferred" mnemonic. * Given a DNSSEC algorithm number, return the "preferred" mnemonic.
* *
* @param algorithm * @param algorithm A DNSSEC algorithm number.
* A DNSSEC algorithm number.
* @return The preferred mnemonic string, or null if not supported or * @return The preferred mnemonic string, or null if not supported or
* recognized. * recognized.
*/ */
@ -486,15 +438,15 @@ public class DnsKeyAlgorithm {
return mIdToMnemonicMap.get(algorithm); return mIdToMnemonicMap.get(algorithm);
} }
public int baseType(int algorithm) { public BaseAlgorithm baseType(int algorithm) {
AlgEntry entry = getEntry(algorithm); AlgEntry entry = getEntry(algorithm);
if (entry != null) if (entry != null)
return entry.baseType; return entry.baseType;
return UNKNOWN; return BaseAlgorithm.UNKNOWN;
} }
public boolean isDSA(int algorithm) { public boolean isDSA(int algorithm) {
return (baseType(algorithm) == DSA); return (baseType(algorithm) == BaseAlgorithm.DSA);
} }
public KeyPair generateKeyPair(int algorithm, int keysize, boolean useLargeExp) public KeyPair generateKeyPair(int algorithm, int keysize, boolean useLargeExp)
@ -506,14 +458,14 @@ public class DnsKeyAlgorithm {
mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA"); mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA");
} }
RSAKeyGenParameterSpec rsa_spec; RSAKeyGenParameterSpec rsaSpec;
if (useLargeExp) { if (useLargeExp) {
rsa_spec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F4); rsaSpec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F4);
} else { } else {
rsa_spec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F0); rsaSpec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F0);
} }
try { try {
mRSAKeyGenerator.initialize(rsa_spec); mRSAKeyGenerator.initialize(rsaSpec);
} catch (InvalidAlgorithmParameterException e) { } catch (InvalidAlgorithmParameterException e) {
// Fold the InvalidAlgorithmParameterException into our existing // Fold the InvalidAlgorithmParameterException into our existing
// thrown exception. Ugly, but requires less code change. // thrown exception. Ugly, but requires less code change.
@ -531,22 +483,6 @@ public class DnsKeyAlgorithm {
pair = mDSAKeyGenerator.generateKeyPair(); pair = mDSAKeyGenerator.generateKeyPair();
break; break;
} }
case ECC_GOST: {
if (mECGOSTKeyGenerator == null) {
mECGOSTKeyGenerator = KeyPairGenerator.getInstance("ECGOST3410");
}
ECParameterSpec ecSpec = getEllipticCurveParams(algorithm);
try {
mECGOSTKeyGenerator.initialize(ecSpec);
} 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: { case ECDSA: {
if (mECKeyGenerator == null) { if (mECKeyGenerator == null) {
mECKeyGenerator = KeyPairGenerator.getInstance("EC"); mECKeyGenerator = KeyPairGenerator.getInstance("EC");
@ -564,19 +500,10 @@ public class DnsKeyAlgorithm {
break; break;
} }
case EDDSA: { case EDDSA: {
if (mEdKeyGenerator == null) { EdAlgEntry entry = (EdAlgEntry) getEntry(algorithm);
mEdKeyGenerator = KeyPairGenerator.getInstance("EdDSA"); KeyPairGenerator edKeyGenerator = KeyPairGenerator.getInstance(entry.curveName);
}
EdDSAParameterSpec edSpec = getEdwardsCurveParams(algorithm); pair = edKeyGenerator.generateKeyPair();
try {
mEdKeyGenerator.initialize(edSpec, new SecureRandom());
} 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 = mEdKeyGenerator.generateKeyPair();
break; break;
} }
default: default:

View File

@ -30,15 +30,20 @@ import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey; import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey; import java.security.interfaces.ECPublicKey;
import java.security.interfaces.EdECPrivateKey;
import java.security.interfaces.EdECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.DSAPrivateKeySpec; import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPrivateKeySpec;
import java.security.spec.EdECPrivateKeySpec;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec; import java.security.spec.KeySpec;
import java.security.spec.NamedParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.logging.Logger;
import javax.crypto.interfaces.DHPrivateKey; import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey; import javax.crypto.interfaces.DHPublicKey;
@ -50,13 +55,6 @@ import org.xbill.DNS.DNSSEC.DNSSECException;
import org.xbill.DNS.Name; import org.xbill.DNS.Name;
import org.xbill.DNS.utils.base64; import org.xbill.DNS.utils.base64;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
// For now, just import the native EdDSA classes
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
/** /**
* This class handles conversions between JCA key formats and DNSSEC and BIND9 * This class handles conversions between JCA key formats and DNSSEC and BIND9
* key formats. * key formats.
@ -70,6 +68,8 @@ public class DnsKeyConverter {
private KeyFactory mECKeyFactory; private KeyFactory mECKeyFactory;
private KeyFactory mEdKeyFactory; private KeyFactory mEdKeyFactory;
private DnsKeyAlgorithm mAlgorithms; private DnsKeyAlgorithm mAlgorithms;
private Logger log = Logger.getLogger(this.getClass().toString());
public DnsKeyConverter() { public DnsKeyConverter() {
mAlgorithms = DnsKeyAlgorithm.getInstance(); mAlgorithms = DnsKeyAlgorithm.getInstance();
@ -87,7 +87,6 @@ public class DnsKeyConverter {
// Because we have arbitrarily aliased algorithms, we need to possibly // Because we have arbitrarily aliased algorithms, we need to possibly
// translate the aliased algorithm back to the actual algorithm. // translate the aliased algorithm back to the actual algorithm.
int originalAlgorithm = mAlgorithms.originalAlgorithm(pKeyRecord.getAlgorithm()); int originalAlgorithm = mAlgorithms.originalAlgorithm(pKeyRecord.getAlgorithm());
if (originalAlgorithm <= 0) if (originalAlgorithm <= 0)
@ -101,16 +100,6 @@ public class DnsKeyConverter {
pKeyRecord.getKey()); pKeyRecord.getKey());
} }
// do not rely on DNSJava's method for EdDSA for now.
if (mAlgorithms.baseType(originalAlgorithm) == DnsKeyAlgorithm.EDDSA) {
try {
return parseEdDSADNSKEYRecord(pKeyRecord);
} catch (InvalidKeySpecException e) {
// just to be expedient, recast this as a NoSuchAlgorithmException.
throw new NoSuchAlgorithmException(e.getMessage());
}
}
try { try {
// This uses DNSJava's DNSSEC.toPublicKey() method. // This uses DNSJava's DNSSEC.toPublicKey() method.
return pKeyRecord.getPublicKey(); return pKeyRecord.getPublicKey();
@ -119,46 +108,27 @@ public class DnsKeyConverter {
} }
} }
/**
* Since we don't (yet) have support in DNSJava for parsing the
* newer EdDSA algorithms, here is a local version.
*/
private PublicKey parseEdDSADNSKEYRecord(DNSKEYRecord pKeyRecord)
throws IllegalArgumentException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] seed = pKeyRecord.getKey();
EdDSAPublicKeySpec spec = new EdDSAPublicKeySpec(seed,
mAlgorithms.getEdwardsCurveParams(pKeyRecord.getAlgorithm()));
KeyFactory factory = KeyFactory.getInstance("EdDSA");
return factory.generatePublic(spec);
}
/** /**
* Given a JCA public key and the ancillary data, generate a DNSKEY record. * Given a JCA public key and the ancillary data, generate a DNSKEY record.
*/ */
public DNSKEYRecord generateDNSKEYRecord(Name name, int dclass, long ttl, public DNSKEYRecord generateDNSKEYRecord(Name name, int dclass, long ttl,
int flags, int alg, PublicKey key) { int flags, int alg, PublicKey key) {
try { try {
if (mAlgorithms.baseType(alg) == DnsKeyAlgorithm.EDDSA) { int origAlgorithm = mAlgorithms.originalAlgorithm(alg);
return generateEdDSADNSKEYRecord(name, dclass, ttl, flags, alg, key); DNSKEYRecord keyrec = new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, origAlgorithm,
}
return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg,
key); key);
if (origAlgorithm == alg) {
return keyrec;
}
return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg, keyrec.getKey());
} catch (DNSSECException e) { } catch (DNSSECException e) {
// FIXME: this mimics the behavior of KEYConverter.buildRecord(), which would log.severe("Unable to generated a DNSKEYRecord: " + e);
// This mimics the behavior of KEYConverter.buildRecord(), which would
// return null if the algorithm was unknown. // return null if the algorithm was unknown.
return null; return null;
} }
} }
private DNSKEYRecord generateEdDSADNSKEYRecord(Name name, int dclass, long ttl,
int flags, int alg, PublicKey key) {
EdDSAPublicKey ed_key = (EdDSAPublicKey) key;
byte[] key_data = ed_key.getAbyte();
return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg,
key_data);
}
// Private Key Specific Parsing routines // Private Key Specific Parsing routines
/** /**
@ -168,9 +138,9 @@ public class DnsKeyConverter {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
try { try {
switch (mAlgorithms.baseType(algorithm)) { switch (mAlgorithms.baseType(algorithm)) {
case DnsKeyAlgorithm.RSA: case RSA:
return mRSAKeyFactory.generatePrivate(spec); return mRSAKeyFactory.generatePrivate(spec);
case DnsKeyAlgorithm.DSA: case DSA:
return mDSAKeyFactory.generatePrivate(spec); return mDSAKeyFactory.generatePrivate(spec);
default: default:
return null; return null;
@ -225,17 +195,15 @@ public class DnsKeyConverter {
int alg = parseInt(val, -1); int alg = parseInt(val, -1);
switch (mAlgorithms.baseType(alg)) { switch (mAlgorithms.baseType(alg)) {
case DnsKeyAlgorithm.RSA: case RSA:
return parsePrivateRSA(lines); return parsePrivateRSA(lines);
case DnsKeyAlgorithm.DSA: case DSA:
return parsePrivateDSA(lines); return parsePrivateDSA(lines);
case DnsKeyAlgorithm.DH: case DH:
return parsePrivateDH(lines); return parsePrivateDH(lines);
case DnsKeyAlgorithm.ECC_GOST: case ECDSA:
return parsePrivateECDSA(lines, alg); return parsePrivateECDSA(lines, alg);
case DnsKeyAlgorithm.ECDSA: case EDDSA:
return parsePrivateECDSA(lines, alg);
case DnsKeyAlgorithm.EDDSA:
return parsePrivateEdDSA(lines, alg); return parsePrivateEdDSA(lines, alg);
default: default:
throw new IOException("unsupported private key algorithm: " + val); throw new IOException("unsupported private key algorithm: " + val);
@ -272,12 +240,12 @@ public class DnsKeyConverter {
private PrivateKey parsePrivateRSA(StringTokenizer lines) private PrivateKey parsePrivateRSA(StringTokenizer lines)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
BigInteger modulus = null; BigInteger modulus = null;
BigInteger public_exponent = null; BigInteger publicExponent = null;
BigInteger private_exponent = null; BigInteger privateExponent = null;
BigInteger prime_p = null; BigInteger primeP = null;
BigInteger prime_q = null; BigInteger primeQ = null;
BigInteger prime_p_exponent = null; BigInteger primePExponent = null;
BigInteger prime_q_exponent = null; BigInteger primeQExponent = null;
BigInteger coefficient = null; BigInteger coefficient = null;
while (lines.hasMoreTokens()) { while (lines.hasMoreTokens()) {
@ -296,33 +264,28 @@ public class DnsKeyConverter {
if (line.startsWith("Modulus: ")) { if (line.startsWith("Modulus: ")) {
modulus = new BigInteger(1, data); modulus = new BigInteger(1, data);
// printBigIntCompare(data, modulus);
} else if (line.startsWith("PublicExponent: ")) { } else if (line.startsWith("PublicExponent: ")) {
public_exponent = new BigInteger(1, data); publicExponent = new BigInteger(1, data);
// printBigIntCompare(data, public_exponent);
} else if (line.startsWith("PrivateExponent: ")) { } else if (line.startsWith("PrivateExponent: ")) {
private_exponent = new BigInteger(1, data); privateExponent = new BigInteger(1, data);
// printBigIntCompare(data, private_exponent);
} else if (line.startsWith("Prime1: ")) { } else if (line.startsWith("Prime1: ")) {
prime_p = new BigInteger(1, data); primeP = new BigInteger(1, data);
// printBigIntCompare(data, prime_p);
} else if (line.startsWith("Prime2: ")) { } else if (line.startsWith("Prime2: ")) {
prime_q = new BigInteger(1, data); primeQ = new BigInteger(1, data);
// printBigIntCompare(data, prime_q);
} else if (line.startsWith("Exponent1: ")) { } else if (line.startsWith("Exponent1: ")) {
prime_p_exponent = new BigInteger(1, data); primePExponent = new BigInteger(1, data);
} else if (line.startsWith("Exponent2: ")) { } else if (line.startsWith("Exponent2: ")) {
prime_q_exponent = new BigInteger(1, data); primeQExponent = new BigInteger(1, data);
} else if (line.startsWith("Coefficient: ")) { } else if (line.startsWith("Coefficient: ")) {
coefficient = new BigInteger(1, data); coefficient = new BigInteger(1, data);
} }
} }
try { try {
KeySpec spec = new RSAPrivateCrtKeySpec(modulus, public_exponent, KeySpec spec = new RSAPrivateCrtKeySpec(modulus, publicExponent,
private_exponent, prime_p, privateExponent, primeP,
prime_q, prime_p_exponent, primeQ, primePExponent,
prime_q_exponent, coefficient); primeQExponent, coefficient);
if (mRSAKeyFactory == null) { if (mRSAKeyFactory == null) {
mRSAKeyFactory = KeyFactory.getInstance("RSA"); mRSAKeyFactory = KeyFactory.getInstance("RSA");
} }
@ -466,13 +429,13 @@ public class DnsKeyConverter {
if (mECKeyFactory == null) { if (mECKeyFactory == null) {
mECKeyFactory = KeyFactory.getInstance("EC"); mECKeyFactory = KeyFactory.getInstance("EC");
} }
ECParameterSpec ec_spec = mAlgorithms.getEllipticCurveParams(algorithm); ECParameterSpec ecSpec = mAlgorithms.getEllipticCurveParams(algorithm);
if (ec_spec == null) { if (ecSpec == null) {
throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm + throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm +
" is not a recognized Elliptic Curve algorithm"); " is not a recognized Elliptic Curve algorithm");
} }
KeySpec spec = new ECPrivateKeySpec(s, ec_spec); KeySpec spec = new ECPrivateKeySpec(s, ecSpec);
try { try {
return mECKeyFactory.generatePrivate(spec); return mECKeyFactory.generatePrivate(spec);
@ -516,13 +479,13 @@ public class DnsKeyConverter {
if (mEdKeyFactory == null) { if (mEdKeyFactory == null) {
mEdKeyFactory = KeyFactory.getInstance("EdDSA"); mEdKeyFactory = KeyFactory.getInstance("EdDSA");
} }
EdDSAParameterSpec ed_spec = mAlgorithms.getEdwardsCurveParams(algorithm); NamedParameterSpec namedSpec = mAlgorithms.getEdwardsCurveSpec(algorithm);
if (ed_spec == null) { if (namedSpec == null) {
throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm + throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm +
" is not a recognized Edwards Curve algorithm"); " is not a recognized Edwards Curve algorithm");
} }
KeySpec spec = new EdDSAPrivateKeySpec(seed, ed_spec); EdECPrivateKeySpec spec = new EdECPrivateKeySpec(namedSpec, seed);
try { try {
return mEdKeyFactory.generatePrivate(spec); return mEdKeyFactory.generatePrivate(spec);
@ -545,8 +508,8 @@ public class DnsKeyConverter {
return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub, alg); return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub, alg);
} else if (priv instanceof ECPrivateKey && pub instanceof ECPublicKey) { } else if (priv instanceof ECPrivateKey && pub instanceof ECPublicKey) {
return generatePrivateEC((ECPrivateKey) priv, (ECPublicKey) pub, alg); return generatePrivateEC((ECPrivateKey) priv, (ECPublicKey) pub, alg);
} else if (priv instanceof EdDSAPrivateKey && pub instanceof EdDSAPublicKey) { } else if (priv instanceof EdECPrivateKey && pub instanceof EdECPublicKey) {
return generatePrivateED((EdDSAPrivateKey) priv, (EdDSAPublicKey) pub, alg); return generatePrivateED((EdECPrivateKey) priv, (EdECPublicKey) pub, alg);
} }
return null; return null;
@ -556,16 +519,16 @@ public class DnsKeyConverter {
* Convert from 'unsigned' big integer to original 'signed format' in Base64 * Convert from 'unsigned' big integer to original 'signed format' in Base64
*/ */
private static String b64BigInt(BigInteger i) { private static String b64BigInt(BigInteger i) {
byte[] orig_bytes = i.toByteArray(); byte[] origBytes = i.toByteArray();
if (orig_bytes[0] != 0 || orig_bytes.length == 1) { if (origBytes[0] != 0 || origBytes.length == 1) {
return base64.toString(orig_bytes); return base64.toString(origBytes);
} }
byte[] signed_bytes = new byte[orig_bytes.length - 1]; byte[] signedBytes = new byte[origBytes.length - 1];
System.arraycopy(orig_bytes, 1, signed_bytes, 0, signed_bytes.length); System.arraycopy(origBytes, 1, signedBytes, 0, signedBytes.length);
return base64.toString(signed_bytes); return base64.toString(signedBytes);
} }
/** /**
@ -668,7 +631,7 @@ public class DnsKeyConverter {
* Given an edwards curve key pair, and the actual algorithm (which will * Given an edwards curve key pair, and the actual algorithm (which will
* describe the curve used), return the BIND9-style text encoding. * describe the curve used), return the BIND9-style text encoding.
*/ */
private String generatePrivateED(EdDSAPrivateKey priv, EdDSAPublicKey pub, int alg) { private String generatePrivateED(EdECPrivateKey priv, EdECPublicKey pub, int alg) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw); PrintWriter out = new PrintWriter(sw);
@ -676,7 +639,8 @@ public class DnsKeyConverter {
out.println("Algorithm: " + alg + " (" + mAlgorithms.algToString(alg) out.println("Algorithm: " + alg + " (" + mAlgorithms.algToString(alg)
+ ")"); + ")");
out.print("PrivateKey: "); out.print("PrivateKey: ");
out.println(base64.toString(priv.getSeed())); byte[] keyBytes = priv.getBytes().orElse("null".getBytes());
out.println(base64.toString(keyBytes));
return sw.toString(); return sw.toString();
} }

View File

@ -16,6 +16,7 @@
package com.verisignlabs.dnssec.security; package com.verisignlabs.dnssec.security;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
@ -127,6 +128,12 @@ public class DnsKeyPair {
setPrivateKeyString(pair.getPrivateKeyString()); setPrivateKeyString(pair.getPrivateKeyString());
} }
public String toString() {
return this.getDNSKEYName() + "/" + this.getDNSKEYAlgorithm() + "/" + this.getDNSKEYFootprint() + "/"
+ this.getDNSKEYPublicPrefix(6);
}
/** @return cached DnsKeyConverter object. */ /** @return cached DnsKeyConverter object. */
protected DnsKeyConverter getKeyConverter() { protected DnsKeyConverter getKeyConverter() {
if (mKeyConverter == null) { if (mKeyConverter == null) {
@ -257,18 +264,22 @@ public class DnsKeyPair {
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
*/ */
public Signature getVerifier() { public Signature getVerifier() {
if (mVerifier == null) { if (mVerifier != null)
mVerifier = getSignature(); return mVerifier;
PublicKey pk = getPublic();
if (mVerifier != null && pk != null) { mVerifier = getSignature();
try { PublicKey pk = getPublic();
mVerifier.initVerify(pk);
} catch (InvalidKeyException e) { if (mVerifier == null || pk == null) {
} log.warning("Could not get a Signature object for this key pair" + this);
} else { return null;
// do not return an uninitialized verifier }
return null;
} try {
mVerifier.initVerify(pk);
} catch (InvalidKeyException e) {
log.warning("Key pair cannot initialize with public key: " + this);
return null;
} }
return mVerifier; return mVerifier;
@ -319,4 +330,27 @@ public class DnsKeyPair {
return kr.getFootprint(); return kr.getFootprint();
return -1; return -1;
} }
// This is from a StackOverflow answer. There are number of bytes-to-hex
// converters in the ecosystem, but this avoid extra dependencies
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static String toHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}
public String getDNSKEYPublicPrefix(int length) {
DNSKEYRecord kr = getDNSKEYRecord();
if (kr == null) {
return "";
}
String hexKey = toHex(kr.getKey());
return hexKey.substring(0, length);
}
} }

View File

@ -70,20 +70,19 @@ public class DnsSecVerifier {
add(pair); add(pair);
} }
public DnsKeyPair find(Name name, int algorithm, int keyid) { public List<DnsKeyPair> find(Name name, int algorithm, int keyid) {
String n = name.toString().toLowerCase(); String n = name.toString().toLowerCase();
List<DnsKeyPair> l = mKeyMap.get(n); List<DnsKeyPair> l = mKeyMap.get(n);
List<DnsKeyPair> result = new ArrayList<>();
if (l == null) if (l == null)
return null; return result;
// FIXME: this algorithm assumes that name+alg+footprint is
// unique, which isn't necessarily true.
for (DnsKeyPair p : l) { for (DnsKeyPair p : l) {
if (p.getDNSKEYAlgorithm() == algorithm && p.getDNSKEYFootprint() == keyid) { if (p.getDNSKEYAlgorithm() == algorithm && p.getDNSKEYFootprint() == keyid) {
return p; result.add(p);
} }
} }
return null; return result;
} }
} }
@ -138,7 +137,7 @@ public class DnsSecVerifier {
mCurrentTime = time; mCurrentTime = time;
} }
private DnsKeyPair findKey(Name name, int algorithm, int footprint) { private List<DnsKeyPair> findKey(Name name, int algorithm, int footprint) {
return mKeyStore.find(name, algorithm, footprint); return mKeyStore.find(name, algorithm, footprint);
} }
@ -219,43 +218,51 @@ public class DnsSecVerifier {
if (!result) if (!result)
return result; return result;
DnsKeyPair keypair = findKey(sigrec.getSigner(), sigrec.getAlgorithm(), List<DnsKeyPair> keypairs = findKey(sigrec.getSigner(), sigrec.getAlgorithm(),
sigrec.getFootprint()); sigrec.getFootprint());
if (keypair == null) { if (keypairs.isEmpty()) {
if (reasons != null) if (reasons != null)
reasons.add("Could not find matching trusted key"); reasons.add("Could not find matching trusted key");
log.fine("could not find matching trusted key"); log.fine("could not find matching trusted key");
return false; return false;
} }
try { try {
byte[] data = SignUtils.generateSigData(rrset, sigrec); byte[] data = SignUtils.generateSigData(rrset, sigrec);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
Signature signer = keypair.getVerifier(); // Tolerate duplicate keytags, so we can have more than one DnsKeyPair
signer.update(data); List<String> localReasons = new ArrayList<>();
boolean validated = false;
for (DnsKeyPair keypair : keypairs) {
Signature signer = keypair.getVerifier();
signer.update(data);
byte[] sig = sigrec.getSignature(); byte[] sig = sigrec.getSignature();
if (algs.baseType(sigrec.getAlgorithm()) == DnsKeyAlgorithm.DSA) { if (algs.baseType(sigrec.getAlgorithm()) == DnsKeyAlgorithm.BaseAlgorithm.DSA) {
sig = SignUtils.convertDSASignature(sig); sig = SignUtils.convertDSASignature(sig);
}
if (sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP256SHA256 ||
sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP384SHA384) {
sig = SignUtils.convertECDSASignature(sig);
}
if (signer.verify(sig)) {
validated = true;
break;
}
log.fine("Signature failed to validate cryptographically with " + keypair);
if (localReasons != null) {
localReasons.add("Signature failed to verify cryptographically with " + keypair);
}
} }
if (sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP256SHA256 || if (!validated) {
sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP384SHA384) { reasons.addAll(localReasons);
sig = SignUtils.convertECDSASignature(sig);
} }
return validated;
if (!signer.verify(sig)) {
if (reasons != null)
reasons.add("Signature failed to verify cryptographically");
log.fine("Signature failed to verify cryptographically");
return false;
}
return true;
} catch (IOException e) { } catch (IOException e) {
log.severe("I/O error: " + e); log.severe("I/O error: " + e);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {

View File

@ -68,21 +68,14 @@ public class JCEDnsSecSigner {
/** /**
* Cryptographically generate a new DNSSEC key. * Cryptographically generate a new DNSSEC key.
* *
* @param owner * @param owner the KEY RR's owner name.
* the KEY RR's owner name. * @param ttl the KEY RR's TTL.
* @param ttl * @param dclass the KEY RR's DNS class.
* the KEY RR's TTL. * @param algorithm the DNSSEC algorithm (RSASHA258, RSASHA512,
* @param dclass
* the KEY RR's DNS class.
* @param algorithm
* the DNSSEC algorithm (RSASHA258, RSASHA512,
* ECDSAP256, etc.) * ECDSAP256, etc.)
* @param flags * @param flags any flags for the KEY RR.
* any flags for the KEY RR. * @param keysize the size of the key to generate.
* @param keysize * @param useLargeExponent if generating an RSA key, use the large exponent.
* the size of the key to generate.
* @param useLargeExponent
* if generating an RSA key, use the large exponent.
* @return a DnsKeyPair with the public and private keys populated. * @return a DnsKeyPair with the public and private keys populated.
*/ */
public DnsKeyPair generateKey(Name owner, long ttl, int dclass, int algorithm, public DnsKeyPair generateKey(Name owner, long ttl, int dclass, int algorithm,
@ -113,29 +106,25 @@ public class JCEDnsSecSigner {
/** /**
* Sign an RRset. * Sign an RRset.
* *
* @param rrset * @param rrset the RRset to sign -- any existing signatures are ignored.
* the RRset to sign -- any existing signatures are ignored. * @param keypars a list of DnsKeyPair objects containing private keys.
* @param keypars * @param start the inception time for the resulting RRSIG records.
* a list of DnsKeyPair objects containing private keys. * @param expire the expiration time for the resulting RRSIG records.
* @param start
* the inception time for the resulting RRSIG records.
* @param expire
* the expiration time for the resulting RRSIG records.
* @return a list of RRSIGRecord objects. * @return a list of RRSIGRecord objects.
*/ */
public List<RRSIGRecord> signRRset(RRset rrset, List<DnsKeyPair> keypairs, Instant start, public List<RRSIGRecord> signRRset(RRset rrset, List<DnsKeyPair> keypairs, Instant start,
Instant expire) throws IOException, Instant expire) throws IOException,
GeneralSecurityException { GeneralSecurityException {
if (rrset == null || keypairs == null) if (rrset == null || keypairs == null)
return null; return new ArrayList<>();
// default start to now, expire to start + 1 second. // default start to now, expire to start + 1 second.
if (start == null) if (start == null)
start = Instant.now(); start = Instant.now();
if (expire == null) if (expire == null)
expire = start.plusSeconds(1); expire = start.plusSeconds(1);
if (keypairs.size() == 0) if (keypairs.isEmpty())
return null; return new ArrayList<>();
if (mVerboseSigning) { if (mVerboseSigning) {
log.info("Signing RRset:"); log.info("Signing RRset:");
@ -143,9 +132,9 @@ public class JCEDnsSecSigner {
} }
// first, pre-calculate the RRset bytes. // first, pre-calculate the RRset bytes.
byte[] rrset_data = SignUtils.generateCanonicalRRsetData(rrset, 0, 0); byte[] rrsetData = SignUtils.generateCanonicalRRsetData(rrset, 0, 0);
ArrayList<RRSIGRecord> sigs = new ArrayList<RRSIGRecord>(keypairs.size()); ArrayList<RRSIGRecord> sigs = new ArrayList<>(keypairs.size());
// for each keypair, sign the RRset. // for each keypair, sign the RRset.
for (DnsKeyPair pair : keypairs) { for (DnsKeyPair pair : keypairs) {
@ -155,13 +144,13 @@ public class JCEDnsSecSigner {
RRSIGRecord presig = SignUtils.generatePreRRSIG(rrset, keyrec, start, expire, RRSIGRecord presig = SignUtils.generatePreRRSIG(rrset, keyrec, start, expire,
rrset.getTTL()); rrset.getTTL());
byte[] sign_data = SignUtils.generateSigData(rrset_data, presig); byte[] signData = SignUtils.generateSigData(rrsetData, presig);
if (mVerboseSigning) { if (mVerboseSigning) {
log.info("Canonical pre-signature data to sign with key " log.info("Canonical pre-signature data to sign with key "
+ keyrec.getName().toString() + "/" + keyrec.getAlgorithm() + "/" + keyrec.getName().toString() + "/" + keyrec.getAlgorithm() + "/"
+ keyrec.getFootprint() + ":"); + keyrec.getFootprint() + ":");
log.info(hexdump.dump(null, sign_data)); log.info(hexdump.dump(null, signData));
} }
Signature signer = pair.getSigner(); Signature signer = pair.getSigner();
@ -174,7 +163,7 @@ public class JCEDnsSecSigner {
} }
// sign the data. // sign the data.
signer.update(sign_data); signer.update(signData);
byte[] sig = signer.sign(); byte[] sig = signer.sign();
if (mVerboseSigning) { if (mVerboseSigning) {
@ -184,7 +173,7 @@ public class JCEDnsSecSigner {
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance(); DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
// Convert to RFC 2536 format, if necessary. // Convert to RFC 2536 format, if necessary.
if (algs.baseType(pair.getDNSKEYAlgorithm()) == DnsKeyAlgorithm.DSA) { if (algs.baseType(pair.getDNSKEYAlgorithm()) == DnsKeyAlgorithm.BaseAlgorithm.DSA) {
DSAPublicKey pk = (DSAPublicKey) pair.getPublic(); DSAPublicKey pk = (DSAPublicKey) pair.getPublic();
sig = SignUtils.convertDSASignature(pk.getParams(), sig); sig = SignUtils.convertDSASignature(pk.getParams(), sig);
} }
@ -206,12 +195,9 @@ public class JCEDnsSecSigner {
/** /**
* Create a completely self-signed DNSKEY RRset. * Create a completely self-signed DNSKEY RRset.
* *
* @param keypairs * @param keypairs the public & private keypairs to use in the keyset.
* the public & private keypairs to use in the keyset. * @param start the RRSIG inception time.
* @param start * @param expire the RRSIG expiration time.
* the RRSIG inception time.
* @param expire
* the RRSIG expiration time.
* @return a signed RRset. * @return a signed RRset.
*/ */
public RRset makeKeySet(List<DnsKeyPair> keypairs, Instant start, Instant expire) public RRset makeKeySet(List<DnsKeyPair> keypairs, Instant start, Instant expire)
@ -236,67 +222,55 @@ public class JCEDnsSecSigner {
/** /**
* Conditionally sign an RRset and add it to the toList. * Conditionally sign an RRset and add it to the toList.
* *
* @param toList * @param toList the list to which we are adding the processed RRsets.
* the list to which we are adding the processed RRsets. * @param zonename the zone apex name.
* @param zonename * @param rrset the RRset under consideration.
* the zone apex name. * @param kskpairs the List of KSKs..
* @param rrset * @param zskpairs the List of zone keys.
* the RRset under consideration. * @param start the RRSIG inception time.
* @param kskpairs * @param expire the RRSIG expiration time.
* the List of KSKs.. * @param fullySignKeyset if true, sign the zone apex keyset with both KSKs
* @param zskpairs * and ZSKs.
* the List of zone keys. * @param lastCut the name of the last delegation point encountered.
* @param start
* the RRSIG inception time.
* @param expire
* the RRSIG expiration time.
* @param fullySignKeyset
* if true, sign the zone apex keyset with both KSKs and
* ZSKs.
* @param last_cut
* the name of the last delegation point encountered.
* *
* @return the name of the new last_cut. * @return the name of the new last_cut.
*/ */
private Name addRRset(List<Record> toList, Name zonename, RRset rrset, private Name addRRset(List<Record> toList, Name zonename, RRset rrset,
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs, Instant start, List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs, Instant start,
Instant expire, boolean fullySignKeyset, Name last_cut, Instant expire, boolean fullySignKeyset, Name lastCut,
Name last_dname) throws IOException, GeneralSecurityException { Name lastDname) throws IOException, GeneralSecurityException {
// add the records themselves // add the records themselves
rrset.rrs().forEach(record -> { rrset.rrs().forEach(toList::add);
toList.add(record);
});
int type = SignUtils.recordSecType(zonename, rrset.getName(), rrset.getType(), int type = SignUtils.recordSecType(zonename, rrset.getName(), rrset.getType(),
last_cut, last_dname); lastCut, lastDname);
// we don't sign non-normal sets (delegations, glue, invalid). // we don't sign non-normal sets (delegations, glue, invalid).
if (type == SignUtils.RR_DELEGATION) { if (type == SignUtils.RR_DELEGATION) {
return rrset.getName(); return rrset.getName();
} }
if (type == SignUtils.RR_GLUE || type == SignUtils.RR_INVALID) { if (type == SignUtils.RR_GLUE || type == SignUtils.RR_INVALID) {
return last_cut; return lastCut;
} }
// check for the zone apex keyset. // check for the zone apex keyset.
if (rrset.getName().equals(zonename) && rrset.getType() == Type.DNSKEY) { if (rrset.getName().equals(zonename) && rrset.getType() == Type.DNSKEY && kskpairs != null && !kskpairs.isEmpty()) {
// if we have ksks, sign the keyset with them, otherwise we will just sign // if we have ksks, sign the keyset with them, otherwise we will just sign
// them with the zsks. // them with the zsks.
if (kskpairs != null && kskpairs.size() > 0) { List<RRSIGRecord> sigs = signRRset(rrset, kskpairs, start, expire);
List<RRSIGRecord> sigs = signRRset(rrset, kskpairs, start, expire); toList.addAll(sigs);
toList.addAll(sigs);
// If we aren't going to sign with all the keys, bail out now.
if (!fullySignKeyset)
return lastCut;
// If we aren't going to sign with all the keys, bail out now.
if (!fullySignKeyset)
return last_cut;
}
} }
// otherwise, we are OK to sign this set. // otherwise, we are OK to sign this set.
List<RRSIGRecord> sigs = signRRset(rrset, zskpairs, start, expire); List<RRSIGRecord> sigs = signRRset(rrset, zskpairs, start, expire);
toList.addAll(sigs); toList.addAll(sigs);
return last_cut; return lastCut;
} }
// Various NSEC/NSEC3 generation modes // Various NSEC/NSEC3 generation modes
@ -311,49 +285,31 @@ public class JCEDnsSecSigner {
* Opt-Out, etc.) External users of this class are expected to use the * Opt-Out, etc.) External users of this class are expected to use the
* appropriate public signZone* methods instead of this. * appropriate public signZone* methods instead of this.
* *
* @param zonename * @param zonename The name of the zone
* The name of the zone * @param records The records comprising the zone. They do not have to
* @param records * be in any particular order, as this method will
* The records comprising the zone. They do not have to * order them as necessary.
* be in any * @param kskpairs The key pairs designated as "key signing keys"
* particular order, as this method will order them as * @param zskpairs The key pairs designated as "zone signing keys"
* necessary. * @param start The RRSIG inception time
* @param kskpairs * @param expire The RRSIG expiration time
* The key pairs designated as "key signing keys" * @param fullySignKeyset If true, all keys (ksk or zsk) will sign the DNSKEY
* @param zskpairs * RRset. If false, only the ksks will sign it.
* The key pairs designated as "zone signing keys" * @param dsDigestAlg The hash algorithm to use for generating DS records
* @param start
* The RRSIG inception time
* @param expire
* The RRSIG expiration time
* @param fullySignKeyset
* If true, all keys (ksk or zsk) will sign the DNSKEY
* RRset. If
* false, only the ksks will sign it.
* @param ds_digest_alg
* The hash algorithm to use for generating DS records
* (DSRecord.SHA1_DIGEST_ID, e.g.) * (DSRecord.SHA1_DIGEST_ID, e.g.)
* @param mode * @param mode The NSEC/NSEC3 generation mode: NSEC_MODE,
* The NSEC/NSEC3 generation mode: NSEC_MODE, NSEC3_MODE, * NSEC3_MODE, NSEC3_OPTOUT_MODE, etc.
* NSEC3_OPTOUT_MODE, etc. * @param includedNames When using an Opt-In/Opt-Out mode, the names listed
* @param includedNames * here will be included in the NSEC/NSEC3 chain
* When using an Opt-In/Opt-Out mode, the names listed * regardless
* here will be * @param salt When using an NSEC3 mode, use this salt.
* included in the NSEC/NSEC3 chain regardless * @param iterations When using an NSEC3 mode, use this number of
* @param salt
* When using an NSEC3 mode, use this salt.
* @param iterations
* When using an NSEC3 mode, use this number of
* iterations * iterations
* @param beConservative * @param beConservative If true, then only turn on the Opt-In flag when
* If true, then only turn on the Opt-In flag when there * there are insecure delegations in the span.
* are insecure * Currently this only works for NSEC_EXP_OPT_IN mode.
* delegations in the span. Currently this only works for * @param nsec3paramttl The TTL to use for the generated NSEC3PARAM record.
* NSEC_EXP_OPT_IN mode. * Negative values will use the SOA TTL.
* @param nsec3paramttl
* The TTL to use for the generated NSEC3PARAM record.
* Negative
* values will use the SOA TTL.
* @return an ordered list of {@link org.xbill.DNS.Record} objects, * @return an ordered list of {@link org.xbill.DNS.Record} objects,
* representing the signed zone. * representing the signed zone.
* *
@ -363,7 +319,7 @@ public class JCEDnsSecSigner {
private List<Record> signZone(Name zonename, List<Record> records, private List<Record> signZone(Name zonename, List<Record> records,
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs, List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
Instant start, Instant expire, boolean fullySignKeyset, Instant start, Instant expire, boolean fullySignKeyset,
int ds_digest_alg, int mode, List<Name> includedNames, int dsDigestAlg, int mode, List<Name> includedNames,
byte[] salt, int iterations, long nsec3paramttl, byte[] salt, int iterations, long nsec3paramttl,
boolean beConservative) throws IOException, boolean beConservative) throws IOException,
GeneralSecurityException { GeneralSecurityException {
@ -380,7 +336,7 @@ public class JCEDnsSecSigner {
// Generate DS records. This replaces any non-zone-apex DNSKEY RRs with DS // Generate DS records. This replaces any non-zone-apex DNSKEY RRs with DS
// RRs. // RRs.
SignUtils.generateDSRecords(zonename, records, ds_digest_alg); SignUtils.generateDSRecords(zonename, records, dsDigestAlg);
// Generate the NSEC or NSEC3 records based on 'mode' // Generate the NSEC or NSEC3 records based on 'mode'
switch (mode) { switch (mode) {
@ -398,6 +354,8 @@ public class JCEDnsSecSigner {
SignUtils.generateOptInNSECRecords(zonename, records, includedNames, SignUtils.generateOptInNSECRecords(zonename, records, includedNames,
beConservative); beConservative);
break; break;
default:
throw new NoSuchAlgorithmException("Unknown NSEC/NSEC3 mode: " + mode);
} }
// Re-sort so we can assemble into rrsets. // Re-sort so we can assemble into rrsets.
@ -405,9 +363,9 @@ public class JCEDnsSecSigner {
// Assemble into RRsets and sign. // Assemble into RRsets and sign.
RRset rrset = new RRset(); RRset rrset = new RRset();
ArrayList<Record> signed_records = new ArrayList<Record>(); ArrayList<Record> signedRecords = new ArrayList<>();
Name last_cut = null; Name lastCut = null;
Name last_dname = null; Name lastDname = null;
for (ListIterator<Record> i = records.listIterator(); i.hasNext();) { for (ListIterator<Record> i = records.listIterator(); i.hasNext();) {
Record r = i.next(); Record r = i.next();
@ -430,48 +388,38 @@ public class JCEDnsSecSigner {
// add the RRset to the list of signed_records, regardless of // add the RRset to the list of signed_records, regardless of
// whether or not we actually end up signing the set. // whether or not we actually end up signing the set.
last_cut = addRRset(signed_records, zonename, rrset, kskpairs, zskpairs, start, lastCut = addRRset(signedRecords, zonename, rrset, kskpairs, zskpairs, start,
expire, fullySignKeyset, last_cut, last_dname); expire, fullySignKeyset, lastCut, lastDname);
if (rrset.getType() == Type.DNAME) if (rrset.getType() == Type.DNAME)
last_dname = rrset.getName(); lastDname = rrset.getName();
rrset.clear(); rrset.clear();
rrset.addRR(r); rrset.addRR(r);
} }
// add the last RR set // add the last RR set
addRRset(signed_records, zonename, rrset, kskpairs, zskpairs, start, expire, addRRset(signedRecords, zonename, rrset, kskpairs, zskpairs, start, expire,
fullySignKeyset, last_cut, last_dname); fullySignKeyset, lastCut, lastDname);
return signed_records; return signedRecords;
} }
/** /**
* Given a zone, sign it using standard NSEC records. * Given a zone, sign it using standard NSEC records.
* *
* @param zonename * @param zonename The name of the zone.
* The name of the zone. * @param records The records comprising the zone. They do not have to
* @param records * be in any particular order, as this method will
* The records comprising the zone. They do not have to * order them as necessary.
* be in any * @param kskpairs The key pairs that are designated as "key signing
* particular order, as this method will order them as
* necessary.
* @param kskpairs
* The key pairs that are designated as "key signing
* keys". * keys".
* @param zskpairs * @param zskpairs This key pairs that are designated as "zone signing
* This key pairs that are designated as "zone signing
* keys". * keys".
* @param start * @param start The RRSIG inception time.
* The RRSIG inception time. * @param expire The RRSIG expiration time.
* @param expire * @param fullySignKeyset Sign the zone apex keyset with all available keys
* The RRSIG expiration time. * (instead of just the key signing keys).
* @param fullySignKeyset * @param dsDigestAlg The digest algorithm to use when generating DS
* Sign the zone apex keyset with all available keys
* (instead of just
* the key signing keys).
* @param ds_digest_alg
* The digest algorithm to use when generating DS
* records. * records.
* *
* @return an ordered list of {@link org.xbill.DNS.Record} objects, * @return an ordered list of {@link org.xbill.DNS.Record} objects,
@ -480,58 +428,42 @@ public class JCEDnsSecSigner {
public List<Record> signZone(Name zonename, List<Record> records, public List<Record> signZone(Name zonename, List<Record> records,
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs, List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
Instant start, Instant expire, boolean fullySignKeyset, Instant start, Instant expire, boolean fullySignKeyset,
int ds_digest_alg) throws IOException, int dsDigestAlg) throws IOException,
GeneralSecurityException { GeneralSecurityException {
return signZone(zonename, records, kskpairs, zskpairs, start, expire, return signZone(zonename, records, kskpairs, zskpairs, start, expire,
fullySignKeyset, ds_digest_alg, NSEC_MODE, null, null, 0, 0, false); fullySignKeyset, dsDigestAlg, NSEC_MODE, null, null, 0, 0, false);
} }
/** /**
* Given a zone, sign it using NSEC3 records. * Given a zone, sign it using NSEC3 records.
* *
* @param signer * @param signer A signer (utility) object used to actually sign
* A signer (utility) object used to actually sign stuff. * stuff.
* @param zonename * @param zonename The name of the zone being signed.
* The name of the zone being signed. * @param records The records comprising the zone. They do not have to
* @param records * be in any particular order, as this method will
* The records comprising the zone. They do not have to * order them as necessary.
* be in any * @param kskpairs The key pairs that are designated as "key signing
* particular order, as this method will order them as
* necessary.
* @param kskpairs
* The key pairs that are designated as "key signing
* keys". * keys".
* @param zskpairs * @param zskpairs This key pairs that are designated as "zone signing
* This key pairs that are designated as "zone signing
* keys". * keys".
* @param start * @param start The RRSIG inception time.
* The RRSIG inception time. * @param expire The RRSIG expiration time.
* @param expire * @param fullySignKeyset If true then the DNSKEY RRset will be signed by all
* The RRSIG expiration time. * available keys, if false, only the key signing keys.
* @param fullySignKeyset * @param useOptOut If true, insecure delegations will be omitted from
* If true then the DNSKEY RRset will be signed by all * the NSEC3 chain, and all NSEC3 records will have the
* available * Opt-Out flag set.
* keys, if false, only the key signing keys. * @param includedNames A list of names to include in the NSEC3 chain
* @param useOptOut
* If true, insecure delegations will be omitted from the
* NSEC3
* chain, and all NSEC3 records will have the Opt-Out
* flag set.
* @param includedNames
* A list of names to include in the NSEC3 chain
* regardless. * regardless.
* @param salt * @param salt The salt to use for the NSEC3 hashing. null means no
* The salt to use for the NSEC3 hashing. null means no
* salt. * salt.
* @param iterations * @param iterations The number of iterations to use for the NSEC3
* The number of iterations to use for the NSEC3 hashing. * hashing.
* @param ds_digest_alg * @param dsDigestAlg The digest algorithm to use when generating DS
* The digest algorithm to use when generating DS
* records. * records.
* @param nsec3paramttl * @param nsec3paramttl The TTL to use for the generated NSEC3PARAM record.
* The TTL to use for the generated NSEC3PARAM record. * Negative values will use the SOA TTL.
* Negative
* values will use the SOA TTL.
* @return an ordered list of {@link org.xbill.DNS.Record} objects, * @return an ordered list of {@link org.xbill.DNS.Record} objects,
* representing the signed zone. * representing the signed zone.
* *
@ -542,16 +474,16 @@ public class JCEDnsSecSigner {
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs, List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
Instant start, Instant expire, boolean fullySignKeyset, Instant start, Instant expire, boolean fullySignKeyset,
boolean useOptOut, List<Name> includedNames, boolean useOptOut, List<Name> includedNames,
byte[] salt, int iterations, int ds_digest_alg, byte[] salt, int iterations, int dsDigestAlg,
long nsec3paramttl) throws IOException, long nsec3paramttl) throws IOException,
GeneralSecurityException { GeneralSecurityException {
if (useOptOut) { if (useOptOut) {
return signZone(zonename, records, kskpairs, zskpairs, start, expire, return signZone(zonename, records, kskpairs, zskpairs, start, expire,
fullySignKeyset, ds_digest_alg, NSEC3_OPTOUT_MODE, includedNames, fullySignKeyset, dsDigestAlg, NSEC3_OPTOUT_MODE, includedNames,
salt, iterations, nsec3paramttl, false); salt, iterations, nsec3paramttl, false);
} else { } else {
return signZone(zonename, records, kskpairs, zskpairs, start, expire, return signZone(zonename, records, kskpairs, zskpairs, start, expire,
fullySignKeyset, ds_digest_alg, NSEC3_MODE, null, salt, iterations, fullySignKeyset, dsDigestAlg, NSEC3_MODE, null, salt, iterations,
nsec3paramttl, false); nsec3paramttl, false);
} }
} }
@ -560,37 +492,25 @@ public class JCEDnsSecSigner {
* Given a zone, sign it using experimental Opt-In NSEC records (see RFC * Given a zone, sign it using experimental Opt-In NSEC records (see RFC
* 4956). * 4956).
* *
* @param zonename * @param zonename the name of the zone.
* the name of the zone. * @param records the records comprising the zone. They do not
* @param records * have to be in any particular order, as this
* the records comprising the zone. They do not have * method will order them as necessary.
* to be in any * @param kskpairs the key pairs that are designated as "key
* particular order, as this method will order them
* as necessary.
* @param kskpairs
* the key pairs that are designated as "key signing
* keys".
* @param zskpairs
* this key pairs that are designated as "zone
* signing keys". * signing keys".
* @param start * @param zskpairs this key pairs that are designated as "zone
* the RRSIG inception time. * signing keys".
* @param expire * @param start the RRSIG inception time.
* the RRSIG expiration time. * @param expire the RRSIG expiration time.
* @param useConservativeOptIn * @param useConservativeOptIn if true, Opt-In NSEC records will only be
* if true, Opt-In NSEC records will only be * generated if there are insecure, unsigned
* generated if there are * delegations in the span.
* insecure, unsigned delegations in the span. * @param fullySignKeyset sign the zone apex keyset with all available
* @param fullySignKeyset
* sign the zone apex keyset with all available
* keys. * keys.
* @param ds_digest_alg * @param dsDigestAlg The digest algorithm to use when generating DS
* The digest algorithm to use when generating DS
* records. * records.
* @param NSECIncludeNames * @param nsecIncludeNames names that are to be included in the NSEC chain
* names that are to be included in the NSEC chain * regardless. This may be null.
* regardless. This
* may be null.
* @return an ordered list of {@link org.xbill.DNS.Record} objects, * @return an ordered list of {@link org.xbill.DNS.Record} objects,
* representing the signed zone. * representing the signed zone.
*/ */
@ -598,12 +518,12 @@ public class JCEDnsSecSigner {
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs, List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
Instant start, Instant expire, Instant start, Instant expire,
boolean useConservativeOptIn, boolean useConservativeOptIn,
boolean fullySignKeyset, List<Name> NSECIncludeNames, boolean fullySignKeyset, List<Name> nsecIncludeNames,
int ds_digest_alg) throws IOException, int dsDigestAlg) throws IOException,
GeneralSecurityException { GeneralSecurityException {
return signZone(zonename, records, kskpairs, zskpairs, start, expire, return signZone(zonename, records, kskpairs, zskpairs, start, expire,
fullySignKeyset, ds_digest_alg, NSEC_EXP_OPT_IN, NSECIncludeNames, fullySignKeyset, dsDigestAlg, NSEC_EXP_OPT_IN, nsecIncludeNames,
null, 0, 0, useConservativeOptIn); null, 0, 0, useConservativeOptIn);
} }
} }

View File

@ -33,6 +33,7 @@ import org.xbill.DNS.Type;
public class RecordComparator implements Comparator<Record> { public class RecordComparator implements Comparator<Record> {
public RecordComparator() { public RecordComparator() {
// nothing to initialize
} }
/** /**
@ -65,15 +66,15 @@ public class RecordComparator implements Comparator<Record> {
} }
private int compareRDATA(Record a, Record b) { private int compareRDATA(Record a, Record b) {
byte[] a_rdata = a.rdataToWireCanonical(); byte[] aRdata = a.rdataToWireCanonical();
byte[] b_rdata = b.rdataToWireCanonical(); byte[] bRdata = b.rdataToWireCanonical();
for (int i = 0; i < a_rdata.length && i < b_rdata.length; i++) { for (int i = 0; i < aRdata.length && i < bRdata.length; i++) {
int n = (a_rdata[i] & 0xFF) - (b_rdata[i] & 0xFF); int n = (aRdata[i] & 0xFF) - (bRdata[i] & 0xFF);
if (n != 0) if (n != 0)
return n; return n;
} }
return (a_rdata.length - b_rdata.length); return (aRdata.length - bRdata.length);
} }
public int compare(Record a, Record b) { public int compare(Record a, Record b) {
@ -88,27 +89,27 @@ public class RecordComparator implements Comparator<Record> {
if (res != 0) if (res != 0)
return res; return res;
int a_type = a.getType(); int aType = a.getType();
int b_type = b.getType(); int bType = b.getType();
int sig_type = 0; int sigType = 0;
if (a_type == Type.RRSIG) { if (aType == Type.RRSIG) {
a_type = ((RRSIGRecord) a).getTypeCovered(); aType = ((RRSIGRecord) a).getTypeCovered();
if (b_type != Type.RRSIG) if (bType != Type.RRSIG)
sig_type = 1; sigType = 1;
} }
if (b_type == Type.RRSIG) { if (bType == Type.RRSIG) {
b_type = ((RRSIGRecord) b).getTypeCovered(); bType = ((RRSIGRecord) b).getTypeCovered();
if (a.getType() != Type.RRSIG) if (a.getType() != Type.RRSIG)
sig_type = -1; sigType = -1;
} }
res = compareTypes(a_type, b_type); res = compareTypes(aType, bType);
if (res != 0) if (res != 0)
return res; return res;
if (sig_type != 0) if (sigType != 0)
return sig_type; return sigType;
return compareRDATA(a, b); return compareRDATA(a, b);
} }

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@ public class TypeMap {
private Set<Integer> typeSet; private Set<Integer> typeSet;
public TypeMap() { public TypeMap() {
this.typeSet = new HashSet<Integer>(); this.typeSet = new HashSet<>();
} }
/** Add the given type to the typemap. */ /** Add the given type to the typemap. */
@ -78,20 +78,20 @@ public class TypeMap {
TypeMap typemap = new TypeMap(); TypeMap typemap = new TypeMap();
int page; int page;
int byte_length; int byteLength;
while (m < map.length) { while (m < map.length) {
page = map[m++]; page = map[m++];
byte_length = map[m++]; byteLength = map[m++];
for (int i = 0; i < byte_length; i++) { for (int i = 0; i < byteLength; i++) {
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
if ((map[m + i] & (1 << (7 - j))) != 0) { if (((map[m + i] & 0xFF) & (1 << (7 - j))) != 0) {
typemap.set((page << 8) + (i * 8) + j); typemap.set((page << 8) + (i * 8) + j);
} }
} }
} }
m += byte_length; m += byteLength;
} }
return typemap; return typemap;
@ -115,7 +115,7 @@ public class TypeMap {
int[] types = getTypes(); int[] types = getTypes();
Arrays.sort(types); Arrays.sort(types);
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < types.length; i++) { for (int i = 0; i < types.length; i++) {
if (i > 0) if (i > 0)
@ -129,16 +129,16 @@ public class TypeMap {
protected static void mapToWire(DNSOutput out, int[] types, int base, int start, int end) { protected static void mapToWire(DNSOutput out, int[] types, int base, int start, int end) {
// calculate the length of this map by looking at the largest // calculate the length of this map by looking at the largest
// typecode in this section. // typecode in this section.
int max_type = types[end - 1] & 0xFF; int maxType = types[end - 1] & 0xFF;
int map_length = (max_type / 8) + 1; int mapLength = (maxType / 8) + 1;
// write the map "header" -- the base and the length of the map. // write the map "header" -- the base and the length of the map.
out.writeU8(base & 0xFF); out.writeU8(base & 0xFF);
out.writeU8(map_length & 0xFF); out.writeU8(mapLength & 0xFF);
// allocate a temporary scratch space for caculating the actual // allocate a temporary scratch space for caculating the actual
// bitmap. // bitmap.
byte[] map = new byte[map_length]; byte[] map = new byte[mapLength];
// for each type in our sub-array, set its corresponding bit in the map. // for each type in our sub-array, set its corresponding bit in the map.
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
@ -179,7 +179,7 @@ public class TypeMap {
} }
public int[] getTypes() { public int[] getTypes() {
Integer[] a = (Integer[]) typeSet.toArray(integerArray); Integer[] a = typeSet.toArray(integerArray);
int[] res = new int[a.length]; int[] res = new int[a.length];
for (int i = 0; i < res.length; i++) { for (int i = 0; i < res.length; i++) {
@ -189,8 +189,8 @@ public class TypeMap {
return res; return res;
} }
public static int[] fromWireToTypes(byte[] wire_fmt) { public static int[] fromWireToTypes(byte[] wireFmt) {
return TypeMap.fromBytes(wire_fmt).getTypes(); return TypeMap.fromBytes(wireFmt).getTypes();
} }
public static byte[] fromTypesToWire(int[] types) { public static byte[] fromTypesToWire(int[] types) {

View File

@ -39,6 +39,10 @@ import org.xbill.DNS.Type;
*/ */
public class ZoneUtils { public class ZoneUtils {
private ZoneUtils() {
}
/** /**
* Load a zone file. * Load a zone file.
* *
@ -53,19 +57,10 @@ public class ZoneUtils {
* if something goes wrong reading the zone file. * if something goes wrong reading the zone file.
*/ */
public static List<Record> readZoneFile(String zonefile, Name origin) throws IOException { public static List<Record> readZoneFile(String zonefile, Name origin) throws IOException {
ArrayList<Record> records = new ArrayList<Record>(); ArrayList<Record> records = new ArrayList<>();
Master m; try (Master m = zonefile.equals("-") ? new Master(System.in) : new Master(zonefile, origin)) {
try {
if (zonefile.equals("-")) {
m = new Master(System.in);
} else {
m = new Master(zonefile, origin);
}
Record r = null; Record r = null;
while ((r = m.nextRecord()) != null) { while ((r = m.nextRecord()) != null) {
records.add(r); records.add(r);
} }
} catch (IOException e) { } catch (IOException e) {
@ -120,7 +115,7 @@ public class ZoneUtils {
} }
public static List<Record> findRRs(List<Record> records, Name name, int type) { public static List<Record> findRRs(List<Record> records, Name name, int type) {
List<Record> res = new ArrayList<Record>(); List<Record> res = new ArrayList<>();
for (Record r : records) { for (Record r : records) {
if (r.getName().equals(name) && r.getType() == type) { if (r.getName().equals(name) && r.getType() == type) {
res.add(r); res.add(r);

View File

@ -88,10 +88,12 @@ public class ZoneVerifier {
public boolean equals(Object o) { public boolean equals(Object o) {
return super.equals(o); return super.equals(o);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return super.hashCode(); return super.hashCode();
} }
boolean getMark() { boolean getMark() {
return mIsMarked; return mIsMarked;
} }
@ -148,7 +150,8 @@ public class ZoneVerifier {
/** /**
* Add a record to the various maps. * Add a record to the various maps.
* *
* @return TODO * @return true if the RR was added, false if it wasn't (because it was a
* duplicate)
*/ */
private boolean addRR(Record r) { private boolean addRR(Record r) {
Name n = r.getName(); Name n = r.getName();
@ -206,8 +209,8 @@ public class ZoneVerifier {
* Given an unsorted list of records, load the node and rrset maps, as well as * Given an unsorted list of records, load the node and rrset maps, as well as
* determine the NSEC3 parameters and signing type. * determine the NSEC3 parameters and signing type.
* *
* @param records * @param records an unsorted list of {@link org.xbill.DNS.Record} objects.
* @return TODO * @return the number of errors encountered.
*/ */
private int calculateNodes(List<Record> records) { private int calculateNodes(List<Record> records) {
mNodeMap = new TreeMap<>(); mNodeMap = new TreeMap<>();
@ -251,7 +254,7 @@ public class ZoneVerifier {
* Given a name, typeset, and name of the last zone cut, determine the node * Given a name, typeset, and name of the last zone cut, determine the node
* type. * type.
*/ */
private NodeType determineNodeType(Name n, Set<Integer> typeset, Name last_cut) { private NodeType determineNodeType(Name n, Set<Integer> typeset, Name lastCut) {
// All RRs at the zone apex are normal // All RRs at the zone apex are normal
if (n.equals(mZoneName)) if (n.equals(mZoneName))
return NodeType.NORMAL; return NodeType.NORMAL;
@ -263,7 +266,7 @@ public class ZoneVerifier {
} }
// If the node is below a zone cut (either a delegation or DNAME), it is // If the node is below a zone cut (either a delegation or DNAME), it is
// glue. // glue.
if (last_cut != null && n.subdomain(last_cut) && !n.equals(last_cut)) { if (lastCut != null && n.subdomain(lastCut) && !n.equals(lastCut)) {
return NodeType.GLUE; return NodeType.GLUE;
} }
@ -294,13 +297,13 @@ public class ZoneVerifier {
*/ */
private int processNodes() throws NoSuchAlgorithmException, TextParseException { private int processNodes() throws NoSuchAlgorithmException, TextParseException {
int errors = 0; int errors = 0;
Name last_cut = null; Name lastCut = null;
for (Map.Entry<Name, Set<Integer>> entry : mNodeMap.entrySet()) { for (Map.Entry<Name, Set<Integer>> entry : mNodeMap.entrySet()) {
Name n = entry.getKey(); Name n = entry.getKey();
Set<Integer> typeset = entry.getValue(); Set<Integer> typeset = entry.getValue();
NodeType ntype = determineNodeType(n, typeset, last_cut); NodeType ntype = determineNodeType(n, typeset, lastCut);
log.finest("Node " + n + " is type " + ntype); log.finest("Node " + n + " is type " + ntype);
// we can ignore glue/invalid RRs. // we can ignore glue/invalid RRs.
@ -309,7 +312,7 @@ public class ZoneVerifier {
// record the last zone cut if this node is a zone cut. // record the last zone cut if this node is a zone cut.
if (ntype == NodeType.DELEGATION || typeset.contains(Type.DNAME)) { if (ntype == NodeType.DELEGATION || typeset.contains(Type.DNAME)) {
last_cut = n; lastCut = n;
} }
// check all of the RRsets that should be signed // check all of the RRsets that should be signed