Compare commits
10 Commits
master
...
33b4630f4b
| Author | SHA1 | Date | |
|---|---|---|---|
| 33b4630f4b | |||
| 1e342b1fb6 | |||
| 15eb319b26 | |||
| 31f35a17f8 | |||
| 19a76c00ae | |||
| 88cc729312 | |||
| 75ff297c09 | |||
| bf6a68e864 | |||
| 39d938c4e1 | |||
| c80595b118 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,5 @@
|
|||||||
build
|
build
|
||||||
bin/main
|
bin/main
|
||||||
dist/
|
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.gradle
|
.gradle
|
||||||
@@ -9,4 +8,3 @@ docs
|
|||||||
test-zones
|
test-zones
|
||||||
settings.json
|
settings.json
|
||||||
.settings
|
.settings
|
||||||
.DS_Store
|
|
||||||
|
|||||||
32
ChangeLog
32
ChangeLog
@@ -1,35 +1,3 @@
|
|||||||
2026-02-19 David Blacka <david@blacka.com>
|
|
||||||
|
|
||||||
* Update dnsjava to 3.6.4 (this adds a number of "new" RR types, including SVCB/HTTPS and ZONEMD.)
|
|
||||||
|
|
||||||
2024-11-02 David Blacka <david@blacka.com>
|
|
||||||
|
|
||||||
* Handle I/O errors from ZoneUtils.readZoneFile() correctly (issue #19).
|
|
||||||
|
|
||||||
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 algorithm 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>
|
2024-03-25 David Blacka <davidb@verisign.com>
|
||||||
|
|
||||||
* Released version 0.19
|
* Released version 0.19
|
||||||
|
|||||||
@@ -11,3 +11,5 @@ 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.
|
||||||
|
|||||||
32
README.md
32
README.md
@@ -8,13 +8,9 @@ This is a collection of DNSSEC tools written in Java. They are intended to be a
|
|||||||
|
|
||||||
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](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.
|
See the "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:
|
||||||
|
|
||||||
@@ -25,11 +21,9 @@ The binary distributions can be downloaded from the [releases](https://github.co
|
|||||||
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:
|
||||||
|
|
||||||
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. Unpack the source distribution, preferably into the same directory that the binary distribution was unpacked.
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -38,19 +32,17 @@ There is a source distribution also downloadable from the [releases](https://git
|
|||||||
|
|
||||||
ant
|
ant
|
||||||
|
|
||||||
4. You can build the distribution tarballs with 'ant dist', although the main `ant` build command will have built the primary jar file.
|
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.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|||||||
19
bin/_jdnssec-dstool
Executable file
19
bin/_jdnssec-dstool
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#! /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 "$@"
|
||||||
19
bin/_jdnssec-keygen
Executable file
19
bin/_jdnssec-keygen
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#! /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 "$@"
|
||||||
19
bin/_jdnssec-keyinfo
Executable file
19
bin/_jdnssec-keyinfo
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#! /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 "$@"
|
||||||
19
bin/_jdnssec-signkeyset
Executable file
19
bin/_jdnssec-signkeyset
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#! /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 "$@"
|
||||||
19
bin/_jdnssec-signzone
Executable file
19
bin/_jdnssec-signzone
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#! /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 "$@"
|
||||||
19
bin/_jdnssec-verifyzone
Executable file
19
bin/_jdnssec-verifyzone
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#! /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 "$@"
|
||||||
19
bin/_jdnssec-zoneformat
Executable file
19
bin/_jdnssec-zoneformat
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#! /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 "$@"
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#! /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 "$@"
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
build.deprecation=true
|
build.deprecation=true
|
||||||
build.debug=false
|
build.debug=true
|
||||||
build.java_version=17
|
|
||||||
|
|||||||
89
build.xml
89
build.xml
@@ -10,20 +10,18 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<project default="build" basedir=".">
|
<project default="compile" 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" />
|
||||||
@@ -35,7 +33,6 @@
|
|||||||
<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">
|
||||||
@@ -43,7 +40,7 @@
|
|||||||
<mkdir dir="${build.lib.dest}" />
|
<mkdir dir="${build.lib.dest}" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="compile" depends="prepare-src" >
|
<target name="sectools" depends="prepare-src" >
|
||||||
<javac srcdir="${build.src}"
|
<javac srcdir="${build.src}"
|
||||||
destdir="${build.dest}"
|
destdir="${build.dest}"
|
||||||
classpathref="project.classpath"
|
classpathref="project.classpath"
|
||||||
@@ -51,32 +48,17 @@
|
|||||||
includeantruntime="false"
|
includeantruntime="false"
|
||||||
includes="com/verisignlabs/dnssec/"
|
includes="com/verisignlabs/dnssec/"
|
||||||
debug="${build.debug}"
|
debug="${build.debug}"
|
||||||
release="${build.java_version}" />
|
release="17" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="build-jar" depends="usage, compile">
|
<target name="sectools-jar" depends="usage,sectools">
|
||||||
<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="build"
|
<target name="compile"
|
||||||
depends="usage,build-jar">
|
depends="usage,sectools-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.6.4.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">
|
||||||
@@ -93,21 +75,16 @@
|
|||||||
</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="dist-clean" depends="usage">
|
<target name="sectools-dist-prepare" depends="usage, compile, javadoc">
|
||||||
<delete dir="${dist.name}" />
|
<mkdir dir="${sectools-distname}" />
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="dist-prepare" depends="usage, build, javadoc">
|
<copy todir="${sectools-distname}">
|
||||||
<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" />
|
||||||
@@ -122,33 +99,37 @@
|
|||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
<copy todir="${dist.name}/lib">
|
<copy todir="${sectools-distname}/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="${dist.name}/bin/jdnssec-*" />
|
<include name="${sectools-distname}/bin/jdnssec-*" />
|
||||||
</patternset>
|
</patternset>
|
||||||
|
|
||||||
<patternset id="src.files">
|
<patternset id="src.files">
|
||||||
<include name="${dist.name}/src/" />
|
<include name="${sectools-distname}/src/" />
|
||||||
<include name="${dist.name}/build.xml" />
|
<include name="${sectools-distname}/build.xml" />
|
||||||
<include name="${dist.name}/build.properties" />
|
<include name="${sectools-distname}/build.properties" />
|
||||||
</patternset>
|
</patternset>
|
||||||
|
|
||||||
<patternset id="bin.files">
|
<patternset id="bin.files">
|
||||||
<include name="${dist.name}/doc/" />
|
<include name="${sectools-distname}/doc/" />
|
||||||
<include name="${dist.name}/lib/" />
|
<include name="${sectools-distname}/lib/" />
|
||||||
<include name="${dist.name}/licenses/" />
|
<include name="${sectools-distname}/licenses/" />
|
||||||
<include name="${dist.name}/VERSION" />
|
<include name="${sectools-distname}/VERSION" />
|
||||||
<include name="${dist.name}/README" />
|
<include name="${sectools-distname}/README" />
|
||||||
</patternset>
|
</patternset>
|
||||||
|
|
||||||
<target name="bin-dist" depends="dist-prepare">
|
<target name="sectools-bin-dist" depends="sectools-dist-prepare">
|
||||||
<tar destfile="${dist.dir}/${dist.name}.tar.gz" compression="gzip">
|
<tar destfile="${sectools-distname}.tar.gz" compression="gzip">
|
||||||
<tarfileset mode="755" dir=".">
|
<tarfileset mode="755" dir=".">
|
||||||
<patternset refid="exec.files" />
|
<patternset refid="exec.files" />
|
||||||
</tarfileset>
|
</tarfileset>
|
||||||
@@ -158,20 +139,21 @@
|
|||||||
</tar>
|
</tar>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="src-dist" depends="dist-prepare">
|
<target name="sectools-src-dist" depends="sectools-dist-prepare">
|
||||||
<tar destfile="${dist.dir}/${dist.name}-src.tar.gz" compression="gzip">
|
<tar destfile="${sectools-distname}-src.tar.gz"
|
||||||
|
compression="gzip">
|
||||||
<tarfileset dir=".">
|
<tarfileset dir=".">
|
||||||
<patternset refid="src.files" />
|
<patternset refid="src.files" />
|
||||||
</tarfileset>
|
</tarfileset>
|
||||||
</tar>
|
</tar>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="onejar"
|
<target name="sectools-dist"
|
||||||
depends="build-onejar">
|
depends="sectools-bin-dist,sectools-src-dist, sectools-dist-clean">
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="dist"
|
|
||||||
depends="bin-dist, src-dist, build-onejar, dist-clean">
|
<target name="dist" depends="sectools-dist">
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="usage">
|
<target name="usage">
|
||||||
@@ -179,11 +161,10 @@
|
|||||||
<echo message="jdnssec-tools 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=" build (default) - compiles the source code, creates main jar" />
|
<echo message=" compile (default) - compiles the source code, creates 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>
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
# 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
|
|
||||||
BIN
lib/dnsjava-3.5.3.jar
Normal file
BIN
lib/dnsjava-3.5.3.jar
Normal file
Binary file not shown.
Binary file not shown.
@@ -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.util.Properties;
|
import java.text.SimpleDateFormat;
|
||||||
|
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,19 +48,8 @@ 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 Logger log = Logger.getLogger(this.getClass().toString());
|
protected static Logger staticLog = Logger.getLogger(CLBase.class.getName());
|
||||||
protected Options opts;
|
protected Logger log;
|
||||||
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
|
||||||
@@ -81,151 +70,136 @@ public abstract class CLBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a base class for command line parsing state. Subclasses should
|
||||||
|
* override setupOptions and processOptions.
|
||||||
|
*/
|
||||||
|
public static class CLIStateBase {
|
||||||
|
protected Options opts;
|
||||||
|
protected String usageStr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base constructor. This will setup the command line options.
|
||||||
|
*
|
||||||
|
* @param usage
|
||||||
|
* The command line usage string (e.g.,
|
||||||
|
* "jdnssec-foo [..options..] zonefile")
|
||||||
|
*/
|
||||||
|
public CLIStateBase(String usage) {
|
||||||
|
usageStr = usage;
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
/** This is the base set of command line options provided to all subclasses. */
|
/** This is the base set of command line options provided to all subclasses. */
|
||||||
private void setupCommonOptions() {
|
private void setup() {
|
||||||
// Set up the standard set of options that all jdnssec command line tools will
|
// Set up the standard set of options that all jdnssec command line tools will
|
||||||
// implement.
|
// implement.
|
||||||
|
opts = new Options();
|
||||||
|
|
||||||
// boolean options
|
// boolean options
|
||||||
opts.addOption("h", "help", false, "Print this message.");
|
opts.addOption("h", "help", false, "Print this message.");
|
||||||
opts.addOption("m", "multiline", false,
|
opts.addOption("m", "multiline", false,
|
||||||
"Output DNS records using 'multiline' format");
|
"Output DNS records using 'multiline' format");
|
||||||
|
|
||||||
opts.addOption(Option.builder("l").longOpt("log-level").argName("level").hasArg()
|
opts.addOption(Option.builder("v").longOpt("verbose").argName("level").hasArg().desc(
|
||||||
.desc("set the logging level with either java.util.logging levels, or 0-6").build());
|
"verbosity level -- 0: silence, 1: error, 2: warning, 3: info, 4/5: fine, 6: finest; default: 2 (warning)")
|
||||||
opts.addOption(Option.builder("v").longOpt("verbose").desc(
|
.build());
|
||||||
"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")
|
opts.addOption(Option.builder("A").hasArg().argName("alias:original:mnemonic").longOpt("alg-alias")
|
||||||
.desc("Define an alias for an algorithm").build());
|
.desc("Define an alias for an algorithm").build());
|
||||||
|
|
||||||
|
setupOptions(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an overridable method for subclasses to add their own command line
|
* This is an overridable method for subclasses to add their own command
|
||||||
* options.
|
* line options.
|
||||||
*/
|
|
||||||
protected abstract void setupOptions();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the command line options
|
|
||||||
*/
|
|
||||||
public void setup() {
|
|
||||||
opts = new Options();
|
|
||||||
setupCommonOptions();
|
|
||||||
setupOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
* @param opts
|
||||||
|
* the options object to add (via OptionBuilder, typically) new
|
||||||
|
* options to.
|
||||||
*/
|
*/
|
||||||
public void parseCommandLine(String[] args) {
|
protected void setupOptions(Options opts) {
|
||||||
String[] logLevelOptionKeys = { "log_level", "log-level" };
|
// Subclasses generally override this.
|
||||||
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 main method for parsing the command line arguments.
|
||||||
|
* 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 (cli.hasOption('h')) {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
String loadedConfig = loadConfig(cli.getOptionValue('c'));
|
|
||||||
|
|
||||||
Logger rootLogger = Logger.getLogger("");
|
Logger rootLogger = Logger.getLogger("");
|
||||||
|
int value = parseInt(cli.getOptionValue('v'), -1);
|
||||||
|
|
||||||
// we set log level with both --log-level and -v/--verbose.
|
switch (value) {
|
||||||
String logLevel = cliOption("log-level", logLevelOptionKeys, null);
|
case 0:
|
||||||
if (logLevel == null) {
|
rootLogger.setLevel(Level.OFF);
|
||||||
logLevel = cli.hasOption("v") ? "fine" : "warning";
|
break;
|
||||||
|
case 1:
|
||||||
|
rootLogger.setLevel(Level.SEVERE);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
rootLogger.setLevel(Level.WARNING);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
rootLogger.setLevel(Level.INFO);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
rootLogger.setLevel(Level.CONFIG);
|
||||||
|
case 5:
|
||||||
|
rootLogger.setLevel(Level.FINE);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
rootLogger.setLevel(Level.ALL);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setLogLevel(rootLogger, logLevel);
|
|
||||||
|
|
||||||
|
// I hate java.util.logging, btw.
|
||||||
for (Handler h : rootLogger.getHandlers()) {
|
for (Handler h : rootLogger.getHandlers()) {
|
||||||
h.setLevel(rootLogger.getLevel());
|
h.setLevel(rootLogger.getLevel());
|
||||||
h.setFormatter(new BareLogFormatter());
|
h.setFormatter(new BareLogFormatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadedConfig != null) {
|
if (cli.hasOption('m')) {
|
||||||
log.info("Loaded config file: " + loadedConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cliBooleanOption("m", multilineOptionKeys, false)) {
|
|
||||||
org.xbill.DNS.Options.set("multiline");
|
org.xbill.DNS.Options.set("multiline");
|
||||||
}
|
}
|
||||||
|
|
||||||
processAliasOptions();
|
String[] optstrs = null;
|
||||||
|
if ((optstrs = cli.getOptionValues('A')) != null) {
|
||||||
|
for (int i = 0; i < optstrs.length; i++) {
|
||||||
|
addArgAlias(optstrs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
processOptions();
|
processOptions(cli);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process additional tool-specific options. Subclasses generally override
|
* Process additional tool-specific options. Subclasses generally override
|
||||||
* this.
|
* 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
|
* @param cli
|
||||||
* first.
|
* The {@link CommandLine} object containing the parsed command
|
||||||
* @return The path of the file that was actually loaded, or null if no config
|
* line state.
|
||||||
* file was loaded.
|
|
||||||
*/
|
*/
|
||||||
protected String loadConfig(String configFile) {
|
protected void processOptions(CommandLine cli) throws ParseException {
|
||||||
// Do not load config files twice
|
// Subclasses generally override this.
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try (FileInputStream stream = new FileInputStream(f)) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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. */
|
/** Print out the usage and help statements, then quit. */
|
||||||
public void usage() {
|
public void usage() {
|
||||||
HelpFormatter f = new HelpFormatter();
|
HelpFormatter f = new HelpFormatter();
|
||||||
@@ -233,167 +207,116 @@ public abstract class CLBase {
|
|||||||
PrintWriter out = new PrintWriter(System.err);
|
PrintWriter out = new PrintWriter(System.err);
|
||||||
|
|
||||||
// print our own usage statement:
|
// print our own usage statement:
|
||||||
f.printHelp(out, 120, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
|
f.printHelp(out, 75, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
|
||||||
HelpFormatter.DEFAULT_DESC_PAD, null);
|
HelpFormatter.DEFAULT_DESC_PAD, null);
|
||||||
|
|
||||||
out.flush();
|
out.flush();
|
||||||
System.exit(0);
|
System.exit(64);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected void addArgAlias(String s) {
|
||||||
* Set the logging level based on a string value
|
if (s == null)
|
||||||
*
|
return;
|
||||||
* @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:
|
|
||||||
level = Level.OFF;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
level = Level.SEVERE;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
default:
|
|
||||||
level = Level.WARNING;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
level = Level.INFO;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
level = Level.FINE;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
level = Level.ALL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
level = Level.parse(levelStr.toUpperCase());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
System.err.println("Verbosity level '" + levelStr + "' not recognized");
|
|
||||||
level = Level.WARNING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.setLevel(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process both property file based alias definitions and command line alias
|
|
||||||
* definitions
|
|
||||||
*/
|
|
||||||
protected void processAliasOptions() {
|
|
||||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
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];
|
|
||||||
|
|
||||||
if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
|
String[] v = s.split(":");
|
||||||
algs.addAlias(aliasAlg, mnemonic, origAlg);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next see if we have any alias options in properties
|
public static int parseInt(String s, int def) {
|
||||||
// Those look like 'signzone.alias.<alias-mnemonic> =
|
try {
|
||||||
// <orig-alg-num>:<alias-alg-num>'
|
return Integer.parseInt(s);
|
||||||
for (String key : props.stringPropertyNames()) {
|
} catch (NumberFormatException e) {
|
||||||
if (key.startsWith(name + ".alias.") || key.startsWith("alias.")) {
|
return def;
|
||||||
String[] keyComponents = key.split("\\.");
|
}
|
||||||
String mnemonic = keyComponents[keyComponents.length - 1];
|
}
|
||||||
String[] valueComponents = props.getProperty(key).split(":");
|
|
||||||
int origAlg = Utils.parseInt(valueComponents[0], -1);
|
|
||||||
int aliasAlg = Utils.parseInt(valueComponents[1], -1);
|
|
||||||
|
|
||||||
if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
|
public static long parseLong(String s, long def) {
|
||||||
algs.addAlias(aliasAlg, mnemonic, origAlg);
|
try {
|
||||||
}
|
return Long.parseLong(s);
|
||||||
}
|
} catch (NumberFormatException e) {
|
||||||
|
return def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a parsed command line, option, and list of possible config
|
* Calculate a date/time from a command line time/offset duration string.
|
||||||
* properties, and a default value, determine value for the option
|
|
||||||
*
|
*
|
||||||
* @param option The option name
|
* @param start
|
||||||
* @param properties A list of configuration parameters that we would like
|
* the start time to calculate offsets from.
|
||||||
* to use for this option, from most preferred to least.
|
* @param duration
|
||||||
* @param defaultValue A default value to return if either the option or
|
* the time/offset string to parse.
|
||||||
* config value cannot be parsed, or neither are present.
|
* @return the calculated time.
|
||||||
* @return The found value, or the default value.
|
|
||||||
*/
|
*/
|
||||||
protected String cliOption(String option, String[] properties, String defaultValue) {
|
public static Instant convertDuration(Instant start, String duration) throws ParseException {
|
||||||
if (cli.hasOption(option)) {
|
if (start == null) {
|
||||||
return cli.getOptionValue(option);
|
start = Instant.now();
|
||||||
}
|
|
||||||
for (String property : properties) {
|
|
||||||
// first look up the scoped version of the property
|
|
||||||
String value = props.getProperty(name + "." + property);
|
|
||||||
if (value != null) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
value = props.getProperty(property);
|
|
||||||
if (value != null) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (duration.startsWith("now")) {
|
||||||
* Given a parsed command line, option, and list of possible config
|
start = Instant.now();
|
||||||
* properties, determine the value for the option, converting the value to
|
if (duration.indexOf("+") < 0)
|
||||||
* long.
|
return start;
|
||||||
*/
|
|
||||||
protected long cliLongOption(String option, String[] properties, long defaultValue) {
|
duration = duration.substring(3);
|
||||||
String value = cliOption(option, properties, Long.toString(defaultValue));
|
|
||||||
return Utils.parseLong(value, defaultValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (duration.startsWith("+")) {
|
||||||
* Given a parsed command line, option, and list of possible config
|
long offset = parseLong(duration.substring(1), 0);
|
||||||
* properties, determine the value for the option, converting the value to
|
return start.plusSeconds(offset);
|
||||||
* int.
|
|
||||||
*/
|
|
||||||
protected int cliIntOption(String option, String[] properties, int defaultValue) {
|
|
||||||
String value = cliOption(option, properties, Integer.toString(defaultValue));
|
|
||||||
return Utils.parseInt(value, defaultValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// This is a heuristic to distinguish UNIX epoch times from the zone file
|
||||||
* Given a parsed command line, option, and list of possible config
|
// format standard (which is length == 14)
|
||||||
* properties, determine the value for the option, converting the value to
|
if (duration.length() <= 10) {
|
||||||
* a boolean.
|
long epoch = parseLong(duration, 0);
|
||||||
*/
|
return Instant.ofEpochSecond(epoch);
|
||||||
protected boolean cliBooleanOption(String option, String[] properties, boolean defaultValue) {
|
}
|
||||||
if (cli.hasOption(option)) {
|
|
||||||
return true;
|
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||||
|
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
|
try {
|
||||||
|
Date parsedDate = dateFormatter.parse(duration);
|
||||||
|
return parsedDate.toInstant();
|
||||||
|
} catch (java.text.ParseException e) {
|
||||||
|
throw new ParseException(e.getMessage());
|
||||||
}
|
}
|
||||||
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(String[] args) {
|
public void run(CLIStateBase state, 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 {
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,18 +18,21 @@
|
|||||||
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
|
||||||
@@ -37,15 +40,7 @@ import com.verisignlabs.dnssec.security.DnsKeyPair;
|
|||||||
* @author David Blacka
|
* @author David Blacka
|
||||||
*/
|
*/
|
||||||
public class DSTool extends CLBase {
|
public class DSTool extends CLBase {
|
||||||
private dsType createType = dsType.DS;
|
private CLIState state;
|
||||||
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 {
|
||||||
@@ -57,76 +52,80 @@ public class DSTool extends CLBase {
|
|||||||
* state.
|
* state.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
protected static class CLIState extends CLIStateBase {
|
||||||
|
public dsType createType = dsType.DS;
|
||||||
|
public String outputfile = null;
|
||||||
|
public String keyname = null;
|
||||||
|
public int digestId = DNSSEC.Digest.SHA256;
|
||||||
|
|
||||||
|
public CLIState() {
|
||||||
|
super("jdnssec-dstool [..options..] keyfile");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the command line options.
|
* Set up the command line options.
|
||||||
*
|
*
|
||||||
* @return a set of command line options.
|
* @return a set of command line options.
|
||||||
*/
|
*/
|
||||||
protected void setupOptions() {
|
@Override
|
||||||
|
protected void setupOptions(Options opts) {
|
||||||
opts.addOption(Option.builder("D").longOpt("dlv").desc("Generate a DLV record instead.").build());
|
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("C").longOpt("cds").desc("Generate a CDS record instead").build());
|
||||||
String[] algStrings = DSAlgorithm.getInstance().supportedAlgorithmMnemonics();
|
|
||||||
String algStringSet = String.join(" | ", algStrings);
|
|
||||||
opts.addOption(
|
opts.addOption(
|
||||||
Option.builder("d").hasArg().argName("id").longOpt("digest").desc(algStringSet + ": default is SHA256")
|
Option.builder("d").hasArg().argName("id").longOpt("digest").desc("The digest algorithm to use").build());
|
||||||
.build());
|
|
||||||
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output").desc("output to file").build());
|
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOptions() {
|
@Override
|
||||||
String[] digestAlgOptionKeys = { "digest_algorithm", "digest_id" };
|
protected void processOptions(CommandLine cli)
|
||||||
String[] dsTTLOptionKeys = { "ds_ttl", "ttl" };
|
throws org.apache.commons.cli.ParseException {
|
||||||
|
|
||||||
outputfile = cli.getOptionValue('f');
|
outputfile = cli.getOptionValue('f');
|
||||||
if (cli.hasOption("dlv")) {
|
if (cli.hasOption("dlv")) {
|
||||||
createType = dsType.DLV;
|
createType = dsType.DLV;
|
||||||
} else if (cli.hasOption("cds")) {
|
} else if (cli.hasOption("cds")) {
|
||||||
createType = dsType.CDS;
|
createType = dsType.CDS;
|
||||||
}
|
}
|
||||||
String digestValue = cliOption("d", digestAlgOptionKeys, Integer.toString(digestId));
|
String optstr = cli.getOptionValue('d');
|
||||||
digestId = DNSSEC.Digest.value(digestValue);
|
if (optstr != null)
|
||||||
|
digestId = DNSSEC.Digest.value(optstr);
|
||||||
dsTTL = cliLongOption("ttl", dsTTLOptionKeys, dsTTL);
|
|
||||||
|
|
||||||
String[] args = cli.getArgs();
|
String[] args = cli.getArgs();
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
fail("missing key file");
|
System.err.println("error: missing key file ");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
keynames = args;
|
keyname = args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createDS(String keyname) throws IOException {
|
}
|
||||||
DSAlgorithm dsAlgorithm = DSAlgorithm.getInstance();
|
|
||||||
DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null);
|
public void execute() throws Exception {
|
||||||
|
DnsKeyPair key = BINDKeyUtils.loadKey(state.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 " + keyname + " is not an SEP-flagged key.");
|
log.warning("DNSKEY is not an SEP-flagged key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
long ttl = dsTTL < 0 ? dnskey.getTTL() : dsTTL;
|
DSRecord ds = SignUtils.calculateDSRecord(dnskey, state.digestId, dnskey.getTTL());
|
||||||
DSRecord ds = dsAlgorithm.calculateDSRecord(dnskey, digestId, ttl);
|
Record res = ds;
|
||||||
Record res;
|
|
||||||
|
|
||||||
switch (createType) {
|
if (state.createType == dsType.DLV) {
|
||||||
case DLV:
|
|
||||||
log.fine("creating DLV.");
|
log.fine("creating DLV.");
|
||||||
res = dsAlgorithm.dsToDLV(ds);
|
DLVRecord dlv = new DLVRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||||
break;
|
ds.getDigestID(), ds.getDigest());
|
||||||
case CDS:
|
res = dlv;
|
||||||
|
} else if (state.createType == dsType.CDS) {
|
||||||
log.fine("creating CDS.");
|
log.fine("creating CDS.");
|
||||||
res = dsAlgorithm.dstoCDS(ds);
|
CDSRecord cds = new CDSRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||||
break;
|
ds.getDClass(), ds.getDigest());
|
||||||
default:
|
res = cds;
|
||||||
res = ds;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputfile != null && !outputfile.equals("-")) {
|
if (state.outputfile != null && !state.outputfile.equals("-")) {
|
||||||
try (PrintWriter out = new PrintWriter(new FileWriter(outputfile))) {
|
try (PrintWriter out = new PrintWriter(new FileWriter(state.outputfile))) {
|
||||||
out.println(res);
|
out.println(res);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -134,15 +133,10 @@ public class DSTool extends CLBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute() throws Exception {
|
|
||||||
for (String keyname : keynames) {
|
|
||||||
createDS(keyname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
DSTool tool = new DSTool("dstool", "jdnssec-dstool [..options..] keyfile [keyfile..]");
|
DSTool tool = new DSTool();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ 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;
|
||||||
@@ -35,25 +37,33 @@ import com.verisignlabs.dnssec.security.JCEDnsSecSigner;
|
|||||||
* @author David Blacka
|
* @author David Blacka
|
||||||
*/
|
*/
|
||||||
public class KeyGen extends CLBase {
|
public class KeyGen extends CLBase {
|
||||||
private int algorithm = 13;
|
private CLIState state;
|
||||||
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
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
protected static class CLIState extends CLIStateBase {
|
||||||
|
public int algorithm = 13;
|
||||||
|
public int keylength = 2048;
|
||||||
|
public boolean useLargeE = true;
|
||||||
|
public String outputfile = null;
|
||||||
|
public File keydir = null;
|
||||||
|
public boolean zoneKey = true;
|
||||||
|
public boolean kskFlag = false;
|
||||||
|
public String owner = null;
|
||||||
|
public long ttl = 86400;
|
||||||
|
public int givenKeyTag = -1;
|
||||||
|
|
||||||
|
public CLIState() {
|
||||||
|
super("jdnssec-keygen [..options..] name");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the command line options.
|
* Set up the command line options.
|
||||||
*/
|
*/
|
||||||
protected void setupOptions() {
|
@Override
|
||||||
|
protected void setupOptions(Options opts) {
|
||||||
// boolean options
|
// boolean options
|
||||||
opts.addOption("k", "kskflag", false,
|
opts.addOption("k", "kskflag", false,
|
||||||
"Key is a key-signing-key (sets the SEP flag).");
|
"Key is a key-signing-key (sets the SEP flag).");
|
||||||
@@ -67,7 +77,7 @@ public class KeyGen extends CLBase {
|
|||||||
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
|
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
|
||||||
String algStringSet = String.join(" | ", algStrings);
|
String algStringSet = String.join(" | ", algStrings);
|
||||||
opts.addOption(Option.builder("a").hasArg().argName("algorithm")
|
opts.addOption(Option.builder("a").hasArg().argName("algorithm")
|
||||||
.desc(algStringSet + " | aliases, ECDSAP256SHA256 is default.").build());
|
.desc(algStringSet + " | alias, ECDSAP256SHA256 is default.").build());
|
||||||
|
|
||||||
opts.addOption(Option.builder("b").hasArg().argName("size").desc(
|
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")
|
"key size, in bits (default 2048). RSA: [512..4096], DSA: [512..1024], DH: [128..4096], ECDSA: ignored, EdDSA: ignored")
|
||||||
@@ -83,96 +93,121 @@ public class KeyGen extends CLBase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOptions() {
|
@Override
|
||||||
String[] useLargeEOptionKeys = { "use_large_exponent", "use_large_e" };
|
protected void processOptions(CommandLine cli)
|
||||||
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
|
throws org.apache.commons.cli.ParseException {
|
||||||
String[] algorithmOptionKeys = { "algorithm", "alg " };
|
String optstr = null;
|
||||||
String[] keyLengthOptionKeys = { "key_length", "keylen" };
|
String[] optstrs = null;
|
||||||
String[] ttlOptionKeys = { "dnskey_ttl", "ttl" };
|
|
||||||
|
|
||||||
if (cli.hasOption('k')) {
|
if (cli.hasOption('k'))
|
||||||
kskFlag = true;
|
kskFlag = true;
|
||||||
}
|
if (cli.hasOption('e'))
|
||||||
useLargeE = cli.hasOption('e'); // explicit command line option for the large exponent
|
useLargeE = true;
|
||||||
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');
|
outputfile = cli.getOptionValue('f');
|
||||||
|
|
||||||
String keydirName = cliOption("d", keyDirectoryOptionKeys, null);
|
if ((optstr = cli.getOptionValue('d')) != null) {
|
||||||
if (keydirName != null) {
|
keydir = new File(optstr);
|
||||||
keydir = new File(keydirName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String algString = cliOption("a", algorithmOptionKeys, Integer.toString(algorithm));
|
if ((optstr = cli.getOptionValue('n')) != null && !optstr.equalsIgnoreCase("ZONE")) {
|
||||||
algorithm = Utils.parseAlg(algString);
|
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) {
|
if (algorithm < 0) {
|
||||||
fail("DNSSEC algorithm " + algString + " is not supported");
|
System.err.println("DNSSEC algorithm " + optstr + " is not supported");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keylength = cliIntOption("b", keyLengthOptionKeys, keylength);
|
if ((optstr = cli.getOptionValue('b')) != null) {
|
||||||
ttl = cliLongOption("ttl", ttlOptionKeys, ttl);
|
keylength = parseInt(optstr, 1024);
|
||||||
givenKeyTag = Utils.parseInt(cli.getOptionValue("with-tag"), -1);
|
}
|
||||||
|
|
||||||
|
if ((optstr = cli.getOptionValue("ttl")) != null) {
|
||||||
|
ttl = parseInt(optstr, 86400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((optstr = cli.getOptionValue("with-tag")) != null) {
|
||||||
|
givenKeyTag = parseInt(optstr, -1);
|
||||||
|
}
|
||||||
|
|
||||||
String[] args = cli.getArgs();
|
String[] args = cli.getArgs();
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
fail("missing key owner name");
|
System.err.println("error: missing key owner name");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
owner = args[0];
|
owner = args[0];
|
||||||
|
}
|
||||||
|
|
||||||
log.fine("keygen options => algorithm: " + algorithm + ", keylength: " + keylength +
|
private static int parseAlg(String s) {
|
||||||
", useLargeE: " + useLargeE + ", kskFlag: " + kskFlag + ", ttl: " + ttl + ", givenKeyTag: " + givenKeyTag);
|
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||||
|
|
||||||
|
int alg = parseInt(s, -1);
|
||||||
|
if (alg > 0) {
|
||||||
|
if (algs.supportedAlgorithm(alg))
|
||||||
|
return alg;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return algs.stringToAlgorithm(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (!owner.endsWith(".")) {
|
if (!state.owner.endsWith(".")) {
|
||||||
owner = owner + ".";
|
state.owner = state.owner + ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
Name ownerName = Name.fromString(owner);
|
Name ownerName = Name.fromString(state.owner);
|
||||||
|
|
||||||
// Calculate our flags
|
// Calculate our flags
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (zoneKey) {
|
if (state.zoneKey) {
|
||||||
flags |= DNSKEYRecord.Flags.ZONE_KEY;
|
flags |= DNSKEYRecord.Flags.ZONE_KEY;
|
||||||
}
|
}
|
||||||
if (kskFlag) {
|
if (state.kskFlag) {
|
||||||
flags |= DNSKEYRecord.Flags.SEP_KEY;
|
flags |= DNSKEYRecord.Flags.SEP_KEY;
|
||||||
}
|
}
|
||||||
log.fine("create key pair with (name = " + ownerName + ", ttl = " + ttl
|
log.fine("create key pair with (name = " + ownerName + ", ttl = " + state.ttl
|
||||||
+ ", alg = " + algorithm + ", flags = " + flags + ", length = "
|
+ ", alg = " + state.algorithm + ", flags = " + flags + ", length = "
|
||||||
+ keylength + ")");
|
+ state.keylength + ")");
|
||||||
|
|
||||||
DnsKeyPair pair = signer.generateKey(ownerName, ttl, DClass.IN,
|
DnsKeyPair pair = signer.generateKey(ownerName, state.ttl, DClass.IN,
|
||||||
algorithm, flags, keylength,
|
state.algorithm, flags, state.keylength,
|
||||||
useLargeE);
|
state.useLargeE);
|
||||||
|
|
||||||
// If we were asked to generate a duplicate keytag, keep trying until we get one
|
// If we were asked to generate a duplicate keytag, keep trying until we get one
|
||||||
// This can take a long time, depending on our key generation speed
|
while (state.givenKeyTag >= 0 && pair.getDNSKEYFootprint() != state.givenKeyTag) {
|
||||||
while (givenKeyTag >= 0 && pair.getDNSKEYFootprint() != givenKeyTag) {
|
pair = signer.generateKey(ownerName, state.ttl, DClass.IN, state.algorithm, flags, state.keylength,
|
||||||
pair = signer.generateKey(ownerName, ttl, DClass.IN, algorithm, flags, keylength,
|
state.useLargeE);
|
||||||
useLargeE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputfile != null) {
|
if (state.outputfile != null) {
|
||||||
BINDKeyUtils.writeKeyFiles(outputfile, pair, keydir);
|
BINDKeyUtils.writeKeyFiles(state.outputfile, pair, state.keydir);
|
||||||
} else {
|
} else {
|
||||||
BINDKeyUtils.writeKeyFiles(pair, keydir);
|
BINDKeyUtils.writeKeyFiles(pair, state.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", "jdnssec-keygen [..options..] zonename");
|
KeyGen tool = new KeyGen();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ 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;
|
||||||
@@ -32,32 +35,41 @@ import com.verisignlabs.dnssec.security.DnsKeyPair;
|
|||||||
* @author David Blacka
|
* @author David Blacka
|
||||||
*/
|
*/
|
||||||
public class KeyInfoTool extends CLBase {
|
public class KeyInfoTool extends CLBase {
|
||||||
private String[] keynames = null;
|
private CLIState state;
|
||||||
|
|
||||||
public KeyInfoTool(String name, String usageStr) {
|
/**
|
||||||
super(name, usageStr);
|
* This is a small inner class used to hold all of the command line option
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
protected static class CLIState extends CLIStateBase {
|
||||||
|
public String[] keynames = null;
|
||||||
|
|
||||||
|
public CLIState() {
|
||||||
|
super("jdnssec-keyinfo [..options..] keyfile");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the command line options.
|
* Set up the command line options.
|
||||||
*/
|
*/
|
||||||
protected void setupOptions() {
|
@Override
|
||||||
|
protected void setupOptions(Options opts) {
|
||||||
// no special options at the moment.
|
// no special options at the moment.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void processOptions() {
|
protected void processOptions(CommandLine cli) throws ParseException {
|
||||||
keynames = cli.getArgs();
|
keynames = cli.getArgs();
|
||||||
|
|
||||||
if (keynames.length < 1) {
|
if (keynames.length < 1) {
|
||||||
fail("missing key file");
|
System.err.println("error: missing key file ");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
for (int i = 0; i < keynames.length; ++i) {
|
for (int i = 0; i < state.keynames.length; ++i) {
|
||||||
String keyname = keynames[i];
|
String keyname = state.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();
|
||||||
@@ -85,15 +97,16 @@ public class KeyInfoTool extends CLBase {
|
|||||||
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 (keynames.length - i > 1) {
|
if (state.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", "jdnssec-keyinfo [..options..] keyfile");
|
KeyInfoTool tool = new KeyInfoTool();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ 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;
|
||||||
@@ -46,24 +48,29 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
|
|||||||
* @author David Blacka
|
* @author David Blacka
|
||||||
*/
|
*/
|
||||||
public class SignKeyset extends CLBase {
|
public class SignKeyset extends CLBase {
|
||||||
private File keyDirectory = null;
|
private CLIState state;
|
||||||
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.
|
||||||
|
*/
|
||||||
|
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() {
|
||||||
|
super("jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the command line options.
|
* Set up the command line options.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
protected void setupOptions() {
|
protected void setupOptions(Options opts) {
|
||||||
// boolean options
|
// boolean options
|
||||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||||
|
|
||||||
@@ -78,46 +85,33 @@ public class SignKeyset extends CLBase {
|
|||||||
Option.builder("f").hasArg().argName("outfile").desc("file the signed keyset is written to").build());
|
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;
|
||||||
|
|
||||||
verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
|
if (cli.hasOption('a'))
|
||||||
|
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()) {
|
||||||
fail("key directory " + optstr + " is not a directory");
|
System.err.println("error: " + optstr + " is not a directory");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if ((optstr = cli.getOptionValue('s')) != null) {
|
||||||
optstr = cliOption("s", inceptionOptionKeys, null);
|
start = convertDuration(null, optstr);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if ((optstr = cli.getOptionValue('e')) != null) {
|
||||||
optstr = cliOption("e", expireOptionKeys, null);
|
expire = convertDuration(start, optstr);
|
||||||
if (optstr != null) {
|
|
||||||
expire = Utils.convertDuration(start, optstr);
|
|
||||||
} else {
|
} else {
|
||||||
expire = Utils.convertDuration(start, "+2592000"); // 30 days
|
expire = 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');
|
||||||
@@ -125,7 +119,8 @@ public class SignKeyset extends CLBase {
|
|||||||
String[] files = cli.getArgs();
|
String[] files = cli.getArgs();
|
||||||
|
|
||||||
if (files.length < 1) {
|
if (files.length < 1) {
|
||||||
fail("missing zone file and/or key files");
|
System.err.println("error: missing zone file and/or key files");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
inputfile = files[0];
|
inputfile = files[0];
|
||||||
@@ -134,6 +129,7 @@ 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.
|
||||||
@@ -142,7 +138,7 @@ public class SignKeyset extends CLBase {
|
|||||||
* @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 boolean verifySigs(List<Record> records,
|
private static boolean verifySigs(List<Record> records,
|
||||||
List<DnsKeyPair> keypairs) {
|
List<DnsKeyPair> keypairs) {
|
||||||
boolean secure = true;
|
boolean secure = true;
|
||||||
|
|
||||||
@@ -164,7 +160,7 @@ public class SignKeyset extends CLBase {
|
|||||||
boolean result = verifier.verify(rrset);
|
boolean result = verifier.verify(rrset);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
staticLog.fine("Signatures did not verify for RRset: " + rrset);
|
||||||
secure = false;
|
secure = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +179,7 @@ public class SignKeyset extends CLBase {
|
|||||||
* @param inDirectory the directory to look in (may be null).
|
* @param inDirectory the directory to look in (may be null).
|
||||||
* @return a list of keypair objects.
|
* @return a list of keypair objects.
|
||||||
*/
|
*/
|
||||||
private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
private static 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<>();
|
||||||
@@ -240,14 +236,10 @@ 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 = null;
|
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null);
|
||||||
try {
|
|
||||||
records = ZoneUtils.readZoneFile(inputfile, null);
|
|
||||||
} catch (java.io.IOException e) {
|
|
||||||
fail("Unable to read input file: " + e.getMessage());
|
|
||||||
}
|
|
||||||
if (records == null || records.isEmpty()) {
|
if (records == null || records.isEmpty()) {
|
||||||
fail("empty keyset file");
|
System.err.println("error: 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.
|
||||||
@@ -263,42 +255,46 @@ public class SignKeyset extends CLBase {
|
|||||||
keysetName = r.getName();
|
keysetName = r.getName();
|
||||||
}
|
}
|
||||||
if (!r.getName().equals(keysetName)) {
|
if (!r.getName().equals(keysetName)) {
|
||||||
fail("DNSKEY with a different name found!");
|
System.err.println("error: DNSKEY with a different name found!");
|
||||||
|
state.usage();
|
||||||
}
|
}
|
||||||
keyset.addRR(r);
|
keyset.addRR(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyset.size() == 0) {
|
if (keyset.size() == 0) {
|
||||||
fail("error: No DNSKEYs found in keyset file");
|
System.err.println("error: No DNSKEYs found in keyset file");
|
||||||
|
state.usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the key pairs.
|
// Load the key pairs.
|
||||||
List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, keyDirectory);
|
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.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(keyDirectory, keysetName);
|
keypairs = findZoneKeys(state.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) {
|
||||||
fail("no signing keys could be determined.");
|
System.err.println("error: No signing keys could be determined.");
|
||||||
|
state.usage();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// default the output file, if not set.
|
// default the output file, if not set.
|
||||||
if (outputfile == null) {
|
if (state.outputfile == null) {
|
||||||
if (keysetName.isAbsolute()) {
|
if (keysetName.isAbsolute()) {
|
||||||
outputfile = keysetName + "signed_keyset";
|
state.outputfile = keysetName + "signed_keyset";
|
||||||
} else {
|
} else {
|
||||||
outputfile = keysetName + ".signed_keyset";
|
state.outputfile = keysetName + ".signed_keyset";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||||
|
|
||||||
List<RRSIGRecord> sigs = signer.signRRset(keyset, keypairs, start, expire);
|
List<RRSIGRecord> sigs = signer.signRRset(keyset, keypairs, state.start, state.expire);
|
||||||
for (RRSIGRecord s : sigs) {
|
for (RRSIGRecord s : sigs) {
|
||||||
keyset.addRR(s);
|
keyset.addRR(s);
|
||||||
}
|
}
|
||||||
@@ -313,9 +309,9 @@ public class SignKeyset extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write out the signed zone
|
// write out the signed zone
|
||||||
ZoneUtils.writeZoneFile(signedRecords, outputfile);
|
ZoneUtils.writeZoneFile(signedRecords, state.outputfile);
|
||||||
|
|
||||||
if (verifySigs) {
|
if (state.verifySigs) {
|
||||||
log.fine("verifying generated signatures");
|
log.fine("verifying generated signatures");
|
||||||
boolean res = verifySigs(signedRecords, keypairs);
|
boolean res = verifySigs(signedRecords, keypairs);
|
||||||
|
|
||||||
@@ -329,8 +325,9 @@ public class SignKeyset extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SignKeyset tool = new SignKeyset("signkeyset", "jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]");
|
SignKeyset tool = new SignKeyset();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ 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;
|
||||||
@@ -47,26 +49,30 @@ 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an inner class used to hold all of the command line option state.
|
||||||
|
*/
|
||||||
|
protected static class CLIState extends CLIStateBase {
|
||||||
private File keyDirectory = null;
|
private File keyDirectory = null;
|
||||||
private String[] keyFiles = null;
|
public String[] keyFiles = null;
|
||||||
private Instant start = null;
|
public Instant start = null;
|
||||||
private Instant expire = null;
|
public Instant expire = null;
|
||||||
private String inputfile = null;
|
public String inputfile = null;
|
||||||
private String outputfile = null;
|
public String outputfile = null;
|
||||||
private boolean verifySigs = false;
|
public boolean verifySigs = false;
|
||||||
private boolean verboseSigning = false;
|
public boolean verboseSigning = false;
|
||||||
|
|
||||||
|
public CLIState() {
|
||||||
public SignRRset(String name, String usageStr) {
|
super("jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]");
|
||||||
super(name, usageStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the command line options.
|
* Set up the command line options.
|
||||||
*/
|
*/
|
||||||
protected void setupOptions() {
|
@Override
|
||||||
|
protected void setupOptions(Options opts) {
|
||||||
// boolean options
|
// boolean options
|
||||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||||
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
|
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
|
||||||
@@ -81,48 +87,34 @@ public class SignRRset extends CLBase {
|
|||||||
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
|
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOptions() {
|
@Override
|
||||||
String[] verifyOptionKeys = { "verify_signatures", "verify" };
|
protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException {
|
||||||
String[] verboseSigningOptionKeys = { "verbose_signing" };
|
|
||||||
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
|
|
||||||
String[] inceptionOptionKeys = { "inception", "start" };
|
|
||||||
String[] expireOptionKeys = { "expire" };
|
|
||||||
|
|
||||||
String optstr = null;
|
String optstr = null;
|
||||||
|
|
||||||
verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
|
if (cli.hasOption('a'))
|
||||||
|
verifySigs = true;
|
||||||
|
if (cli.hasOption('V'))
|
||||||
|
verboseSigning = true;
|
||||||
|
|
||||||
verboseSigning = cliBooleanOption("V", verboseSigningOptionKeys, false);
|
if ((optstr = cli.getOptionValue('D')) != null) {
|
||||||
|
|
||||||
optstr = cliOption("D", keyDirectoryOptionKeys, null);
|
|
||||||
if (optstr != null) {
|
|
||||||
keyDirectory = new File(optstr);
|
keyDirectory = new File(optstr);
|
||||||
if (!keyDirectory.isDirectory()) {
|
if (!keyDirectory.isDirectory()) {
|
||||||
fail("key directory " + optstr + " is not a directory");
|
System.err.println("error: " + optstr + " is not a directory");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if ((optstr = cli.getOptionValue('s')) != null) {
|
||||||
optstr = cliOption("s", inceptionOptionKeys, null);
|
start = convertDuration(null, optstr);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if ((optstr = cli.getOptionValue('e')) != null) {
|
||||||
optstr = cliOption("e", expireOptionKeys, null);
|
expire = convertDuration(start, optstr);
|
||||||
if (optstr != null) {
|
|
||||||
expire = Utils.convertDuration(start, optstr);
|
|
||||||
} else {
|
} else {
|
||||||
expire = Utils.convertDuration(start, "+2592000"); // 30 days
|
expire = 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');
|
||||||
@@ -130,7 +122,8 @@ public class SignRRset extends CLBase {
|
|||||||
String[] files = cli.getArgs();
|
String[] files = cli.getArgs();
|
||||||
|
|
||||||
if (files.length < 1) {
|
if (files.length < 1) {
|
||||||
fail("missing zone file and/or key files");
|
System.err.println("error: missing zone file and/or key files");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
inputfile = files[0];
|
inputfile = files[0];
|
||||||
@@ -139,7 +132,7 @@ public class SignRRset 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.
|
||||||
@@ -148,7 +141,7 @@ public class SignRRset extends CLBase {
|
|||||||
* @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 boolean verifySigs(List<Record> records, List<DnsKeyPair> keypairs) {
|
private static boolean verifySigs(List<Record> records, List<DnsKeyPair> keypairs) {
|
||||||
boolean secure = true;
|
boolean secure = true;
|
||||||
|
|
||||||
DnsSecVerifier verifier = new DnsSecVerifier();
|
DnsSecVerifier verifier = new DnsSecVerifier();
|
||||||
@@ -170,7 +163,7 @@ public class SignRRset extends CLBase {
|
|||||||
boolean result = verifier.verify(rrset);
|
boolean result = verifier.verify(rrset);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
staticLog.fine("Signatures did not verify for RRset: " + rrset);
|
||||||
secure = false;
|
secure = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +182,7 @@ public class SignRRset extends CLBase {
|
|||||||
* @param inDirectory the directory to look in (may be null).
|
* @param inDirectory the directory to look in (may be null).
|
||||||
* @return a list of keypair objects.
|
* @return a list of keypair objects.
|
||||||
*/
|
*/
|
||||||
private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
private static 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<>();
|
||||||
@@ -211,14 +204,10 @@ 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 = null;
|
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null);
|
||||||
try {
|
|
||||||
records = ZoneUtils.readZoneFile(inputfile, null);
|
|
||||||
} catch (java.io.IOException e) {
|
|
||||||
fail("Unable to read input file: " + e.getMessage());
|
|
||||||
}
|
|
||||||
if (records == null || records.isEmpty()) {
|
if (records == null || records.isEmpty()) {
|
||||||
fail("empty RRset file");
|
System.err.println("error: 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.
|
||||||
@@ -241,21 +230,25 @@ public class SignRRset extends CLBase {
|
|||||||
&& rrset.getDClass() == r.getDClass()) {
|
&& rrset.getDClass() == r.getDClass()) {
|
||||||
rrset.addRR(r);
|
rrset.addRR(r);
|
||||||
} else {
|
} else {
|
||||||
fail("records do not all belong to the same RRset");
|
System.err.println("Records do not all belong to the same RRset.");
|
||||||
|
state.usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rrset == null || rrset.size() == 0) {
|
if (rrset == null || rrset.size() == 0) {
|
||||||
fail("no records found in inputfile");
|
System.err.println("No records found in inputfile.");
|
||||||
|
state.usage();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the key pairs.
|
// Load the key pairs.
|
||||||
|
|
||||||
if (keyFiles.length == 0) {
|
if (state.keyFiles.length == 0) {
|
||||||
fail("at least one keyfile must be specified");
|
System.err.println("error: at least one keyfile must be specified");
|
||||||
|
state.usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, keyDirectory);
|
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.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.
|
||||||
@@ -267,18 +260,19 @@ public class SignRRset extends CLBase {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!pair.getDNSKEYName().equals(keysetName)) {
|
if (!pair.getDNSKEYName().equals(keysetName)) {
|
||||||
fail("keys do not all have the same name");
|
System.err.println("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 (outputfile == null && !inputfile.equals("-")) {
|
if (state.outputfile == null && !state.inputfile.equals("-")) {
|
||||||
outputfile = inputfile + ".signed";
|
state.outputfile = state.inputfile + ".signed";
|
||||||
}
|
}
|
||||||
|
|
||||||
JCEDnsSecSigner signer = new JCEDnsSecSigner(verboseSigning);
|
JCEDnsSecSigner signer = new JCEDnsSecSigner(state.verboseSigning);
|
||||||
|
|
||||||
List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, start, expire);
|
List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, state.start, state.expire);
|
||||||
for (RRSIGRecord s : sigs) {
|
for (RRSIGRecord s : sigs) {
|
||||||
rrset.addRR(s);
|
rrset.addRR(s);
|
||||||
}
|
}
|
||||||
@@ -293,9 +287,9 @@ public class SignRRset extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write out the signed zone
|
// write out the signed zone
|
||||||
ZoneUtils.writeZoneFile(signedRecords, outputfile);
|
ZoneUtils.writeZoneFile(signedRecords, state.outputfile);
|
||||||
|
|
||||||
if (verifySigs) {
|
if (state.verifySigs) {
|
||||||
log.fine("verifying generated signatures");
|
log.fine("verifying generated signatures");
|
||||||
boolean res = verifySigs(signedRecords, keypairs);
|
boolean res = verifySigs(signedRecords, keypairs);
|
||||||
|
|
||||||
@@ -309,8 +303,9 @@ public class SignRRset extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SignRRset tool = new SignRRset("signrrset", "jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]");
|
SignRRset tool = new SignRRset();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ 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;
|
||||||
@@ -51,32 +54,39 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
|
|||||||
* @author David Blacka
|
* @author David Blacka
|
||||||
*/
|
*/
|
||||||
public class SignZone extends CLBase {
|
public class SignZone extends CLBase {
|
||||||
private File keyDirectory = null;
|
private CLIState state;
|
||||||
private File keysetDirectory = null;
|
|
||||||
private String[] kskFiles = null;
|
/**
|
||||||
private String[] keyFiles = null;
|
* This is an inner class used to hold all of the command line option state.
|
||||||
private String zonefile = null;
|
*/
|
||||||
private Instant start = null;
|
private static class CLIState extends CLIStateBase {
|
||||||
private Instant expire = null;
|
public File keyDirectory = null;
|
||||||
private String outputfile = null;
|
public File keysetDirectory = null;
|
||||||
private boolean verifySigs = false;
|
public String[] kskFiles = null;
|
||||||
private boolean useOptOut = false;
|
public String[] keyFiles = null;
|
||||||
private boolean fullySignKeyset = false;
|
public String zonefile = null;
|
||||||
private List<Name> includeNames = null;
|
public Instant start = null;
|
||||||
private boolean useNsec3 = false;
|
public Instant expire = null;
|
||||||
private byte[] salt = {};
|
public String outputfile = null;
|
||||||
private int iterations = 0;
|
public boolean verifySigs = false;
|
||||||
private int digestId = DNSSEC.Digest.SHA256;
|
public boolean useOptOut = false;
|
||||||
private long nsec3paramttl = -1;
|
public boolean fullySignKeyset = false;
|
||||||
private boolean verboseSigning = 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;
|
||||||
|
|
||||||
private static final Random rand = new Random();
|
private static final Random rand = new Random();
|
||||||
|
|
||||||
public SignZone(String name, String usageStr) {
|
public CLIState() {
|
||||||
super(name, usageStr);
|
super("jdnssec-signzone [..options..] zone_file [key_file ...]");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupOptions() {
|
@Override
|
||||||
|
protected void setupOptions(Options opts) {
|
||||||
// boolean options
|
// boolean options
|
||||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||||
opts.addOption("F", "fully-sign-keyset", false,
|
opts.addOption("F", "fully-sign-keyset", false,
|
||||||
@@ -112,115 +122,114 @@ public class SignZone extends CLBase {
|
|||||||
.desc("Use this TTL for the NSEC3PARAM record (default is min(soa.min, soa.ttl))").build());
|
.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")
|
opts.addOption(Option.builder().hasArg().argName("id").longOpt("ds-digest")
|
||||||
.desc("Digest algorithm to use for generated DS records").build());
|
.desc("Digest algorithm to use for generated DS records").build());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOptions() {
|
@Override
|
||||||
String[] verifyOptionKeys = { "verify_signatures", "verify" };
|
protected void processOptions(CommandLine cli) throws ParseException {
|
||||||
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;
|
String optstr = null;
|
||||||
|
|
||||||
verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
|
if (cli.hasOption('a'))
|
||||||
useNsec3 = cliBooleanOption("3", nsec3OptionKeys, false);
|
verifySigs = true;
|
||||||
useOptOut = cliBooleanOption("O", optOutOptionKeys, false);
|
if (cli.hasOption('3'))
|
||||||
verboseSigning = cliBooleanOption("V", verboseSigningOptionKeys, false);
|
useNsec3 = true;
|
||||||
|
if (cli.hasOption('O'))
|
||||||
|
useOptOut = true;
|
||||||
|
if (cli.hasOption('V'))
|
||||||
|
verboseSigning = true;
|
||||||
|
|
||||||
if (useOptOut && !useNsec3) {
|
if (useOptOut && !useNsec3) {
|
||||||
System.err.println("Opt-Out not supported without NSEC3 -- ignored.");
|
System.err.println("Opt-Out not supported without NSEC3 -- ignored.");
|
||||||
useOptOut = false;
|
useOptOut = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullySignKeyset = cliBooleanOption("F", fullySignKeysetOptionKeys, false);
|
if (cli.hasOption('F'))
|
||||||
|
fullySignKeyset = true;
|
||||||
|
|
||||||
optstr = cliOption("D", keyDirectoryOptionKeys, null);
|
if ((optstr = cli.getOptionValue('d')) != null) {
|
||||||
if (optstr != 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);
|
keyDirectory = new File(optstr);
|
||||||
if (!keyDirectory.isDirectory()) {
|
if (!keyDirectory.isDirectory()) {
|
||||||
fail("key directory " + optstr + " is not a directory");
|
System.err.println("error: " + optstr + " is not a directory");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if ((optstr = cli.getOptionValue('s')) != null) {
|
||||||
optstr = cliOption("s", inceptionOptionKeys, null);
|
start = CLBase.convertDuration(null, optstr);
|
||||||
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.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if ((optstr = cli.getOptionValue('e')) != null) {
|
||||||
optstr = cliOption("e", expireOptionKeys, null);
|
expire = CLBase.convertDuration(start, optstr);
|
||||||
if (optstr != null) {
|
|
||||||
expire = Utils.convertDuration(start, optstr);
|
|
||||||
} else {
|
} else {
|
||||||
expire = Utils.convertDuration(start, "+2592000"); // 30 days
|
expire = CLBase.convertDuration(start, "+2592000"); // 30 days
|
||||||
}
|
|
||||||
} catch (java.text.ParseException e) {
|
|
||||||
fail("unable to parse end time specficiation: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outputfile = cli.getOptionValue('f');
|
outputfile = cli.getOptionValue('f');
|
||||||
|
|
||||||
kskFiles = cli.getOptionValues('k');
|
kskFiles = cli.getOptionValues('k');
|
||||||
|
|
||||||
optstr = cliOption("S", nsec3SaltOptionKeys, null);
|
if ((optstr = cli.getOptionValue('I')) != null) {
|
||||||
if (optstr != null) {
|
File includeNamesFile = new File(optstr);
|
||||||
salt = base16.fromString(optstr);
|
try {
|
||||||
if (salt == null && !optstr.equals("-")) {
|
includeNames = CLIState.getNameList(includeNamesFile);
|
||||||
fail("salt is not valid hexidecimal");
|
} catch (IOException e) {
|
||||||
|
throw new ParseException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optstr = cliOption("R", randomSaltOptionKeys, null);
|
if ((optstr = cli.getOptionValue('S')) != null) {
|
||||||
if (optstr != null) {
|
salt = base16.fromString(optstr);
|
||||||
int length = Utils.parseInt(optstr, 0);
|
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) {
|
if (length > 0 && length <= 255) {
|
||||||
salt = new byte[length];
|
salt = new byte[length];
|
||||||
rand.nextBytes(salt);
|
rand.nextBytes(salt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iterations = cliIntOption("iterations", nsec3IterationsOptionKeys, 0);
|
if ((optstr = cli.getOptionValue("iterations")) != null) {
|
||||||
if (iterations > 150) {
|
iterations = parseInt(optstr, iterations);
|
||||||
log.warning("NSEC3 iterations value is too high for normal use: " + iterations
|
if (iterations < 0 || iterations > 8388607) {
|
||||||
+ " is greater than current accepted threshold of 150");
|
System.err.println("error: iterations value is invalid");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optstr = cliOption("ds-digest", digestAlgOptionKeys, Integer.toString(digestId));
|
if ((optstr = cli.getOptionValue("ds-digest")) != null) {
|
||||||
digestId = DNSSEC.Digest.value(optstr);
|
digestId = parseInt(optstr, -1);
|
||||||
|
if (digestId < 0) {
|
||||||
nsec3paramttl = cliIntOption("nsec3paramttl", nsec3paramTTLOptionKeys, -1);
|
System.err.println("error: DS digest ID is not a valid identifier");
|
||||||
|
usage();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((optstr = cli.getOptionValue("nsec3paramttl")) != null) {
|
||||||
|
nsec3paramttl = parseInt(optstr, -1);
|
||||||
|
}
|
||||||
|
|
||||||
String[] files = cli.getArgs();
|
String[] files = cli.getArgs();
|
||||||
|
|
||||||
if (files.length < 1) {
|
if (files.length < 1) {
|
||||||
fail("missing zone file and/or key files");
|
System.err.println("error: missing zone file and/or key files");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
zonefile = files[0];
|
zonefile = files[0];
|
||||||
@@ -228,20 +237,6 @@ public class SignZone extends CLBase {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -251,7 +246,7 @@ public class SignZone extends CLBase {
|
|||||||
* names.
|
* names.
|
||||||
* @return a list of {@link org.xbill.DNS.Name} objects.
|
* @return a list of {@link org.xbill.DNS.Name} objects.
|
||||||
*/
|
*/
|
||||||
private List<Name> getNameList(File nameListFile) throws IOException {
|
private static List<Name> getNameList(File nameListFile) throws IOException {
|
||||||
try (BufferedReader br = new BufferedReader(new FileReader(nameListFile))) {
|
try (BufferedReader br = new BufferedReader(new FileReader(nameListFile))) {
|
||||||
List<Name> res = new ArrayList<>();
|
List<Name> res = new ArrayList<>();
|
||||||
|
|
||||||
@@ -265,13 +260,14 @@ public class SignZone extends CLBase {
|
|||||||
|
|
||||||
res.add(n);
|
res.add(n);
|
||||||
} catch (TextParseException e) {
|
} catch (TextParseException e) {
|
||||||
log.severe("DNS Name parsing error:" + e);
|
staticLog.severe("DNS Name parsing error:" + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the generated signatures.
|
* Verify the generated signatures.
|
||||||
@@ -280,7 +276,7 @@ public class SignZone extends CLBase {
|
|||||||
* @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 boolean verifyZoneSigs(List<Record> records,
|
private static boolean verifyZoneSigs(List<Record> records,
|
||||||
List<DnsKeyPair> keypairs, List<DnsKeyPair> kskpairs) {
|
List<DnsKeyPair> keypairs, List<DnsKeyPair> kskpairs) {
|
||||||
boolean secure = true;
|
boolean secure = true;
|
||||||
|
|
||||||
@@ -306,7 +302,7 @@ public class SignZone extends CLBase {
|
|||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
System.err.println("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);
|
staticLog.fine("Signatures did not verify for RRset: " + rrset);
|
||||||
secure = false;
|
secure = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +321,7 @@ public class SignZone extends CLBase {
|
|||||||
* @param inDirectory the directory to look in (may be null).
|
* @param inDirectory the directory to look in (may be null).
|
||||||
* @return a list of keypair objects.
|
* @return a list of keypair objects.
|
||||||
*/
|
*/
|
||||||
private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
private static 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<>();
|
||||||
@@ -346,7 +342,7 @@ public class SignZone extends CLBase {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DnsKeyPair> getKeys(List<Record> dnskeyrrs, File inDirectory)
|
private static 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) {
|
||||||
@@ -380,7 +376,7 @@ public class SignZone extends CLBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename)
|
private static List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (inDirectory == null) {
|
if (inDirectory == null) {
|
||||||
inDirectory = new File(".");
|
inDirectory = new File(".");
|
||||||
@@ -423,7 +419,7 @@ public class SignZone extends CLBase {
|
|||||||
* keysets that do not belong in the zone.
|
* keysets that do not belong in the zone.
|
||||||
* @return a list of {@link org.xbill.DNS.Record}s found in the keyset files.
|
* @return a list of {@link org.xbill.DNS.Record}s found in the keyset files.
|
||||||
*/
|
*/
|
||||||
private List<Record> getKeysets(File inDirectory, Name zonename)
|
private static List<Record> getKeysets(File inDirectory, Name zonename)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (inDirectory == null) {
|
if (inDirectory == null) {
|
||||||
inDirectory = new File(".");
|
inDirectory = new File(".");
|
||||||
@@ -474,42 +470,37 @@ 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(zonefile, null);
|
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null);
|
||||||
if (records == null || records.isEmpty()) {
|
if (records == null || records.isEmpty()) {
|
||||||
fail("empty zone file");
|
System.err.println("error: 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) {
|
||||||
fail("invalid zone file - no SOA");
|
System.err.println("error: invalid zone file - no SOA");
|
||||||
|
state.usage();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the key pairs. Note that getKeys() always returns an ArrayList,
|
// Load the key pairs. Note that getKeys() always returns array.
|
||||||
// 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.isEmpty()) {
|
if (keypairs.isEmpty()) {
|
||||||
List<Record> dnskeys = ZoneUtils.findRRs(records, zonename, Type.DNSKEY);
|
List<Record> dnskeys = ZoneUtils.findRRs(records, zonename, Type.DNSKEY);
|
||||||
keypairs = getKeys(dnskeys, keyDirectory);
|
keypairs = getKeys(dnskeys, state.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.isEmpty()) {
|
if (keypairs.isEmpty()) {
|
||||||
keypairs = findZoneKeys(keyDirectory, zonename);
|
keypairs = findZoneKeys(state.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
|
||||||
@@ -529,7 +520,9 @@ public class SignZone extends CLBase {
|
|||||||
|
|
||||||
// If we have zero keypairs at all, we are stuck.
|
// If we have zero keypairs at all, we are stuck.
|
||||||
if (keypairs.isEmpty() && kskpairs.isEmpty()) {
|
if (keypairs.isEmpty() && kskpairs.isEmpty()) {
|
||||||
fail("no zone signing keys could be determined");
|
System.err.println("No zone signing keys could be determined.");
|
||||||
|
state.usage();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we only have one type of key (all ZSKs or all KSKs), then these are
|
// If we only have one type of key (all ZSKs or all KSKs), then these are
|
||||||
@@ -558,18 +551,19 @@ public class SignZone extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default the output file, if not set.
|
// default the output file, if not set.
|
||||||
if (outputfile == null && !zonefile.equals("-")) {
|
if (state.outputfile == null && !state.zonefile.equals("-")) {
|
||||||
if (zonename.isAbsolute()) {
|
if (zonename.isAbsolute()) {
|
||||||
outputfile = zonename + "signed";
|
state.outputfile = zonename + "signed";
|
||||||
} else {
|
} else {
|
||||||
outputfile = zonename + ".signed";
|
state.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)) {
|
||||||
fail("specified keypairs are not valid for the zone.");
|
System.err.println("error: 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
|
||||||
@@ -587,34 +581,34 @@ public class SignZone extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read in the keysets, if any.
|
// read in the keysets, if any.
|
||||||
List<Record> keysetrecs = getKeysets(keysetDirectory, zonename);
|
List<Record> keysetrecs = getKeysets(state.keysetDirectory, zonename);
|
||||||
if (keysetrecs != null) {
|
if (keysetrecs != null) {
|
||||||
records.addAll(keysetrecs);
|
records.addAll(keysetrecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
JCEDnsSecSigner signer = new JCEDnsSecSigner(verboseSigning);
|
JCEDnsSecSigner signer = new JCEDnsSecSigner(state.verboseSigning);
|
||||||
|
|
||||||
// Sign the zone.
|
// Sign the zone.
|
||||||
List<Record> signedRecords;
|
List<Record> signedRecords;
|
||||||
|
|
||||||
if (useNsec3) {
|
if (state.useNsec3) {
|
||||||
signedRecords = signer.signZoneNSEC3(zonename, records, kskpairs, keypairs,
|
signedRecords = signer.signZoneNSEC3(zonename, records, kskpairs, keypairs,
|
||||||
start, expire,
|
state.start, state.expire,
|
||||||
fullySignKeyset, useOptOut,
|
state.fullySignKeyset, state.useOptOut,
|
||||||
includeNames, salt,
|
state.includeNames, state.salt,
|
||||||
iterations, digestId,
|
state.iterations, state.digestId,
|
||||||
nsec3paramttl);
|
state.nsec3paramttl);
|
||||||
} else {
|
} else {
|
||||||
signedRecords = signer.signZone(zonename, records, kskpairs, keypairs,
|
signedRecords = signer.signZone(zonename, records, kskpairs, keypairs,
|
||||||
start, expire, fullySignKeyset,
|
state.start, state.expire, state.fullySignKeyset,
|
||||||
digestId);
|
state.digestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write out the signed zone
|
// write out the signed zone
|
||||||
ZoneUtils.writeZoneFile(signedRecords, outputfile);
|
ZoneUtils.writeZoneFile(signedRecords, state.outputfile);
|
||||||
System.out.println("zone signing complete");
|
System.out.println("zone signing complete");
|
||||||
|
|
||||||
if (verifySigs) {
|
if (state.verifySigs) {
|
||||||
log.fine("verifying generated signatures");
|
log.fine("verifying generated signatures");
|
||||||
boolean res = verifyZoneSigs(signedRecords, keypairs, kskpairs);
|
boolean res = verifyZoneSigs(signedRecords, keypairs, kskpairs);
|
||||||
|
|
||||||
@@ -627,8 +621,9 @@ public class SignZone extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SignZone tool = new SignZone("signzone", "jdnssec-signzone [..options..] zone_file [key_file ...]");
|
SignZone tool = new SignZone();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,10 @@ 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;
|
||||||
@@ -32,19 +35,28 @@ 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;
|
|
||||||
|
|
||||||
public VerifyZone(String name, String usageStr) {
|
private CLIState state;
|
||||||
super(name, usageStr);
|
|
||||||
|
/**
|
||||||
|
* This is a small inner class used to hold all of the command line option
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
protected static class CLIState extends CLIStateBase {
|
||||||
|
public String zonefile = null;
|
||||||
|
public String[] keyfiles = null;
|
||||||
|
public int startfudge = 0;
|
||||||
|
public int expirefudge = 0;
|
||||||
|
public boolean ignoreTime = false;
|
||||||
|
public boolean ignoreDups = false;
|
||||||
|
public Instant currentTime = null;
|
||||||
|
|
||||||
|
public CLIState() {
|
||||||
|
super("jdnssec-verifyzone [..options..] zonefile");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupOptions() {
|
@Override
|
||||||
|
protected void setupOptions(Options opts) {
|
||||||
opts.addOption(Option.builder("S").hasArg().argName("seconds").longOpt("sig-start-fudge")
|
opts.addOption(Option.builder("S").hasArg().argName("seconds").longOpt("sig-start-fudge")
|
||||||
.desc("'fudge' RRSIG inception ties by 'seconds'").build());
|
.desc("'fudge' RRSIG inception ties by 'seconds'").build());
|
||||||
opts.addOption(Option.builder("E").hasArg().argName("seconds").longOpt("sig-expire-fudge")
|
opts.addOption(Option.builder("E").hasArg().argName("seconds").longOpt("sig-expire-fudge")
|
||||||
@@ -57,31 +69,46 @@ public class VerifyZone extends CLBase {
|
|||||||
opts.addOption(Option.builder().longOpt("ignore-duplicate-rrs").desc("Ignore duplicate record errors.").build());
|
opts.addOption(Option.builder().longOpt("ignore-duplicate-rrs").desc("Ignore duplicate record errors.").build());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOptions() {
|
@Override
|
||||||
String[] ignoreTimeOptionKeys = { "ignore_time" };
|
protected void processOptions(CommandLine cli) {
|
||||||
String[] ignoreDuplicateOptionKeys = { "ingore_duplicate_rrs", "ignore_duplicates" };
|
if (cli.hasOption("ignore-time")) {
|
||||||
String[] startFudgeOptionKeys = { "start_fudge" };
|
ignoreTime = true;
|
||||||
String[] expireFudgeOptionKeys = { "expire_fudge" };
|
}
|
||||||
String[] currentTimeOptionKeys = { "current_time" };
|
|
||||||
|
|
||||||
ignoreTime = cliBooleanOption("ignore-time", ignoreTimeOptionKeys, false);
|
if (cli.hasOption("ignore-duplicate-rrs")) {
|
||||||
ignoreDups = cliBooleanOption("ignore-duplicate-rrs", ignoreDuplicateOptionKeys, false);
|
ignoreDups = true;
|
||||||
startfudge = cliIntOption("S", startFudgeOptionKeys, 0);
|
}
|
||||||
expirefudge = cliIntOption("E", expireFudgeOptionKeys, 0);
|
|
||||||
|
|
||||||
String optstr = cliOption("t", currentTimeOptionKeys, null);
|
String optstr = null;
|
||||||
if (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 {
|
try {
|
||||||
currentTime = Utils.convertDuration(null, optstr);
|
currentTime = convertDuration(null, optstr);
|
||||||
} catch (java.text.ParseException e) {
|
} catch (ParseException e) {
|
||||||
fail("could not parse timespec");
|
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();
|
String[] args = cli.getArgs();
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
fail("missing zone file");
|
System.err.println("error: missing zone file");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
zonefile = args[0];
|
zonefile = args[0];
|
||||||
@@ -91,24 +118,17 @@ public class VerifyZone extends CLBase {
|
|||||||
System.arraycopy(args, 1, keyfiles, 0, keyfiles.length);
|
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(startfudge);
|
zoneverifier.getVerifier().setStartFudge(state.startfudge);
|
||||||
zoneverifier.getVerifier().setExpireFudge(expirefudge);
|
zoneverifier.getVerifier().setExpireFudge(state.expirefudge);
|
||||||
zoneverifier.getVerifier().setIgnoreTime(ignoreTime);
|
zoneverifier.getVerifier().setIgnoreTime(state.ignoreTime);
|
||||||
zoneverifier.getVerifier().setCurrentTime(currentTime);
|
zoneverifier.getVerifier().setCurrentTime(state.currentTime);
|
||||||
zoneverifier.setIgnoreDuplicateRRs(ignoreDups);
|
zoneverifier.setIgnoreDuplicateRRs(state.ignoreDups);
|
||||||
|
|
||||||
List<Record> records = null;
|
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null);
|
||||||
try {
|
|
||||||
records = ZoneUtils.readZoneFile(zonefile, null);
|
|
||||||
} catch (java.io.IOException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
if (records == null) {
|
|
||||||
fail("Unable to read a zone file");
|
|
||||||
}
|
|
||||||
|
|
||||||
log.fine("verifying zone...");
|
log.fine("verifying zone...");
|
||||||
int errors = zoneverifier.verifyZone(records);
|
int errors = zoneverifier.verifyZone(records);
|
||||||
@@ -124,8 +144,9 @@ public class VerifyZone extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
VerifyZone tool = new VerifyZone("verifyzone", "jdnssec-verifyzone [..options..] zonefile");
|
VerifyZone tool = new VerifyZone();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ 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;
|
||||||
@@ -42,33 +45,43 @@ import com.verisignlabs.dnssec.security.RecordComparator;
|
|||||||
* @author David Blacka
|
* @author David Blacka
|
||||||
*/
|
*/
|
||||||
public class ZoneFormat extends CLBase {
|
public class ZoneFormat extends CLBase {
|
||||||
private String file;
|
private CLIState state;
|
||||||
private boolean assignNSEC3;
|
|
||||||
|
|
||||||
public ZoneFormat(String name, String usageStr) {
|
/**
|
||||||
super(name, usageStr);
|
* This is a small inner class used to hold all of the command line option
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
protected static class CLIState extends CLIStateBase {
|
||||||
|
public String file;
|
||||||
|
public boolean assignNSEC3;
|
||||||
|
|
||||||
|
public CLIState() {
|
||||||
|
super("jdnssec-zoneformat [..options..] zonefile");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupOptions() {
|
@Override
|
||||||
|
protected void setupOptions(Options opts) {
|
||||||
opts.addOption("N", "nsec3", false,
|
opts.addOption("N", "nsec3", false,
|
||||||
"attempt to determine the original ownernames for NSEC3 RRs.");
|
"attempt to determine the original ownernames for NSEC3 RRs.");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOptions() {
|
@Override
|
||||||
String[] assignNsec3OwnersOptionKeys = { "assign_nsec3_owners", "assign_owners" };
|
protected void processOptions(CommandLine cli) throws ParseException {
|
||||||
|
if (cli.hasOption('N'))
|
||||||
assignNSEC3 = cliBooleanOption("N", assignNsec3OwnersOptionKeys, false);
|
assignNSEC3 = true;
|
||||||
|
|
||||||
String[] args = cli.getArgs();
|
String[] args = cli.getArgs();
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
fail("must specify a zone file");
|
System.err.println("error: must specify a zone file");
|
||||||
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
file = args[0];
|
file = args[0];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<Record> readZoneFile(String filename) throws IOException {
|
private static 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;
|
||||||
@@ -85,14 +98,14 @@ public class ZoneFormat extends CLBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatZone(List<Record> zone) {
|
private static void formatZone(List<Record> zone) {
|
||||||
|
|
||||||
for (Record r : zone) {
|
for (Record r : zone) {
|
||||||
System.out.println(r.toString());
|
System.out.println(r.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void determineNSEC3Owners(List<Record> zone)
|
private static 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
|
||||||
@@ -160,11 +173,11 @@ public class ZoneFormat extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void execute() throws IOException, NoSuchAlgorithmException {
|
public void execute() throws IOException, NoSuchAlgorithmException {
|
||||||
List<Record> z = readZoneFile(file);
|
List<Record> z = readZoneFile(state.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 (assignNSEC3) {
|
if (state.assignNSEC3) {
|
||||||
determineNSEC3Owners(z);
|
determineNSEC3Owners(z);
|
||||||
} else {
|
} else {
|
||||||
formatZone(z);
|
formatZone(z);
|
||||||
@@ -172,9 +185,10 @@ public class ZoneFormat extends CLBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ZoneFormat tool = new ZoneFormat("zoneformat", "jdnssec-zoneformat [..options..] zonefile");
|
ZoneFormat tool = new ZoneFormat();
|
||||||
|
tool.state = new CLIState();
|
||||||
|
|
||||||
tool.run(args);
|
tool.run(tool.state, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ public class BINDKeyUtils {
|
|||||||
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");
|
||||||
|
|
||||||
@@ -250,6 +251,8 @@ public class BINDKeyUtils {
|
|||||||
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);
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,14 +27,20 @@
|
|||||||
|
|
||||||
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.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.NamedParameterSpec;
|
||||||
import java.security.spec.RSAKeyGenParameterSpec;
|
import java.security.spec.RSAKeyGenParameterSpec;
|
||||||
@@ -60,13 +66,14 @@ 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. Similarly, ECDSAP256SHA256 and
|
// RSASHA256, etc. all boil down to 'RSA' here. Similary, ECDSAP256SHA256 and
|
||||||
// ECDSAP384SHA384 both become 'ECDSA'.
|
// ECDSAP384SHA384 both become 'ECDSA'.
|
||||||
public enum BaseAlgorithm {
|
public enum BaseAlgorithm {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
RSA,
|
RSA,
|
||||||
DH,
|
DH,
|
||||||
DSA,
|
DSA,
|
||||||
|
ECC_GOST,
|
||||||
ECDSA,
|
ECDSA,
|
||||||
EDDSA;
|
EDDSA;
|
||||||
}
|
}
|
||||||
@@ -122,8 +129,12 @@ 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());
|
||||||
|
|
||||||
@@ -131,6 +142,17 @@ 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<?> bcProviderClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
||||||
|
Provider bcProvider = (Provider) bcProviderClass.getDeclaredConstructor().newInstance();
|
||||||
|
Security.addProvider(bcProvider);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
log.fine("Unable to load BC provider");
|
||||||
|
}
|
||||||
|
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +189,14 @@ public class DnsKeyAlgorithm {
|
|||||||
addAlgorithm(DNSSEC.Algorithm.RSASHA512, "SHA512withRSA", BaseAlgorithm.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
|
||||||
|
// 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", BaseAlgorithm.ECC_GOST, null);
|
||||||
|
addMnemonic("ECCGOST", DNSSEC.Algorithm.ECC_GOST);
|
||||||
|
addMnemonic("ECC-GOST", DNSSEC.Algorithm.ECC_GOST);
|
||||||
|
|
||||||
addAlgorithm(DNSSEC.Algorithm.ECDSAP256SHA256, "SHA256withECDSA", BaseAlgorithm.ECDSA, "secp256r1");
|
addAlgorithm(DNSSEC.Algorithm.ECDSAP256SHA256, "SHA256withECDSA", BaseAlgorithm.ECDSA, "secp256r1");
|
||||||
addMnemonic("ECDSAP256SHA256", DNSSEC.Algorithm.ECDSAP256SHA256);
|
addMnemonic("ECDSAP256SHA256", DNSSEC.Algorithm.ECDSAP256SHA256);
|
||||||
addMnemonic("ECDSA-P256", DNSSEC.Algorithm.ECDSAP256SHA256);
|
addMnemonic("ECDSA-P256", DNSSEC.Algorithm.ECDSAP256SHA256);
|
||||||
@@ -196,7 +226,9 @@ public class DnsKeyAlgorithm {
|
|||||||
* library (SunEC).
|
* library (SunEC).
|
||||||
*/
|
*/
|
||||||
private void addECDSAAlgorithm(int algorithm, String sigName, String curveName) {
|
private void addECDSAAlgorithm(int algorithm, String sigName, String curveName) {
|
||||||
ECParameterSpec ecSpec = ECSpecFromName(curveName);
|
ECParameterSpec ecSpec = ECSpecFromAlgorithm(algorithm);
|
||||||
|
if (ecSpec == null)
|
||||||
|
ecSpec = ECSpecFromName(curveName);
|
||||||
if (ecSpec == null)
|
if (ecSpec == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -246,15 +278,10 @@ public class DnsKeyAlgorithm {
|
|||||||
* @param curveName the name of the curve.
|
* @param curveName the name of the curve.
|
||||||
*/
|
*/
|
||||||
private void addAlgorithm(int algorithm, String sigName, BaseAlgorithm baseType, String curveName) {
|
private void addAlgorithm(int algorithm, String sigName, BaseAlgorithm baseType, String curveName) {
|
||||||
switch (baseType) {
|
if (baseType == BaseAlgorithm.ECDSA) {
|
||||||
case ECDSA:
|
|
||||||
addECDSAAlgorithm(algorithm, sigName, curveName);
|
addECDSAAlgorithm(algorithm, sigName, curveName);
|
||||||
break;
|
} else if (baseType == BaseAlgorithm.EDDSA) {
|
||||||
case EDDSA:
|
|
||||||
addEdDSAAlgorithm(algorithm, sigName, curveName);
|
addEdDSAAlgorithm(algorithm, sigName, curveName);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Non-Ellipic curve algorithm passed.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +324,25 @@ 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) {
|
||||||
|
if (algorithm == 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);
|
||||||
|
}
|
||||||
|
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 {
|
||||||
@@ -483,6 +529,22 @@ 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");
|
||||||
@@ -501,9 +563,9 @@ public class DnsKeyAlgorithm {
|
|||||||
}
|
}
|
||||||
case EDDSA: {
|
case EDDSA: {
|
||||||
EdAlgEntry entry = (EdAlgEntry) getEntry(algorithm);
|
EdAlgEntry entry = (EdAlgEntry) getEntry(algorithm);
|
||||||
KeyPairGenerator edKeyGenerator = KeyPairGenerator.getInstance(entry.curveName);
|
mEdKeyGenerator = KeyPairGenerator.getInstance(entry.curveName);
|
||||||
|
|
||||||
pair = edKeyGenerator.generateKeyPair();
|
pair = mEdKeyGenerator.generateKeyPair();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ 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;
|
||||||
@@ -69,8 +68,6 @@ public class DnsKeyConverter {
|
|||||||
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();
|
||||||
}
|
}
|
||||||
@@ -114,16 +111,10 @@ public class DnsKeyConverter {
|
|||||||
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 {
|
||||||
int origAlgorithm = mAlgorithms.originalAlgorithm(alg);
|
return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg,
|
||||||
DNSKEYRecord keyrec = new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, origAlgorithm,
|
|
||||||
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) {
|
||||||
log.severe("Unable to generated a DNSKEYRecord: " + e);
|
// FIXME: this mimics the behavior of KEYConverter.buildRecord(), which would
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
@@ -201,6 +192,8 @@ public class DnsKeyConverter {
|
|||||||
return parsePrivateDSA(lines);
|
return parsePrivateDSA(lines);
|
||||||
case DH:
|
case DH:
|
||||||
return parsePrivateDH(lines);
|
return parsePrivateDH(lines);
|
||||||
|
case ECC_GOST:
|
||||||
|
return parsePrivateECDSA(lines, alg);
|
||||||
case ECDSA:
|
case ECDSA:
|
||||||
return parsePrivateECDSA(lines, alg);
|
return parsePrivateECDSA(lines, alg);
|
||||||
case EDDSA:
|
case EDDSA:
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ public class JCEDnsSecSigner {
|
|||||||
* @param ttl the KEY RR's TTL.
|
* @param ttl the KEY RR's TTL.
|
||||||
* @param dclass the KEY RR's DNS class.
|
* @param dclass the KEY RR's DNS class.
|
||||||
* @param algorithm the DNSSEC algorithm (RSASHA258, RSASHA512,
|
* @param algorithm the DNSSEC algorithm (RSASHA258, RSASHA512,
|
||||||
* ECDSAP256, etc.)
|
* ECDSAP256,
|
||||||
|
* etc.)
|
||||||
* @param flags any flags for the KEY RR.
|
* @param flags any flags for the KEY RR.
|
||||||
* @param keysize the size of the key to generate.
|
* @param keysize the size of the key to generate.
|
||||||
* @param useLargeExponent if generating an RSA key, use the large exponent.
|
* @param useLargeExponent if generating an RSA key, use the large exponent.
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ public class SignUtils {
|
|||||||
if (n.labels() != labels) {
|
if (n.labels() != labels) {
|
||||||
n = n.wild(n.labels() - labels);
|
n = n.wild(n.labels() - labels);
|
||||||
wildcardName = true;
|
wildcardName = true;
|
||||||
log.finer("Detected wildcard expansion: " + rrset.getName() + " changed to " + n);
|
log.fine("Detected wildcard expansion: " + rrset.getName() + " changed to " + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now convert the wire format records in the RRset into a
|
// now convert the wire format records in the RRset into a
|
||||||
@@ -1063,7 +1063,7 @@ public class SignUtils {
|
|||||||
int ldiff = node.name.labels() - zonename.labels();
|
int ldiff = node.name.labels() - zonename.labels();
|
||||||
for (int i = 1; i < ldiff; i++) {
|
for (int i = 1; i < ldiff; i++) {
|
||||||
Name n = new Name(node.name, i);
|
Name n = new Name(node.name, i);
|
||||||
log.finer("Generating ENT NSEC3 for " + n);
|
log.fine("Generating ENT NSEC3 for " + n);
|
||||||
ProtoNSEC3 nsec3 = generateNSEC3(n, zonename, node.ttl, salt, iterations, optIn,
|
ProtoNSEC3 nsec3 = generateNSEC3(n, zonename, node.ttl, salt, iterations, optIn,
|
||||||
null);
|
null);
|
||||||
nsec3s.add(nsec3);
|
nsec3s.add(nsec3);
|
||||||
@@ -1127,11 +1127,11 @@ public class SignUtils {
|
|||||||
// check to see if cur is a duplicate (by name)
|
// check to see if cur is a duplicate (by name)
|
||||||
if (prevNSEC3 != null
|
if (prevNSEC3 != null
|
||||||
&& Arrays.equals(prevNSEC3.getOwner(), curNSEC3.getOwner())) {
|
&& Arrays.equals(prevNSEC3.getOwner(), curNSEC3.getOwner())) {
|
||||||
log.finer("found duplicate NSEC3 (by name) -- merging type maps: "
|
log.fine("found duplicate NSEC3 (by name) -- merging type maps: "
|
||||||
+ prevNSEC3.getTypemap() + " and " + curNSEC3.getTypemap());
|
+ prevNSEC3.getTypemap() + " and " + curNSEC3.getTypemap());
|
||||||
i.remove();
|
i.remove();
|
||||||
prevNSEC3.mergeTypes(curNSEC3.getTypemap());
|
prevNSEC3.mergeTypes(curNSEC3.getTypemap());
|
||||||
log.finer("merged type map: " + prevNSEC3.getTypemap());
|
log.fine("merged type map: " + prevNSEC3.getTypemap());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1317,8 +1317,6 @@ public class SignUtils {
|
|||||||
*/
|
*/
|
||||||
public static void generateDSRecords(Name zonename, List<Record> records, int digestAlg) {
|
public static void generateDSRecords(Name zonename, List<Record> records, int digestAlg) {
|
||||||
|
|
||||||
DSAlgorithm dsAlgorithm = DSAlgorithm.getInstance();
|
|
||||||
|
|
||||||
for (ListIterator<Record> i = records.listIterator(); i.hasNext();) {
|
for (ListIterator<Record> i = records.listIterator(); i.hasNext();) {
|
||||||
Record r = i.next();
|
Record r = i.next();
|
||||||
if (r == null)
|
if (r == null)
|
||||||
@@ -1330,7 +1328,7 @@ public class SignUtils {
|
|||||||
|
|
||||||
// Convert non-zone level KEY records into DS records.
|
// Convert non-zone level KEY records into DS records.
|
||||||
if (r.getType() == Type.DNSKEY && !rName.equals(zonename)) {
|
if (r.getType() == Type.DNSKEY && !rName.equals(zonename)) {
|
||||||
DSRecord ds = dsAlgorithm.calculateDSRecord((DNSKEYRecord) r, digestAlg, r.getTTL());
|
DSRecord ds = calculateDSRecord((DNSKEYRecord) r, digestAlg, r.getTTL());
|
||||||
|
|
||||||
i.set(ds);
|
i.set(ds);
|
||||||
}
|
}
|
||||||
@@ -1378,6 +1376,53 @@ public class SignUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 static 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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate an NSEC3 hash based on a DNS name and NSEC3 hash parameters.
|
* Calculate an NSEC3 hash based on a DNS name and NSEC3 hash parameters.
|
||||||
|
|||||||
@@ -58,11 +58,14 @@ public class ZoneUtils {
|
|||||||
*/
|
*/
|
||||||
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<>();
|
ArrayList<Record> records = new ArrayList<>();
|
||||||
Master m = zonefile.equals("-") ? new Master(System.in) : new Master(zonefile, origin);
|
try (Master m = zonefile.equals("-") ? new Master(System.in) : 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) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user