Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
1406cd2e68 | |||
1727d7c7d8 | |||
2876649a4e | |||
6118ae718e | |||
5fef1dcf24 |
6
.gitattributes
vendored
6
.gitattributes
vendored
|
@ -1,6 +0,0 @@
|
|||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# These are explicitly windows files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
build
|
||||
bin/main
|
||||
dist/
|
||||
.classpath
|
||||
.project
|
||||
.gradle
|
||||
|
|
43
ChangeLog
43
ChangeLog
|
@ -1,3 +1,46 @@
|
|||
2024-04-13 David Blacka <david@blacka.com>
|
||||
|
||||
* Remove support for ECC_GOST
|
||||
* Create a new DSAlgorithm class, move DS creation into that
|
||||
* Add support for DS algorithms 3 and 4 -- bouncycastle crypto
|
||||
provider used for DS algoirthm 3 (GOST R 34.11-94)
|
||||
* Moved support for loading the bouncycastle provider to the new
|
||||
DSAlgorithm class
|
||||
|
||||
2024-04-07 David Blacka <david@blacka.com>
|
||||
|
||||
* Released version 0.20
|
||||
* Removed support for Gradle builds since the gradle config was
|
||||
out-of-date
|
||||
* Requires Java 15 or later for EdDSA support
|
||||
* Supports DNSSEC algorithm 16 using the SunEC provider
|
||||
* Supports a java properties-formatted config file. See
|
||||
jdnssec-tools.properties.example for an example
|
||||
* Updated to dnsjava 3.5.3
|
||||
* Updated to commons-cli 1.6.0
|
||||
* Added a "one-jar" distribution method, and a "univeral" CLI to
|
||||
use with it.
|
||||
* Formatting and linter suggestions
|
||||
|
||||
2024-03-25 David Blacka <davidb@verisign.com>
|
||||
|
||||
* Released version 0.19
|
||||
* Handle duplicate key tags
|
||||
* jdnssec-keygen can now attempt to generate keys with specified key tags
|
||||
|
||||
2023-07-24 David Blacka <davidb@verisign.com>
|
||||
|
||||
* Released version 0.17.1
|
||||
* Add a `-t` option to jdnssec-verifyzone: verify using specified time.
|
||||
|
||||
2022-09-21 David Blacka <davidb@verisign.com>
|
||||
|
||||
* Released version 0.17
|
||||
* Updated to dnsjava 3.5.1
|
||||
* Formatting and linter suggestions
|
||||
* Use slf4j instead of log4j.
|
||||
* jdnssec-dstool can now generate CDS records
|
||||
|
||||
2019-07-23 David Blacka <davidb@verisign.com>
|
||||
|
||||
* Released version 0.16
|
||||
|
|
|
@ -11,5 +11,3 @@ This bit of code has been around since approximately 2005, and has been in "mini
|
|||
* allowing for an external sort once the data is shown to be larger than X, and/or
|
||||
* allowing for a memory-constrained internal sort that uses disk, and/or,
|
||||
* 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.
|
||||
|
|
36
README.md
36
README.md
|
@ -2,15 +2,19 @@
|
|||
|
||||
* <https://github.com/dblacka/jdnssec-tools/wiki>
|
||||
|
||||
Author: David Blacka (davidb@verisign.com)
|
||||
Author: David Blacka (<davidb@verisign.com>)
|
||||
|
||||
This is a collection of DNSSEC tools written in Java. They are intended to be an addition or replacement for the DNSSEC tools that are part of BIND 9.
|
||||
|
||||
These tools depend upon DNSjava (<https://github.com/dnsjava/dnsjava>), the Jakarta Commons CLI and Logging libraries (<https://commons.apache.org/proper/commons-cli>), slf4j (<https://www.slf4j.org>), and Sun's Java Cryptography extensions. A copy of each of these libraries is included in the distribution.
|
||||
|
||||
See the "licenses" directory for the licensing information of this package and the other packages that are distributed with it.
|
||||
See the "[licenses](https://github.com/dblacka/jdnssec-tools/tree/master/licenses)" directory for the licensing information of this package and the other packages that are distributed with it.
|
||||
|
||||
Getting started:
|
||||
## Getting Started
|
||||
|
||||
### Using the binary distribution
|
||||
|
||||
The binary distributions can be downloaded from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page. To use it;
|
||||
|
||||
1. Unpack the binary distribution:
|
||||
|
||||
|
@ -21,9 +25,11 @@ Getting started:
|
|||
cd java-dnssec-tools-x.x.x
|
||||
./bin/jdnssec-signzone -h
|
||||
|
||||
Building from source:
|
||||
### Building from source
|
||||
|
||||
1. Unpack the source distribution, preferably into the same directory that the binary distribution was unpacked.
|
||||
There is a source distribution also downloadable from the [releases](https://github.com/dblacka/jdnssec-tools/releases) page, but this should work with a clone of this repo.
|
||||
|
||||
1. (If downloaded) Unpack the source distribution, preferably into the same directory that the binary distribution was unpacked.
|
||||
|
||||
tar zxvf java-dnssec-tools-x.x.x-src.tar.gz
|
||||
|
||||
|
@ -32,17 +38,19 @@ Building from source:
|
|||
|
||||
ant
|
||||
|
||||
4. You can build the distribution tarballs with 'ant dist'. You can run the tools directly from the build area (without building the jdnssec-tools.jar file) by using the ./bin/\_jdnssec_* wrappers.
|
||||
|
||||
5. Alternatively, build the project using gradle:
|
||||
|
||||
gradlew clean
|
||||
gradlew assemble -i
|
||||
|
||||
The resulting jar file gets generated in build/libs.
|
||||
4. You can build the distribution tarballs with 'ant dist', although the main `ant` build command will have built the primary jar file.
|
||||
|
||||
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).
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.DSTool "$@"
|
|
@ -1,19 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.KeyGen "$@"
|
|
@ -1,19 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.KeyInfoTool "$@"
|
|
@ -1,19 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.SignKeyset "$@"
|
|
@ -1,19 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.SignZone "$@"
|
|
@ -1,19 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.VerifyZone "$@"
|
|
@ -1,19 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=`dirname $0`
|
||||
basedir=`cd $thisdir/..; pwd`
|
||||
|
||||
ulimit_max=`ulimit -H -n`
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
CLASSPATH=$CLASSPATH:$basedir/build/classes
|
||||
|
||||
for i in $basedir/lib/*.jar $basedir/lib/*.zip; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.ZoneFormat "$@"
|
22
bin/jdnssec-tools
Executable file
22
bin/jdnssec-tools
Executable file
|
@ -0,0 +1,22 @@
|
|||
#! /bin/sh
|
||||
|
||||
thisdir=$(dirname $0)
|
||||
basedir=$(cd $thisdir/.. || exit; pwd)
|
||||
|
||||
ulimit_max=$(ulimit -H -n)
|
||||
if [ $ulimit_max != "unlimited" ]; then
|
||||
ulimit -n $ulimit_max
|
||||
fi
|
||||
|
||||
# set the classpath
|
||||
for i in "$basedir"/lib/*.jar "$basedir"/lib/*.zip "$basedir"/build/libs/*.jar; do
|
||||
if ! [ -f $i ]; then continue; fi
|
||||
if [ -z "$CLASSPATH" ]; then
|
||||
CLASSPATH=$i
|
||||
else
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
fi
|
||||
done
|
||||
export CLASSPATH
|
||||
|
||||
exec java com.verisignlabs.dnssec.cl.CLI "$@"
|
25
build.gradle
25
build.gradle
|
@ -1,25 +0,0 @@
|
|||
/**
|
||||
|
||||
Declares dependencies for Jdnssec-tools
|
||||
|
||||
**/
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
|
||||
jar {
|
||||
baseName = 'jdnssec-tools'
|
||||
version = '0.17'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'lib', include: '*.jar')
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
#build.compiler=jikes
|
||||
#build.compiler=modern
|
||||
build.deprecation=true
|
||||
build.debug=false
|
||||
build.java_version=17
|
||||
|
|
97
build.xml
97
build.xml
|
@ -10,18 +10,20 @@
|
|||
|
||||
-->
|
||||
|
||||
<project default="compile" basedir=".">
|
||||
<project default="build" basedir=".">
|
||||
|
||||
<property file="build.properties" />
|
||||
<property file="VERSION" />
|
||||
|
||||
<property name="sectools-distname" value="jdnssec-tools-${version}" />
|
||||
|
||||
<property name="build.dir" value="build" />
|
||||
<property name="build.dest" value="${build.dir}/classes" />
|
||||
<property name="build.lib.dest" value="${build.dir}/libs" />
|
||||
<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="doc.dir" value="docs" />
|
||||
<property name="javadoc.dest" value="${doc.dir}/javadoc" />
|
||||
|
@ -33,6 +35,7 @@
|
|||
<pathelement location="${build.dest}" />
|
||||
<fileset dir="${lib.dir}" includes="*.jar,*.zip" />
|
||||
</path>
|
||||
|
||||
<property name="project.classpath" refid="project.classpath" />
|
||||
|
||||
<target name="prepare-src">
|
||||
|
@ -40,25 +43,40 @@
|
|||
<mkdir dir="${build.lib.dest}" />
|
||||
</target>
|
||||
|
||||
<target name="sectools" depends="prepare-src" >
|
||||
<target name="compile" depends="prepare-src" >
|
||||
<javac srcdir="${build.src}"
|
||||
destdir="${build.dest}"
|
||||
classpathref="project.classpath"
|
||||
deprecation="true"
|
||||
deprecation="${build.deprecation}"
|
||||
includeantruntime="false"
|
||||
includes="com/verisignlabs/dnssec/"
|
||||
source="11"
|
||||
target="11" />
|
||||
debug="${build.debug}"
|
||||
release="${build.java_version}" />
|
||||
</target>
|
||||
|
||||
<target name="sectools-jar" depends="usage,sectools">
|
||||
<target name="build-jar" depends="usage, compile">
|
||||
<jar jarfile="${build.lib.dest}/jdnssec-tools.jar"
|
||||
basedir="${build.dest}"
|
||||
includes="com/verisignlabs/dnssec/" />
|
||||
</target>
|
||||
|
||||
<target name="compile"
|
||||
depends="usage,sectools-jar">
|
||||
<target name="build"
|
||||
depends="usage,build-jar">
|
||||
</target>
|
||||
|
||||
<target name="build-onejar" depends="compile">
|
||||
<jar destfile="${dist.dir}/${dist.name}.jar">
|
||||
<zipfileset dir="${build.dest}" includes="**/*.class" />
|
||||
|
||||
<zipfileset src="${lib.dir}/dnsjava-3.5.3.jar" />
|
||||
<zipfileset src="${lib.dir}/commons-cli-1.6.0.jar" />
|
||||
<zipfileset src="${lib.dir}/slf4j-api-1.7.36.jar" />
|
||||
<zipfileset src="${lib.dir}/slf4j-simple-1.7.36.jar" />
|
||||
<manifest>
|
||||
<attribute name="Main-Class"
|
||||
value="com.verisignlabs.dnssec.cl.CLI" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="javadoc" depends="usage">
|
||||
|
@ -75,16 +93,21 @@
|
|||
</javadoc>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="clean" depends="usage">
|
||||
<delete dir="${build.dest}" />
|
||||
<delete dir="${build.lib.dest}" />
|
||||
<delete dir="${dist.dir}" />
|
||||
</target>
|
||||
|
||||
<target name="sectools-dist-prepare" depends="usage, compile, javadoc">
|
||||
<mkdir dir="${sectools-distname}" />
|
||||
<target name="dist-clean" depends="usage">
|
||||
<delete dir="${dist.name}" />
|
||||
</target>
|
||||
|
||||
<copy todir="${sectools-distname}">
|
||||
<target name="dist-prepare" depends="usage, build, javadoc">
|
||||
<mkdir dir="${dist.dir}" />
|
||||
<mkdir dir="${dist.name}" />
|
||||
|
||||
<copy todir="${dist.name}">
|
||||
<fileset dir=".">
|
||||
<include name="bin/jdnssec-*" />
|
||||
<include name="lib/*.jar" />
|
||||
|
@ -99,37 +122,33 @@
|
|||
</fileset>
|
||||
</copy>
|
||||
|
||||
<copy todir="${sectools-distname}/lib">
|
||||
<copy todir="${dist.name}/lib">
|
||||
<fileset dir="${build.lib.dest}">
|
||||
<include name="*.jar" />
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="sectools-dist-clean">
|
||||
<delete dir="${sectools-distname}" />
|
||||
</target>
|
||||
|
||||
<patternset id="exec.files">
|
||||
<include name="${sectools-distname}/bin/jdnssec-*" />
|
||||
<include name="${dist.name}/bin/jdnssec-*" />
|
||||
</patternset>
|
||||
|
||||
<patternset id="src.files">
|
||||
<include name="${sectools-distname}/src/" />
|
||||
<include name="${sectools-distname}/build.xml" />
|
||||
<include name="${sectools-distname}/build.properties" />
|
||||
<include name="${dist.name}/src/" />
|
||||
<include name="${dist.name}/build.xml" />
|
||||
<include name="${dist.name}/build.properties" />
|
||||
</patternset>
|
||||
|
||||
<patternset id="bin.files">
|
||||
<include name="${sectools-distname}/doc/" />
|
||||
<include name="${sectools-distname}/lib/" />
|
||||
<include name="${sectools-distname}/licenses/" />
|
||||
<include name="${sectools-distname}/VERSION" />
|
||||
<include name="${sectools-distname}/README" />
|
||||
<include name="${dist.name}/doc/" />
|
||||
<include name="${dist.name}/lib/" />
|
||||
<include name="${dist.name}/licenses/" />
|
||||
<include name="${dist.name}/VERSION" />
|
||||
<include name="${dist.name}/README" />
|
||||
</patternset>
|
||||
|
||||
<target name="sectools-bin-dist" depends="sectools-dist-prepare">
|
||||
<tar destfile="${sectools-distname}.tar.gz" compression="gzip">
|
||||
<target name="bin-dist" depends="dist-prepare">
|
||||
<tar destfile="${dist.dir}/${dist.name}.tar.gz" compression="gzip">
|
||||
<tarfileset mode="755" dir=".">
|
||||
<patternset refid="exec.files" />
|
||||
</tarfileset>
|
||||
|
@ -139,34 +158,28 @@
|
|||
</tar>
|
||||
</target>
|
||||
|
||||
<target name="sectools-src-dist" depends="sectools-dist-prepare">
|
||||
<tar destfile="${sectools-distname}-src.tar.gz"
|
||||
compression="gzip">
|
||||
<target name="src-dist" depends="dist-prepare">
|
||||
<tar destfile="${dist.dir}/${dist.name}-src.tar.gz" compression="gzip">
|
||||
<tarfileset dir=".">
|
||||
<patternset refid="src.files" />
|
||||
</tarfileset>
|
||||
</tar>
|
||||
</target>
|
||||
|
||||
<target name="sectools-dist"
|
||||
depends="sectools-bin-dist,sectools-src-dist, sectools-dist-clean">
|
||||
</target>
|
||||
|
||||
|
||||
<target name="dist" depends="sectools-dist">
|
||||
<echo message="do not forget to tag the release:" />
|
||||
<echo message=" svn-tag ${version}" />
|
||||
<target name="dist"
|
||||
depends="bin-dist, src-dist, build-onejar, dist-clean">
|
||||
</target>
|
||||
|
||||
<target name="usage">
|
||||
<echo message=" " />
|
||||
<echo message="SECTOOLS v. ${version} Build System" />
|
||||
<echo message="jdnssec-tools v. ${version} Build System" />
|
||||
<echo message="--------------------------------" />
|
||||
<echo message="Available Targets:" />
|
||||
<echo message=" compile (default) - compiles the source code, creates jar" />
|
||||
<echo message=" build (default) - compiles the source code, creates main jar" />
|
||||
<echo message=" javadoc - create javadoc from source" />
|
||||
<echo message=" clean - delete class files" />
|
||||
<echo message=" dist - package it up" />
|
||||
<echo message=" onejar - build the executable jar" />
|
||||
<echo message=" usage - this help message" />
|
||||
<echo message=" " />
|
||||
</target>
|
||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +0,0 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
185
gradlew
vendored
185
gradlew
vendored
|
@ -1,185 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
89
gradlew.bat
vendored
|
@ -1,89 +0,0 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
93
jdnssec-tools.properties.example
Normal file
93
jdnssec-tools.properties.example
Normal file
|
@ -0,0 +1,93 @@
|
|||
# An example properties file for jdnssec-tools
|
||||
# Properties may be be scoped by the tool name, which is the name minus "jdnssec-"
|
||||
# If unscoped, the same named property will be used by multiple tools
|
||||
|
||||
# Common properties
|
||||
|
||||
# log_level = warning
|
||||
# verbose = true # same as log_level = fine (true) or log_level = warning (false)
|
||||
# multiline = false
|
||||
|
||||
# algorithm aliasing is <scope>.alias.<new-mnemonic> = <orig-alg-id>:<alias-alg-id>
|
||||
# alias.NEWALG = 8:100
|
||||
|
||||
# jdnssec-dstool properties
|
||||
|
||||
## These are all equivalent. Unscoped properties might apply to other tools
|
||||
# dstool.digest_algorithm = 4
|
||||
# digest_algorithm = 4 # applies to jdnssec-signzone, too
|
||||
# dstool.digest_id = 4
|
||||
|
||||
# jdnssec-keygen properties
|
||||
|
||||
# keygen.use_large_exponent = true
|
||||
# keygen.key_directory = .
|
||||
# key_directory = /path/to/dnskey_files # applies to jdnssec-sign*
|
||||
# keygen.algorithm = ED448
|
||||
# keygen.keylength = 2048
|
||||
# keygen.keylen = 2048 # same thing
|
||||
# keygen.ttl = 3600
|
||||
|
||||
# jdnssec-keyinfotool
|
||||
|
||||
# no additional keys
|
||||
|
||||
# jdnssec-signkeyset
|
||||
|
||||
# signkeyset.verify = false
|
||||
# signkeyset.key_directory = .
|
||||
# signkeyset.start = -300
|
||||
# signkeyset.inception = 1712424863
|
||||
# signkeyset.expire = +604800
|
||||
|
||||
# jdnssec-signrrset
|
||||
|
||||
# signrrset.verify_signatures = false
|
||||
# signrrset.verify = false # same thing
|
||||
# signrrset.key_directory = .
|
||||
# signrrset.start = now
|
||||
# signrrset.inception = now # same thing
|
||||
# signrrset.expire = now+3600
|
||||
|
||||
# jdnssec-signzone
|
||||
|
||||
# signzone.verify_signatures = false
|
||||
# signzone.verify = false # same thing
|
||||
# signzone.use_nsec3 = false
|
||||
# signzone.nsec3 = false # same thing
|
||||
# signzone.use_opt_out = false
|
||||
# signzone.opt_out = false # same thing
|
||||
# signzone.verbose_signing = false
|
||||
# signzone.fully_sign_keyset = false
|
||||
# signzone.fully_sign = false # same thing
|
||||
# signzone.key_directory = .
|
||||
# signzone.keydir = . # same thing
|
||||
# signzone.start = now
|
||||
# signzone.inception = now
|
||||
# signzone.expire = now+3600
|
||||
# signzone.nsec3_salt = DEADBEEF
|
||||
# signzone.salt = DEADBEEF # same thing
|
||||
# signzone.nsec3_random_salt_length = 6
|
||||
# signzone.nsec3_salt_length = 6 # same thing
|
||||
# signzone.random_salt_length = 6 # same thing
|
||||
# signzone.nsec3_iterations = 0
|
||||
# signzone.iterations = 0 # same thing
|
||||
# signzone.digest_algorithm = 4
|
||||
# signzone.digest_id = 4 # same thing
|
||||
# signzone.nsec3param_ttl = 86400
|
||||
# signzone.include_names_file = /path/to/include-names
|
||||
# signzone.include_names = /path/to/include-names # same thing
|
||||
|
||||
# jdnssec-verifyzone
|
||||
|
||||
# verifyzone.ignore_time = false
|
||||
# verifyzone.ignore_duplicate_rrs = false
|
||||
# verifyzone.ignore_duplicates = false # same thing
|
||||
# verifyzone.start_fudge = 0
|
||||
# verifyzone.expire_fudge = 0
|
||||
# verifyzone.current_time = now
|
||||
|
||||
# jdnssec-zoneformat
|
||||
|
||||
# zoneformat.assign_nsec3_owners = false
|
||||
# zoneformat.assign_owners = false # same thing
|
Binary file not shown.
BIN
lib/commons-cli-1.6.0.jar
Normal file
BIN
lib/commons-cli-1.6.0.jar
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -17,11 +17,11 @@
|
|||
|
||||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
|
@ -48,8 +48,19 @@ import com.verisignlabs.dnssec.security.DnsKeyAlgorithm;
|
|||
* subclass variant of the CLIState and call run().
|
||||
*/
|
||||
public abstract class CLBase {
|
||||
protected static Logger staticLog = Logger.getLogger(CLBase.class.getName());
|
||||
protected Logger log;
|
||||
protected Logger log = Logger.getLogger(this.getClass().toString());
|
||||
protected Options opts;
|
||||
protected String name;
|
||||
protected String usageStr;
|
||||
protected Properties props;
|
||||
protected CommandLine cli;
|
||||
|
||||
protected CLBase(String name, String usageStr) {
|
||||
this.name = name;
|
||||
this.usageStr = usageStr;
|
||||
|
||||
setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a very simple log formatter that simply outputs the log level and
|
||||
|
@ -70,253 +81,319 @@ public abstract class CLBase {
|
|||
}
|
||||
}
|
||||
|
||||
/** This is the base set of command line options provided to all subclasses. */
|
||||
private void setupCommonOptions() {
|
||||
// Set up the standard set of options that all jdnssec command line tools will
|
||||
// implement.
|
||||
|
||||
// boolean options
|
||||
opts.addOption("h", "help", false, "Print this message.");
|
||||
opts.addOption("m", "multiline", false,
|
||||
"Output DNS records using 'multiline' format");
|
||||
|
||||
opts.addOption(Option.builder("l").longOpt("log-level").argName("level").hasArg()
|
||||
.desc("set the logging level with either java.util.logging levels, or 0-6").build());
|
||||
opts.addOption(Option.builder("v").longOpt("verbose").desc(
|
||||
"set as verbose (log-level = fine)").build());
|
||||
|
||||
opts.addOption(Option.builder("c").longOpt("config").argName("file").hasArg()
|
||||
.desc("configuration file (format: java properties)").build());
|
||||
|
||||
opts.addOption(Option.builder("A").hasArg().argName("alias:original:mnemonic").longOpt("alg-alias")
|
||||
.desc("Define an alias for an algorithm").build());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a base class for command line parsing state. Subclasses should
|
||||
* override setupOptions and processOptions.
|
||||
* This is an overridable method for subclasses to add their own command line
|
||||
* options.
|
||||
*/
|
||||
public static class CLIStateBase {
|
||||
protected Options opts;
|
||||
protected String usageStr;
|
||||
protected abstract void setupOptions();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void parseCommandLine(String[] args) {
|
||||
String[] logLevelOptionKeys = { "log_level", "log-level" };
|
||||
String[] multilineOptionKeys = { "multiline" };
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
try {
|
||||
cli = parser.parse(opts, args);
|
||||
} catch (UnrecognizedOptionException e) {
|
||||
fail("unknown option encountered: " + e.getMessage());
|
||||
} catch (AlreadySelectedException e) {
|
||||
fail("mutually exclusive options have been selected:\n " + e.getMessage());
|
||||
} catch (ParseException e) {
|
||||
fail("unable to parse command line: " + e);
|
||||
}
|
||||
|
||||
/** This is the base set of command line options provided to all subclasses. */
|
||||
private void setup() {
|
||||
// Set up the standard set of options that all jdnssec command line tools will
|
||||
// implement.
|
||||
opts = new Options();
|
||||
|
||||
// boolean options
|
||||
opts.addOption("h", "help", false, "Print this message.");
|
||||
opts.addOption("m", "multiline", false,
|
||||
"Output DNS records using 'multiline' format");
|
||||
|
||||
opts.addOption(Option.builder("v").longOpt("verbose").argName("level").optionalArg(true).desc(
|
||||
"verbosity level -- 0 is silence, 3 is info, 5 is debug information, 6 is trace information. default is level 2 (warning)")
|
||||
.build());
|
||||
|
||||
opts.addOption(Option.builder("A").hasArg().argName("alias:original:mnemonic").longOpt("alg-alias")
|
||||
.desc("Define an alias for an algorithm").build());
|
||||
|
||||
setupOptions(opts);
|
||||
if (cli.hasOption('h')) {
|
||||
usage();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an overridable method for subclasses to add their own command
|
||||
* line options.
|
||||
*
|
||||
* @param opts
|
||||
* the options object to add (via OptionBuilder, typically) new
|
||||
* options to.
|
||||
*/
|
||||
protected void setupOptions(Options opts) {
|
||||
// Subclasses generally override this.
|
||||
String loadedConfig = loadConfig(cli.getOptionValue('c'));
|
||||
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
|
||||
// we set log level with both --log-level and -v/--verbose.
|
||||
String logLevel = cliOption("log-level", logLevelOptionKeys, null);
|
||||
if (logLevel == null) {
|
||||
logLevel = cli.hasOption("v") ? "fine" : "warning";
|
||||
}
|
||||
setLogLevel(rootLogger, logLevel);
|
||||
|
||||
for (Handler h : rootLogger.getHandlers()) {
|
||||
h.setLevel(rootLogger.getLevel());
|
||||
h.setFormatter(new BareLogFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (loadedConfig != null) {
|
||||
log.info("Loaded config file: " + loadedConfig);
|
||||
}
|
||||
|
||||
if (cli.hasOption('h')) {
|
||||
usage();
|
||||
if (cliBooleanOption("m", multilineOptionKeys, false)) {
|
||||
org.xbill.DNS.Options.set("multiline");
|
||||
}
|
||||
|
||||
processAliasOptions();
|
||||
|
||||
processOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process additional tool-specific options. Subclasses generally override
|
||||
* this.
|
||||
*/
|
||||
protected abstract void processOptions();
|
||||
|
||||
/**
|
||||
* Load a configuration (java properties) file for jdnssec-tools. Returns
|
||||
* the path of the loaded file.
|
||||
*
|
||||
* @param configFile a given path to a config file. This will be considered
|
||||
* first.
|
||||
* @return The path of the file that was actually loaded, or null if no config
|
||||
* file was loaded.
|
||||
*/
|
||||
protected String loadConfig(String configFile) {
|
||||
// Do not load config files twice
|
||||
if (props != null) {
|
||||
return null;
|
||||
}
|
||||
props = new Properties();
|
||||
String[] configFiles = { configFile, "jdnssec-tools.properties", ".jdnssec-tools.properties",
|
||||
System.getProperty("user.home") + "/.jdnssec-tools.properties" };
|
||||
|
||||
File f = null;
|
||||
|
||||
for (String fname : configFiles) {
|
||||
if (fname == null) {
|
||||
continue;
|
||||
}
|
||||
f = new File(fname);
|
||||
if (!f.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger rootLogger = Logger.getLogger("");
|
||||
int value = parseInt(cli.getOptionValue('v'), -1);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
if (f != null) {
|
||||
return f.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void fail(String errorMessage) {
|
||||
log.severe(errorMessage);
|
||||
System.exit(64);
|
||||
}
|
||||
/** Print out the usage and help statements, then quit. */
|
||||
public void usage() {
|
||||
HelpFormatter f = new HelpFormatter();
|
||||
|
||||
PrintWriter out = new PrintWriter(System.err);
|
||||
|
||||
// print our own usage statement:
|
||||
f.printHelp(out, 120, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
|
||||
HelpFormatter.DEFAULT_DESC_PAD, null);
|
||||
|
||||
out.flush();
|
||||
System.exit(0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the logging level based on a string value
|
||||
*
|
||||
* @param logger The logger to set -- usually the rootLogger
|
||||
* @param levelStr A level string that is either an integer from 0 to 6, or a
|
||||
* java.util.logging log level string (severe, warning, info,
|
||||
* fine, finer,
|
||||
* finest).
|
||||
*/
|
||||
private void setLogLevel(Logger logger, String levelStr) {
|
||||
Level level;
|
||||
int internalLogLevel = Utils.parseInt(levelStr, -1);
|
||||
if (internalLogLevel != -1) {
|
||||
switch (internalLogLevel) {
|
||||
case 0:
|
||||
rootLogger.setLevel(Level.OFF);
|
||||
level = Level.OFF;
|
||||
break;
|
||||
case 1:
|
||||
rootLogger.setLevel(Level.SEVERE);
|
||||
level = Level.SEVERE;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
rootLogger.setLevel(Level.WARNING);
|
||||
level = Level.WARNING;
|
||||
break;
|
||||
case 3:
|
||||
rootLogger.setLevel(Level.INFO);
|
||||
level = Level.INFO;
|
||||
break;
|
||||
case 4:
|
||||
rootLogger.setLevel(Level.CONFIG);
|
||||
level = Level.FINE;
|
||||
break;
|
||||
case 5:
|
||||
rootLogger.setLevel(Level.FINE);
|
||||
break;
|
||||
case 6:
|
||||
rootLogger.setLevel(Level.ALL);
|
||||
break;
|
||||
level = Level.ALL;
|
||||
}
|
||||
|
||||
// I hate java.util.logging, btw.
|
||||
for (Handler h : rootLogger.getHandlers()) {
|
||||
h.setLevel(rootLogger.getLevel());
|
||||
h.setFormatter(new BareLogFormatter());
|
||||
} else {
|
||||
try {
|
||||
level = Level.parse(levelStr.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Verbosity level '" + levelStr + "' not recognized");
|
||||
level = Level.WARNING;
|
||||
}
|
||||
}
|
||||
logger.setLevel(level);
|
||||
}
|
||||
|
||||
if (cli.hasOption('m')) {
|
||||
org.xbill.DNS.Options.set("multiline");
|
||||
}
|
||||
/**
|
||||
* Process both property file based alias definitions and command line alias
|
||||
* definitions
|
||||
*/
|
||||
protected void processAliasOptions() {
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
// First parse any command line options
|
||||
// those look like '-A <alias-num>:<orig-num>:<mnemonic>', e.g., '-A
|
||||
// 21:13:ECDSAP256-NSEC6'
|
||||
String[] optstrs = null;
|
||||
if ((optstrs = cli.getOptionValues('A')) != null) {
|
||||
for (String value : optstrs) {
|
||||
String[] valueComponents = value.split(":");
|
||||
int aliasAlg = Utils.parseInt(valueComponents[0], -1);
|
||||
int origAlg = Utils.parseInt(valueComponents[1], -1);
|
||||
String mnemonic = valueComponents[2];
|
||||
|
||||
String[] optstrs = null;
|
||||
if ((optstrs = cli.getOptionValues('A')) != null) {
|
||||
for (int i = 0; i < optstrs.length; i++) {
|
||||
addArgAlias(optstrs[i]);
|
||||
if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
|
||||
algs.addAlias(aliasAlg, mnemonic, origAlg);
|
||||
}
|
||||
}
|
||||
|
||||
processOptions(cli);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process additional tool-specific options. Subclasses generally override
|
||||
* this.
|
||||
*
|
||||
* @param cli
|
||||
* The {@link CommandLine} object containing the parsed command
|
||||
* line state.
|
||||
*/
|
||||
protected void processOptions(CommandLine cli) throws ParseException {
|
||||
// Subclasses generally override this.
|
||||
}
|
||||
// Next see if we have any alias options in properties
|
||||
// Those look like 'signzone.alias.<alias-mnemonic> =
|
||||
// <orig-alg-num>:<alias-alg-num>'
|
||||
for (String key : props.stringPropertyNames()) {
|
||||
if (key.startsWith(name + ".alias.") || key.startsWith("alias.")) {
|
||||
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);
|
||||
|
||||
/** Print out the usage and help statements, then quit. */
|
||||
public void usage() {
|
||||
HelpFormatter f = new HelpFormatter();
|
||||
|
||||
PrintWriter out = new PrintWriter(System.err);
|
||||
|
||||
// print our own usage statement:
|
||||
f.printHelp(out, 75, usageStr, null, opts, HelpFormatter.DEFAULT_LEFT_PAD,
|
||||
HelpFormatter.DEFAULT_DESC_PAD, null);
|
||||
|
||||
out.flush();
|
||||
System.exit(64);
|
||||
|
||||
}
|
||||
|
||||
protected void addArgAlias(String s) {
|
||||
if (s == null)
|
||||
return;
|
||||
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
|
||||
String[] v = s.split(":");
|
||||
if (v.length < 2)
|
||||
return;
|
||||
|
||||
int alias = parseInt(v[0], -1);
|
||||
if (alias <= 0)
|
||||
return;
|
||||
int orig = parseInt(v[1], -1);
|
||||
if (orig <= 0)
|
||||
return;
|
||||
String mn = null;
|
||||
if (v.length > 2)
|
||||
mn = v[2];
|
||||
|
||||
algs.addAlias(alias, mn, orig);
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int def) {
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public static long parseLong(String s, long def) {
|
||||
try {
|
||||
return Long.parseLong(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
if (mnemonic != null && origAlg >= 0 && aliasAlg >= 0) {
|
||||
algs.addAlias(aliasAlg, mnemonic, origAlg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a date/time from a command line time/offset duration string.
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, and a default value, determine value for the option
|
||||
*
|
||||
* @param start
|
||||
* the start time to calculate offsets from.
|
||||
* @param duration
|
||||
* the time/offset string to parse.
|
||||
* @return the calculated time.
|
||||
* @param option The option name
|
||||
* @param properties A list of configuration parameters that we would like
|
||||
* to use for this option, from most preferred to least.
|
||||
* @param defaultValue A default value to return if either the option or
|
||||
* config value cannot be parsed, or neither are present.
|
||||
* @return The found value, or the default value.
|
||||
*/
|
||||
public static Instant convertDuration(Instant start, String duration) throws ParseException {
|
||||
if (start == null) {
|
||||
start = Instant.now();
|
||||
protected String cliOption(String option, String[] properties, String defaultValue) {
|
||||
if (cli.hasOption(option)) {
|
||||
return cli.getOptionValue(option);
|
||||
}
|
||||
|
||||
if (duration.startsWith("now")) {
|
||||
start = Instant.now();
|
||||
if (duration.indexOf("+") < 0)
|
||||
return start;
|
||||
|
||||
duration = duration.substring(3);
|
||||
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("+")) {
|
||||
long offset = parseLong(duration.substring(1), 0);
|
||||
return start.plusSeconds(offset);
|
||||
}
|
||||
/**
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, determine the value for the option, converting the value to
|
||||
* long.
|
||||
*/
|
||||
protected long cliLongOption(String option, String[] properties, long defaultValue) {
|
||||
String value = cliOption(option, properties, Long.toString(defaultValue));
|
||||
return Utils.parseLong(value, defaultValue);
|
||||
}
|
||||
|
||||
// This is a heuristic to distinguish UNIX epoch times from the zone file
|
||||
// format standard (which is length == 14)
|
||||
if (duration.length() <= 10) {
|
||||
long epoch = parseLong(duration, 0);
|
||||
return Instant.ofEpochSecond(epoch);
|
||||
}
|
||||
/**
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, determine the value for the option, converting the value to
|
||||
* int.
|
||||
*/
|
||||
protected int cliIntOption(String option, String[] properties, int defaultValue) {
|
||||
String value = cliOption(option, properties, Integer.toString(defaultValue));
|
||||
return Utils.parseInt(value, defaultValue);
|
||||
}
|
||||
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
try {
|
||||
Date parsedDate = dateFormatter.parse(duration);
|
||||
return parsedDate.toInstant();
|
||||
} catch (java.text.ParseException e) {
|
||||
throw new ParseException(e.getMessage());
|
||||
/**
|
||||
* Given a parsed command line, option, and list of possible config
|
||||
* properties, determine the value for the option, converting the value to
|
||||
* a boolean.
|
||||
*/
|
||||
protected boolean cliBooleanOption(String option, String[] properties, boolean defaultValue) {
|
||||
if (cli.hasOption(option)) {
|
||||
return true;
|
||||
}
|
||||
String value = cliOption(option, properties, Boolean.toString(defaultValue));
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public abstract void execute() throws Exception;
|
||||
|
||||
public 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();
|
||||
}
|
||||
public void run(String[] args) {
|
||||
|
||||
parseCommandLine(args);
|
||||
log = Logger.getLogger(this.getClass().toString());
|
||||
|
||||
try {
|
||||
|
|
84
src/main/java/com/verisignlabs/dnssec/cl/CLI.java
Normal file
84
src/main/java/com/verisignlabs/dnssec/cl/CLI.java
Normal file
|
@ -0,0 +1,84 @@
|
|||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
public class CLI {
|
||||
private SubCommandType subCommand = null;
|
||||
private String commandSetStr = null;
|
||||
|
||||
enum SubCommandType {
|
||||
DSTOOL, KEYGEN, KEYINFO, SIGNKEYSET, SIGNRRSET, SIGNZONE, VERIFYZONE, ZONEFORMAT;
|
||||
}
|
||||
|
||||
public CLI(String name, String usageStr) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (SubCommandType type : SubCommandType.class.getEnumConstants()) {
|
||||
sb.append(type.toString().toLowerCase());
|
||||
sb.append(" ");
|
||||
}
|
||||
commandSetStr = sb.toString().trim();
|
||||
}
|
||||
|
||||
private void fail(String errorMessage){
|
||||
System.err.println("ERROR: " + errorMessage);
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
public void run(String[] args) {
|
||||
String[] subCommandArgs = null;
|
||||
if (args.length < 1) {
|
||||
fail("missing command: must be one of: " + commandSetStr);
|
||||
}
|
||||
|
||||
String command = args[0];
|
||||
if (command.equals("-h")) {
|
||||
System.err.println("usage: jdnssec-tools <command> <command args..>");
|
||||
System.err.println(" <command> is one of: " + commandSetStr);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
subCommand = SubCommandType.valueOf(command.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
fail("unrecognized command '" + command + "': must be one of: " + commandSetStr);
|
||||
}
|
||||
subCommandArgs = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, subCommandArgs, 0, args.length - 1);
|
||||
|
||||
CLBase cmd = null;
|
||||
|
||||
switch(subCommand) {
|
||||
case DSTOOL:
|
||||
cmd = new DSTool("dstool", "jdnssec-tools dstool [..options..] keyfile [keyfile..]");
|
||||
break;
|
||||
case KEYGEN:
|
||||
cmd = new KeyGen("keygen", "jdnssec-tools keygen [..options..] zonename");
|
||||
break;
|
||||
case KEYINFO:
|
||||
cmd = new KeyInfoTool("keyinfotool", "jdnssec-tools keyinfo [..options..] keyfile");
|
||||
break;
|
||||
case SIGNKEYSET:
|
||||
cmd = new SignKeyset("signkeyset", "jdnssec-tools signkeyset [..options..] dnskeyset_file [key_file ...]");
|
||||
break;
|
||||
case SIGNRRSET:
|
||||
cmd = new SignRRset("signrrset", "jdnssec-tools signrrset [..options..] rrset_file key_file [key_file ...]");
|
||||
break;
|
||||
case SIGNZONE:
|
||||
cmd = new SignZone("signzone", "jdnssec-tools signzone [..options..] zone_file [key_file ...]");
|
||||
break;
|
||||
case VERIFYZONE:
|
||||
cmd = new VerifyZone("verifyzone", "jdnssec-tools verifyzone [..options..] zonefile");
|
||||
break;
|
||||
case ZONEFORMAT:
|
||||
cmd = new ZoneFormat("zoneformat", "jdnssec-tools zoneformat [..options..] zonefile");
|
||||
break;
|
||||
default:
|
||||
fail("commmand " + command + " has not been implemented.");
|
||||
break;
|
||||
}
|
||||
cmd.run(subCommandArgs);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CLI cli = new CLI("cli", "jdnssec-tools <command> [..args..]");
|
||||
cli.run(args);
|
||||
}
|
||||
}
|
|
@ -18,21 +18,18 @@
|
|||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
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.DNSSEC;
|
||||
import org.xbill.DNS.DSRecord;
|
||||
import org.xbill.DNS.Record;
|
||||
|
||||
import com.verisignlabs.dnssec.security.BINDKeyUtils;
|
||||
import com.verisignlabs.dnssec.security.DSAlgorithm;
|
||||
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
|
||||
|
@ -40,7 +37,15 @@ import com.verisignlabs.dnssec.security.SignUtils;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class DSTool extends CLBase {
|
||||
private CLIState state;
|
||||
private dsType createType = dsType.DS;
|
||||
private String outputfile = null;
|
||||
private String[] keynames = null;
|
||||
private int digestId = DNSSEC.Digest.SHA256;
|
||||
private long dsTTL = -1;
|
||||
|
||||
public DSTool(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
/** There are several records that are based on DS. */
|
||||
protected enum dsType {
|
||||
|
@ -52,79 +57,76 @@ public class DSTool extends CLBase {
|
|||
* 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.SHA1;
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-dstool [..options..] keyfile");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*
|
||||
* @return a set of command line options.
|
||||
*/
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
opts.addOption(Option.builder("D").longOpt("dlv").desc("Generate a DLV record instead.").build());
|
||||
opts.addOption(Option.builder("C").longOpt("cds").desc("Generate a CDS record instead").build());
|
||||
opts.addOption(Option.builder("d").hasArg().argName("id").longOpt("digest").desc("The digest algorithm to use").build());
|
||||
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output").desc("output to file").build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli)
|
||||
throws org.apache.commons.cli.ParseException {
|
||||
outputfile = cli.getOptionValue('f');
|
||||
if (cli.hasOption("dlv")) {
|
||||
createType = dsType.DLV;
|
||||
} else if (cli.hasOption("cds")) {
|
||||
createType = dsType.CDS;
|
||||
}
|
||||
String optstr = cli.getOptionValue('d');
|
||||
if (optstr != null)
|
||||
digestId = DNSSEC.Digest.value(optstr);
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
System.err.println("error: missing key file ");
|
||||
usage();
|
||||
}
|
||||
|
||||
keyname = args[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*
|
||||
* @return a set of command line options.
|
||||
*/
|
||||
protected void setupOptions() {
|
||||
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());
|
||||
String[] algStrings = DSAlgorithm.getInstance().supportedAlgorithmMnemonics();
|
||||
String algStringSet = String.join(" | ", algStrings);
|
||||
opts.addOption(
|
||||
Option.builder("d").hasArg().argName("id").longOpt("digest").desc(algStringSet + ": default is SHA256")
|
||||
.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());
|
||||
}
|
||||
|
||||
public void execute() throws Exception {
|
||||
DnsKeyPair key = BINDKeyUtils.loadKey(state.keyname, null);
|
||||
protected void processOptions() {
|
||||
String[] digestAlgOptionKeys = { "digest_algorithm", "digest_id" };
|
||||
String[] dsTTLOptionKeys = { "ds_ttl", "ttl" };
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
if (cli.hasOption("dlv")) {
|
||||
createType = dsType.DLV;
|
||||
} else if (cli.hasOption("cds")) {
|
||||
createType = dsType.CDS;
|
||||
}
|
||||
String digestValue = cliOption("d", digestAlgOptionKeys, Integer.toString(digestId));
|
||||
digestId = DNSSEC.Digest.value(digestValue);
|
||||
|
||||
dsTTL = cliLongOption("ttl", dsTTLOptionKeys, dsTTL);
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
fail("missing key file");
|
||||
}
|
||||
|
||||
keynames = args;
|
||||
}
|
||||
|
||||
public void createDS(String keyname) throws IOException {
|
||||
DSAlgorithm dsAlgorithm = DSAlgorithm.getInstance();
|
||||
DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null);
|
||||
DNSKEYRecord dnskey = key.getDNSKEYRecord();
|
||||
|
||||
if ((dnskey.getFlags() & DNSKEYRecord.Flags.SEP_KEY) == 0) {
|
||||
log.warning("DNSKEY is not an SEP-flagged key.");
|
||||
log.warning("DNSKEY " + keyname + " is not an SEP-flagged key.");
|
||||
}
|
||||
|
||||
DSRecord ds = SignUtils.calculateDSRecord(dnskey, state.digestId, dnskey.getTTL());
|
||||
Record res = ds;
|
||||
long ttl = dsTTL < 0 ? dnskey.getTTL() : dsTTL;
|
||||
DSRecord ds = dsAlgorithm.calculateDSRecord(dnskey, digestId, ttl);
|
||||
Record res;
|
||||
|
||||
if (state.createType == dsType.DLV) {
|
||||
log.fine("creating DLV.");
|
||||
DLVRecord dlv = new DLVRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||
ds.getDigestID(), ds.getDigest());
|
||||
res = dlv;
|
||||
} else if (state.createType == dsType.CDS) {
|
||||
log.fine("creating CDS.");
|
||||
CDSRecord cds = new CDSRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||
ds.getDClass(), ds.getDigest());
|
||||
res = cds;
|
||||
switch (createType) {
|
||||
case DLV:
|
||||
log.fine("creating DLV.");
|
||||
res = dsAlgorithm.dsToDLV(ds);
|
||||
break;
|
||||
case CDS:
|
||||
log.fine("creating CDS.");
|
||||
res = dsAlgorithm.dstoCDS(ds);
|
||||
break;
|
||||
default:
|
||||
res = ds;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state.outputfile != null && !state.outputfile.equals("-")) {
|
||||
try (PrintWriter out = new PrintWriter(new FileWriter(state.outputfile))) {
|
||||
|
||||
if (outputfile != null && !outputfile.equals("-")) {
|
||||
try (PrintWriter out = new PrintWriter(new FileWriter(outputfile))) {
|
||||
out.println(res);
|
||||
}
|
||||
} else {
|
||||
|
@ -132,10 +134,15 @@ public class DSTool extends CLBase {
|
|||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
DSTool tool = new DSTool();
|
||||
tool.state = new CLIState();
|
||||
public void execute() throws Exception {
|
||||
for (String keyname : keynames) {
|
||||
createDS(keyname);
|
||||
}
|
||||
}
|
||||
|
||||
tool.run(tool.state, args);
|
||||
public static void main(String[] args) {
|
||||
DSTool tool = new DSTool("dstool", "jdnssec-dstool [..options..] keyfile [keyfile..]");
|
||||
|
||||
tool.run(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,7 @@ package com.verisignlabs.dnssec.cl;
|
|||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.xbill.DNS.DClass;
|
||||
import org.xbill.DNS.DNSKEYRecord;
|
||||
import org.xbill.DNS.Name;
|
||||
|
@ -37,163 +35,144 @@ import com.verisignlabs.dnssec.security.JCEDnsSecSigner;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class KeyGen extends CLBase {
|
||||
private CLIState state;
|
||||
private int algorithm = 13;
|
||||
private int keylength = 2048;
|
||||
private boolean useLargeE = true;
|
||||
private String outputfile = null;
|
||||
private File keydir = null;
|
||||
private boolean zoneKey = true;
|
||||
private boolean kskFlag = false;
|
||||
private String owner = null;
|
||||
private long ttl = 86400;
|
||||
private int givenKeyTag = -1;
|
||||
|
||||
public KeyGen(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase {
|
||||
public int algorithm = 8;
|
||||
public int keylength = 1024;
|
||||
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;
|
||||
protected void setupOptions() {
|
||||
// boolean options
|
||||
opts.addOption("k", "kskflag", false,
|
||||
"Key is a key-signing-key (sets the SEP flag).");
|
||||
opts.addOption("e", "large-exponent", false, "Use large RSA exponent (default)");
|
||||
opts.addOption("E", "small-exponent", false, "Use small RSA exponent");
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-keygen [..options..] name");
|
||||
// Argument options
|
||||
opts.addOption(
|
||||
Option.builder("n").longOpt("nametype").hasArg().argName("type").desc("ZONE | OTHER (default ZONE)").build());
|
||||
|
||||
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
|
||||
String algStringSet = String.join(" | ", algStrings);
|
||||
opts.addOption(Option.builder("a").hasArg().argName("algorithm")
|
||||
.desc(algStringSet + " | aliases, ECDSAP256SHA256 is default.").build());
|
||||
|
||||
opts.addOption(Option.builder("b").hasArg().argName("size").desc(
|
||||
"key size, in bits (default 2048). RSA: [512..4096], DSA: [512..1024], DH: [128..4096], ECDSA: ignored, EdDSA: ignored")
|
||||
.build());
|
||||
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output-file")
|
||||
.desc("base filename from the public/private key files").build());
|
||||
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keydir")
|
||||
.desc("generated keyfiles are written to this directory").build());
|
||||
opts.addOption(Option.builder("T").hasArg().argName("ttl").longOpt("ttl")
|
||||
.desc("use this TTL for the generated DNSKEY records (default: 86400").build());
|
||||
opts.addOption(Option.builder().hasArg().argName("tag").longOpt("with-tag")
|
||||
.desc("Generate keys until tag is the given value.").build());
|
||||
|
||||
}
|
||||
|
||||
protected void processOptions() {
|
||||
String[] useLargeEOptionKeys = { "use_large_exponent", "use_large_e" };
|
||||
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
|
||||
String[] algorithmOptionKeys = { "algorithm", "alg " };
|
||||
String[] keyLengthOptionKeys = { "key_length", "keylen" };
|
||||
String[] ttlOptionKeys = { "dnskey_ttl", "ttl" };
|
||||
|
||||
if (cli.hasOption('k')) {
|
||||
kskFlag = true;
|
||||
}
|
||||
useLargeE = cli.hasOption('e'); // explicit command line option for the large exponent
|
||||
useLargeE = !cli.hasOption('E'); // explicit command line option for the small exponent
|
||||
String optstr = cliOption("e", useLargeEOptionKeys, Boolean.toString(useLargeE)); // get any config file properties
|
||||
if (optstr != null) {
|
||||
useLargeE = Boolean.parseBoolean(optstr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
// boolean options
|
||||
opts.addOption("k", "kskflag", false,
|
||||
"Key is a key-signing-key (sets the SEP flag).");
|
||||
opts.addOption("e", "large-exponent", false, "Use large RSA exponent (default)");
|
||||
opts.addOption("E", "small-exponent", false, "Use small RSA exponent");
|
||||
|
||||
// Argument options
|
||||
opts.addOption(
|
||||
Option.builder("n").longOpt("nametype").hasArg().argName("type").desc("ZONE | OTHER (default ZONE)").build());
|
||||
|
||||
String[] algStrings = DnsKeyAlgorithm.getInstance().supportedAlgMnemonics();
|
||||
String algStringSet = String.join(" | ", algStrings);
|
||||
opts.addOption(Option.builder("a").hasArg().argName("algorithm")
|
||||
.desc(algStringSet + " | alias, RSASHA256 is default.").build());
|
||||
|
||||
opts.addOption(Option.builder("b").hasArg().argName("size").desc(
|
||||
"key size, in bits (default 1024). RSA: [512..4096], DSA: [512..1024], DH: [128..4096], ECDSA: ignored, EdDSA: ignored")
|
||||
.build());
|
||||
opts.addOption(Option.builder("f").hasArg().argName("file").longOpt("output-file")
|
||||
.desc("base filename from the public/private key files").build());
|
||||
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keydir")
|
||||
.desc("generated keyfiles are written to this directory").build());
|
||||
opts.addOption(Option.builder("T").hasArg().argName("ttl").longOpt("ttl")
|
||||
.desc("use this TTL for the generated DNSKEY records (default: 86400").build());
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
String keydirName = cliOption("d", keyDirectoryOptionKeys, null);
|
||||
if (keydirName != null) {
|
||||
keydir = new File(keydirName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli)
|
||||
throws org.apache.commons.cli.ParseException {
|
||||
String optstr = null;
|
||||
String[] optstrs = null;
|
||||
|
||||
if (cli.hasOption('k'))
|
||||
kskFlag = true;
|
||||
if (cli.hasOption('e'))
|
||||
useLargeE = true;
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
if ((optstr = cli.getOptionValue('d')) != null) {
|
||||
keydir = new File(optstr);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('n')) != null && !optstr.equalsIgnoreCase("ZONE")) {
|
||||
zoneKey = false;
|
||||
}
|
||||
|
||||
if ((optstrs = cli.getOptionValues('A')) != null) {
|
||||
for (int i = 0; i < optstrs.length; i++) {
|
||||
addArgAlias(optstrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('a')) != null) {
|
||||
algorithm = CLIState.parseAlg(optstr);
|
||||
if (algorithm < 0) {
|
||||
System.err.println("DNSSEC algorithm " + optstr + " is not supported");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('b')) != null) {
|
||||
keylength = parseInt(optstr, 1024);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("ttl")) != null) {
|
||||
ttl = parseInt(optstr, 86400);
|
||||
}
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
System.err.println("error: missing key owner name");
|
||||
usage();
|
||||
}
|
||||
|
||||
owner = args[0];
|
||||
String algString = cliOption("a", algorithmOptionKeys, Integer.toString(algorithm));
|
||||
algorithm = Utils.parseAlg(algString);
|
||||
if (algorithm < 0) {
|
||||
fail("DNSSEC algorithm " + algString + " is not supported");
|
||||
}
|
||||
|
||||
private static int parseAlg(String s) {
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
keylength = cliIntOption("b", keyLengthOptionKeys, keylength);
|
||||
ttl = cliLongOption("ttl", ttlOptionKeys, ttl);
|
||||
givenKeyTag = Utils.parseInt(cli.getOptionValue("with-tag"), -1);
|
||||
|
||||
int alg = parseInt(s, -1);
|
||||
if (alg > 0) {
|
||||
if (algs.supportedAlgorithm(alg))
|
||||
return alg;
|
||||
return -1;
|
||||
}
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
return algs.stringToAlgorithm(s);
|
||||
if (args.length < 1) {
|
||||
fail("missing key owner name");
|
||||
}
|
||||
|
||||
owner = args[0];
|
||||
|
||||
log.fine("keygen options => algorithm: " + algorithm + ", keylength: " + keylength +
|
||||
", useLargeE: " + useLargeE + ", kskFlag: " + kskFlag + ", ttl: " + ttl + ", givenKeyTag: " + givenKeyTag);
|
||||
}
|
||||
|
||||
public void execute() throws Exception {
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||
|
||||
// Minor hack to make the owner name absolute.
|
||||
if (!state.owner.endsWith(".")) {
|
||||
state.owner = state.owner + ".";
|
||||
if (!owner.endsWith(".")) {
|
||||
owner = owner + ".";
|
||||
}
|
||||
|
||||
Name ownerName = Name.fromString(state.owner);
|
||||
Name ownerName = Name.fromString(owner);
|
||||
|
||||
// Calculate our flags
|
||||
int flags = 0;
|
||||
if (state.zoneKey)
|
||||
if (zoneKey) {
|
||||
flags |= DNSKEYRecord.Flags.ZONE_KEY;
|
||||
if (state.kskFlag)
|
||||
}
|
||||
if (kskFlag) {
|
||||
flags |= DNSKEYRecord.Flags.SEP_KEY;
|
||||
}
|
||||
log.fine("create key pair with (name = " + ownerName + ", ttl = " + ttl
|
||||
+ ", alg = " + algorithm + ", flags = " + flags + ", length = "
|
||||
+ keylength + ")");
|
||||
|
||||
log.fine("create key pair with (name = " + ownerName + ", ttl = " + state.ttl
|
||||
+ ", alg = " + state.algorithm + ", flags = " + flags + ", length = "
|
||||
+ state.keylength + ")");
|
||||
DnsKeyPair pair = signer.generateKey(ownerName, ttl, DClass.IN,
|
||||
algorithm, flags, keylength,
|
||||
useLargeE);
|
||||
|
||||
DnsKeyPair pair = signer.generateKey(ownerName, state.ttl, DClass.IN,
|
||||
state.algorithm, flags, state.keylength,
|
||||
state.useLargeE);
|
||||
// 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 (givenKeyTag >= 0 && pair.getDNSKEYFootprint() != givenKeyTag) {
|
||||
pair = signer.generateKey(ownerName, ttl, DClass.IN, algorithm, flags, keylength,
|
||||
useLargeE);
|
||||
}
|
||||
|
||||
if (state.outputfile != null) {
|
||||
BINDKeyUtils.writeKeyFiles(state.outputfile, pair, state.keydir);
|
||||
if (outputfile != null) {
|
||||
BINDKeyUtils.writeKeyFiles(outputfile, pair, keydir);
|
||||
} else {
|
||||
BINDKeyUtils.writeKeyFiles(pair, state.keydir);
|
||||
BINDKeyUtils.writeKeyFiles(pair, keydir);
|
||||
System.out.println(BINDKeyUtils.keyFileBase(pair));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
KeyGen tool = new KeyGen();
|
||||
tool.state = new CLIState();
|
||||
KeyGen tool = new KeyGen("keygen", "jdnssec-keygen [..options..] zonename");
|
||||
|
||||
tool.run(tool.state, args);
|
||||
tool.run(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,6 @@ package com.verisignlabs.dnssec.cl;
|
|||
import java.security.interfaces.DSAPublicKey;
|
||||
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 com.verisignlabs.dnssec.security.BINDKeyUtils;
|
||||
|
@ -35,41 +32,32 @@ import com.verisignlabs.dnssec.security.DnsKeyPair;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class KeyInfoTool extends CLBase {
|
||||
private CLIState state;
|
||||
private String[] keynames = null;
|
||||
|
||||
public KeyInfoTool(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a small inner class used to hold all of the command line option
|
||||
* state.
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase {
|
||||
public String[] keynames = null;
|
||||
protected void setupOptions() {
|
||||
// no special options at the moment.
|
||||
}
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-keyinfo [..options..] keyfile");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
// no special options at the moment.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli) throws ParseException {
|
||||
protected void processOptions() {
|
||||
keynames = cli.getArgs();
|
||||
|
||||
if (keynames.length < 1) {
|
||||
System.err.println("error: missing key file ");
|
||||
usage();
|
||||
fail("missing key file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void execute() throws Exception {
|
||||
for (int i = 0; i < state.keynames.length; ++i) {
|
||||
String keyname = state.keynames[i];
|
||||
for (int i = 0; i < keynames.length; ++i) {
|
||||
String keyname = keynames[i];
|
||||
DnsKeyPair key = BINDKeyUtils.loadKey(keyname, null);
|
||||
DNSKEYRecord dnskey = key.getDNSKEYRecord();
|
||||
DnsKeyAlgorithm dnskeyalg = DnsKeyAlgorithm.getInstance();
|
||||
|
@ -84,29 +72,28 @@ public class KeyInfoTool extends CLBase {
|
|||
+ " (" + dnskey.getAlgorithm() + ")");
|
||||
System.out.println("ID: " + dnskey.getFootprint());
|
||||
System.out.println("KeyFileBase: " + BINDKeyUtils.keyFileBase(key));
|
||||
int basetype = dnskeyalg.baseType(dnskey.getAlgorithm());
|
||||
DnsKeyAlgorithm.BaseAlgorithm basetype = dnskeyalg.baseType(dnskey.getAlgorithm());
|
||||
|
||||
if (basetype == DnsKeyAlgorithm.RSA) {
|
||||
if (basetype == DnsKeyAlgorithm.BaseAlgorithm.RSA) {
|
||||
RSAPublicKey pub = (RSAPublicKey) key.getPublic();
|
||||
System.out.println("RSA Public Exponent: " + pub.getPublicExponent());
|
||||
System.out.println("RSA Modulus: " + pub.getModulus());
|
||||
} else if (basetype == DnsKeyAlgorithm.DSA) {
|
||||
} else if (basetype == DnsKeyAlgorithm.BaseAlgorithm.DSA) {
|
||||
DSAPublicKey pub = (DSAPublicKey) key.getPublic();
|
||||
System.out.println("DSA base (G): " + pub.getParams().getG());
|
||||
System.out.println("DSA prime (P): " + pub.getParams().getP());
|
||||
System.out.println("DSA subprime (Q): " + pub.getParams().getQ());
|
||||
System.out.println("DSA public (Y): " + pub.getY());
|
||||
}
|
||||
if (state.keynames.length - i > 1) {
|
||||
if (keynames.length - i > 1) {
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
KeyInfoTool tool = new KeyInfoTool();
|
||||
tool.state = new CLIState();
|
||||
KeyInfoTool tool = new KeyInfoTool("keyinfotool", "jdnssec-keyinfo [..options..] keyfile");
|
||||
|
||||
tool.run(tool.state, args);
|
||||
tool.run(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,7 @@ import java.time.Instant;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.xbill.DNS.Name;
|
||||
import org.xbill.DNS.RRSIGRecord;
|
||||
import org.xbill.DNS.RRset;
|
||||
|
@ -48,66 +46,78 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class SignKeyset extends CLBase {
|
||||
private CLIState state;
|
||||
private File keyDirectory = null;
|
||||
private String[] keyFiles = null;
|
||||
private Instant start = null;
|
||||
private Instant expire = null;
|
||||
private String inputfile = null;
|
||||
private String outputfile = null;
|
||||
private boolean verifySigs = false;
|
||||
|
||||
public SignKeyset(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is an inner class used to hold all of the command line option state.
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase {
|
||||
public File keyDirectory = null;
|
||||
public String[] keyFiles = null;
|
||||
public Instant start = null;
|
||||
public Instant expire = null;
|
||||
public String inputfile = null;
|
||||
public String outputfile = null;
|
||||
public boolean verifySigs = false;
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]");
|
||||
}
|
||||
protected void setupOptions() {
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
// Argument options
|
||||
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
|
||||
.desc("directory where key files are found (default '.').").build());
|
||||
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
|
||||
.desc("signature starting time (default is now - 1 hour)").build());
|
||||
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
|
||||
.desc("signature expiration time (default is start-time + 30 days)").build());
|
||||
opts.addOption(
|
||||
Option.builder("f").hasArg().argName("outfile").desc("file the signed keyset is written to").build());
|
||||
}
|
||||
|
||||
// Argument options
|
||||
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory").desc("directory where key files are found (default '.').").build());
|
||||
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time").desc("signature starting time (default is now - 1 hour)").build());
|
||||
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time").desc("signature expiration time (default is start-time + 30 days)").build());
|
||||
opts.addOption(Option.builder("f").hasArg().argName("outfile").desc("file the signed keyset is written to").build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli)
|
||||
throws org.apache.commons.cli.ParseException {
|
||||
protected void processOptions() {
|
||||
String[] verifyOptionKeys = { "verify_signatures", "verify" };
|
||||
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
|
||||
String[] inceptionOptionKeys = { "inception", "start" };
|
||||
String[] expireOptionKeys = { "expire" };
|
||||
|
||||
String optstr = null;
|
||||
|
||||
if (cli.hasOption('a'))
|
||||
verifySigs = true;
|
||||
|
||||
if ((optstr = cli.getOptionValue('D')) != null) {
|
||||
verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
|
||||
|
||||
String keyDirectoryName = cliOption("D", keyDirectoryOptionKeys, null);
|
||||
if (keyDirectoryName != null) {
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory()) {
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
fail("key directory " + optstr + " is not a directory");
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('s')) != null) {
|
||||
start = convertDuration(null, optstr);
|
||||
} else {
|
||||
// default is now - 1 hour.
|
||||
start = Instant.now().minusSeconds(3600);
|
||||
try {
|
||||
optstr = cliOption("s", inceptionOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
start = Utils.convertDuration(null, optstr);
|
||||
} else {
|
||||
// default is now - 1 hour.
|
||||
start = Instant.now().minusSeconds(3600);
|
||||
}
|
||||
} catch (java.text.ParseException e) {
|
||||
fail("Unable to parse start time specifiction: " + e);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('e')) != null) {
|
||||
expire = convertDuration(start, optstr);
|
||||
} else {
|
||||
expire = convertDuration(start, "+2592000"); // 30 days
|
||||
try {
|
||||
optstr = cliOption("e", expireOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
expire = Utils.convertDuration(start, optstr);
|
||||
} else {
|
||||
expire = Utils.convertDuration(start, "+2592000"); // 30 days
|
||||
}
|
||||
} catch (java.text.ParseException e) {
|
||||
fail("Unable to parse expire time specification: " + e);
|
||||
}
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
@ -115,8 +125,7 @@ public class SignKeyset extends CLBase {
|
|||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 1) {
|
||||
System.err.println("error: missing zone file and/or key files");
|
||||
usage();
|
||||
fail("missing zone file and/or key files");
|
||||
}
|
||||
|
||||
inputfile = files[0];
|
||||
|
@ -125,18 +134,15 @@ public class SignKeyset extends CLBase {
|
|||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the generated signatures.
|
||||
*
|
||||
* @param records
|
||||
* a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs
|
||||
* a list of keypairs used the sign the zone.
|
||||
*
|
||||
* @param records a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs a list of keypairs used the sign the zone.
|
||||
* @return true if all of the signatures validated.
|
||||
*/
|
||||
private static boolean verifySigs(List<Record> records,
|
||||
private boolean verifySigs(List<Record> records,
|
||||
List<DnsKeyPair> keypairs) {
|
||||
boolean secure = true;
|
||||
|
||||
|
@ -158,7 +164,7 @@ public class SignKeyset extends CLBase {
|
|||
boolean result = verifier.verify(rrset);
|
||||
|
||||
if (!result) {
|
||||
staticLog.fine("Signatures did not verify for RRset: " + rrset);
|
||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
||||
secure = false;
|
||||
}
|
||||
}
|
||||
|
@ -168,19 +174,16 @@ public class SignKeyset extends CLBase {
|
|||
|
||||
/**
|
||||
* Load the key pairs from the key files.
|
||||
*
|
||||
* @param keyfiles
|
||||
* a string array containing the base names or paths of the
|
||||
* keys
|
||||
* to be loaded.
|
||||
* @param startIndex
|
||||
* the starting index of keyfiles string array to use. This
|
||||
* allows us to use the straight command line argument array.
|
||||
* @param inDirectory
|
||||
* the directory to look in (may be null).
|
||||
*
|
||||
* @param keyfiles a string array containing the base names or paths of the
|
||||
* keys to be loaded.
|
||||
* @param startIndex the starting index of keyfiles string array to use. This
|
||||
* allows us to use the straight command line argument
|
||||
* array.
|
||||
* @param inDirectory the directory to look in (may be null).
|
||||
* @return a list of keypair objects.
|
||||
*/
|
||||
private static List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
||||
private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
||||
File inDirectory) throws IOException {
|
||||
if (keyfiles == null)
|
||||
return new ArrayList<>();
|
||||
|
@ -237,15 +240,14 @@ public class SignKeyset extends CLBase {
|
|||
|
||||
public void execute() throws Exception {
|
||||
// Read in the zone
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null);
|
||||
List<Record> records = ZoneUtils.readZoneFile(inputfile, null);
|
||||
if (records == null || records.isEmpty()) {
|
||||
System.err.println("error: empty keyset file");
|
||||
state.usage();
|
||||
fail("empty keyset file");
|
||||
}
|
||||
|
||||
// Make sure that all records are DNSKEYs with the same name.
|
||||
Name keysetName = null;
|
||||
RRset keyset = new RRset();
|
||||
Name keysetName = null;
|
||||
RRset keyset = new RRset();
|
||||
|
||||
for (Record r : records) {
|
||||
if (r.getType() != Type.DNSKEY) {
|
||||
|
@ -256,46 +258,42 @@ public class SignKeyset extends CLBase {
|
|||
keysetName = r.getName();
|
||||
}
|
||||
if (!r.getName().equals(keysetName)) {
|
||||
System.err.println("error: DNSKEY with a different name found!");
|
||||
state.usage();
|
||||
fail("DNSKEY with a different name found!");
|
||||
}
|
||||
keyset.addRR(r);
|
||||
}
|
||||
|
||||
if (keyset.size() == 0) {
|
||||
System.err.println("error: No DNSKEYs found in keyset file");
|
||||
state.usage();
|
||||
fail("error: No DNSKEYs found in keyset file");
|
||||
}
|
||||
|
||||
// Load the key pairs.
|
||||
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
||||
List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, keyDirectory);
|
||||
|
||||
// If we *still* don't have any key pairs, look for keys the key
|
||||
// directory
|
||||
// that match
|
||||
if (keypairs == null) {
|
||||
keypairs = findZoneKeys(state.keyDirectory, keysetName);
|
||||
keypairs = findZoneKeys(keyDirectory, keysetName);
|
||||
}
|
||||
|
||||
// If there *still* aren't any ZSKs defined, bail.
|
||||
if (keypairs == null || keypairs.isEmpty() || keysetName == null) {
|
||||
System.err.println("error: No signing keys could be determined.");
|
||||
state.usage();
|
||||
return;
|
||||
fail("no signing keys could be determined.");
|
||||
}
|
||||
|
||||
// default the output file, if not set.
|
||||
if (state.outputfile == null) {
|
||||
if (outputfile == null) {
|
||||
if (keysetName.isAbsolute()) {
|
||||
state.outputfile = keysetName + "signed_keyset";
|
||||
outputfile = keysetName + "signed_keyset";
|
||||
} else {
|
||||
state.outputfile = keysetName + ".signed_keyset";
|
||||
outputfile = keysetName + ".signed_keyset";
|
||||
}
|
||||
}
|
||||
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner();
|
||||
|
||||
List<RRSIGRecord> sigs = signer.signRRset(keyset, keypairs, state.start, state.expire);
|
||||
List<RRSIGRecord> sigs = signer.signRRset(keyset, keypairs, start, expire);
|
||||
for (RRSIGRecord s : sigs) {
|
||||
keyset.addRR(s);
|
||||
}
|
||||
|
@ -310,9 +308,9 @@ public class SignKeyset extends CLBase {
|
|||
}
|
||||
|
||||
// write out the signed zone
|
||||
ZoneUtils.writeZoneFile(signedRecords, state.outputfile);
|
||||
ZoneUtils.writeZoneFile(signedRecords, outputfile);
|
||||
|
||||
if (state.verifySigs) {
|
||||
if (verifySigs) {
|
||||
log.fine("verifying generated signatures");
|
||||
boolean res = verifySigs(signedRecords, keypairs);
|
||||
|
||||
|
@ -326,9 +324,8 @@ public class SignKeyset extends CLBase {
|
|||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SignKeyset tool = new SignKeyset();
|
||||
tool.state = new CLIState();
|
||||
SignKeyset tool = new SignKeyset("signkeyset", "jdnssec-signkeyset [..options..] dnskeyset_file [key_file ...]");
|
||||
|
||||
tool.run(tool.state, args);
|
||||
tool.run(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,7 @@ import java.time.Instant;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.xbill.DNS.Name;
|
||||
import org.xbill.DNS.RRSIGRecord;
|
||||
import org.xbill.DNS.RRset;
|
||||
|
@ -49,101 +47,108 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class SignRRset extends CLBase {
|
||||
private CLIState state;
|
||||
private File keyDirectory = null;
|
||||
private String[] keyFiles = null;
|
||||
private Instant start = null;
|
||||
private Instant expire = null;
|
||||
private String inputfile = null;
|
||||
private String outputfile = null;
|
||||
private boolean verifySigs = false;
|
||||
private boolean verboseSigning = false;
|
||||
|
||||
|
||||
public SignRRset(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is an inner class used to hold all of the command line option state.
|
||||
* Set up the command line options.
|
||||
*/
|
||||
protected static class CLIState extends CLIStateBase {
|
||||
private 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 boolean verboseSigning = false;
|
||||
protected void setupOptions() {
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]");
|
||||
}
|
||||
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
|
||||
.desc("directory to find key files (default '.'").build());
|
||||
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
|
||||
.desc("signature starting time (default is now - 1 hour)").build());
|
||||
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
|
||||
.desc("signature expiration time (default is start-time + 30 days)").build());
|
||||
opts.addOption(
|
||||
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the command line options.
|
||||
*/
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
|
||||
protected void processOptions() {
|
||||
String[] verifyOptionKeys = { "verify_signatures", "verify" };
|
||||
String[] verboseSigningOptionKeys = { "verbose_signing" };
|
||||
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
|
||||
String[] inceptionOptionKeys = { "inception", "start" };
|
||||
String[] expireOptionKeys = { "expire" };
|
||||
|
||||
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
|
||||
.desc("directory to find key files (default '.'").build());
|
||||
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
|
||||
.desc("signature starting time (default is now - 1 hour)").build());
|
||||
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
|
||||
.desc("signature expiration time (default is start-time + 30 days)").build());
|
||||
opts.addOption(
|
||||
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
|
||||
}
|
||||
String optstr = null;
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli) throws org.apache.commons.cli.ParseException {
|
||||
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) {
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory()) {
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
}
|
||||
optstr = cliOption("D", keyDirectoryOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory()) {
|
||||
fail("key directory " + optstr + " is not a directory");
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('s')) != null) {
|
||||
start = convertDuration(null, optstr);
|
||||
try {
|
||||
optstr = cliOption("s", inceptionOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
start = Utils.convertDuration(null, optstr);
|
||||
} else {
|
||||
// default is now - 1 hour.
|
||||
start = Instant.now().minusSeconds(3600);
|
||||
}
|
||||
} catch (java.text.ParseException e) {
|
||||
fail("unable to parse start time specifiction: " + e);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('e')) != null) {
|
||||
expire = convertDuration(start, optstr);
|
||||
try {
|
||||
optstr = cliOption("e", expireOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
expire = Utils.convertDuration(start, optstr);
|
||||
} else {
|
||||
expire = convertDuration(start, "+2592000"); // 30 days
|
||||
expire = Utils.convertDuration(start, "+2592000"); // 30 days
|
||||
}
|
||||
} catch (java.text.ParseException e) {
|
||||
fail("Unable to parse expire time specification: " + e);
|
||||
}
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
String[] files = cli.getArgs();
|
||||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 1) {
|
||||
System.err.println("error: missing zone file and/or key files");
|
||||
usage();
|
||||
}
|
||||
if (files.length < 1) {
|
||||
fail("missing zone file and/or key files");
|
||||
}
|
||||
|
||||
inputfile = files[0];
|
||||
if (files.length > 1) {
|
||||
keyFiles = new String[files.length - 1];
|
||||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
}
|
||||
inputfile = files[0];
|
||||
if (files.length > 1) {
|
||||
keyFiles = new String[files.length - 1];
|
||||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify the generated signatures.
|
||||
*
|
||||
* @param records
|
||||
* a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs
|
||||
* a list of keypairs used the sign the zone.
|
||||
* @param records a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs a list of keypairs used the sign the zone.
|
||||
* @return true if all of the signatures validated.
|
||||
*/
|
||||
private static boolean verifySigs(List<Record> records, List<DnsKeyPair> keypairs) {
|
||||
private boolean verifySigs(List<Record> records, List<DnsKeyPair> keypairs) {
|
||||
boolean secure = true;
|
||||
|
||||
DnsSecVerifier verifier = new DnsSecVerifier();
|
||||
|
@ -165,7 +170,7 @@ public class SignRRset extends CLBase {
|
|||
boolean result = verifier.verify(rrset);
|
||||
|
||||
if (!result) {
|
||||
staticLog.fine("Signatures did not verify for RRset: " + rrset);
|
||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
||||
secure = false;
|
||||
}
|
||||
}
|
||||
|
@ -176,18 +181,15 @@ public class SignRRset extends CLBase {
|
|||
/**
|
||||
* Load the key pairs from the key files.
|
||||
*
|
||||
* @param keyfiles
|
||||
* a string array containing the base names or paths of the
|
||||
* keys
|
||||
* to be loaded.
|
||||
* @param startIndex
|
||||
* the starting index of keyfiles string array to use. This
|
||||
* allows us to use the straight command line argument array.
|
||||
* @param inDirectory
|
||||
* the directory to look in (may be null).
|
||||
* @param keyfiles a string array containing the base names or paths of the
|
||||
* keys to be loaded.
|
||||
* @param startIndex the starting index of keyfiles string array to use. This
|
||||
* allows us to use the straight command line argument
|
||||
* array.
|
||||
* @param inDirectory the directory to look in (may be null).
|
||||
* @return a list of keypair objects.
|
||||
*/
|
||||
private static List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
||||
private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
||||
File inDirectory) throws IOException {
|
||||
if (keyfiles == null)
|
||||
return new ArrayList<>();
|
||||
|
@ -209,10 +211,9 @@ public class SignRRset extends CLBase {
|
|||
|
||||
public void execute() throws Exception {
|
||||
// Read in the zone
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null);
|
||||
List<Record> records = ZoneUtils.readZoneFile(inputfile, null);
|
||||
if (records == null || records.isEmpty()) {
|
||||
System.err.println("error: empty RRset file");
|
||||
state.usage();
|
||||
fail("empty RRset file");
|
||||
}
|
||||
// Construct the RRset. Complain if the records in the input file
|
||||
// consist of more than one RRset.
|
||||
|
@ -235,25 +236,21 @@ public class SignRRset extends CLBase {
|
|||
&& rrset.getDClass() == r.getDClass()) {
|
||||
rrset.addRR(r);
|
||||
} else {
|
||||
System.err.println("Records do not all belong to the same RRset.");
|
||||
state.usage();
|
||||
fail("records do not all belong to the same RRset");
|
||||
}
|
||||
}
|
||||
|
||||
if (rrset == null || rrset.size() == 0) {
|
||||
System.err.println("No records found in inputfile.");
|
||||
state.usage();
|
||||
return;
|
||||
fail("no records found in inputfile");
|
||||
}
|
||||
|
||||
// Load the key pairs.
|
||||
|
||||
if (state.keyFiles.length == 0) {
|
||||
System.err.println("error: at least one keyfile must be specified");
|
||||
state.usage();
|
||||
if (keyFiles.length == 0) {
|
||||
fail("at least one keyfile must be specified");
|
||||
}
|
||||
|
||||
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
||||
List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, keyDirectory);
|
||||
|
||||
// Make sure that all the keypairs have the same name.
|
||||
// This will be used as the zone name, too.
|
||||
|
@ -265,19 +262,18 @@ public class SignRRset extends CLBase {
|
|||
continue;
|
||||
}
|
||||
if (!pair.getDNSKEYName().equals(keysetName)) {
|
||||
System.err.println("Keys do not all have the same name.");
|
||||
state.usage();
|
||||
fail("keys do not all have the same name");
|
||||
}
|
||||
}
|
||||
|
||||
// default the output file, if not set.
|
||||
if (state.outputfile == null && !state.inputfile.equals("-")) {
|
||||
state.outputfile = state.inputfile + ".signed";
|
||||
if (outputfile == null && !inputfile.equals("-")) {
|
||||
outputfile = inputfile + ".signed";
|
||||
}
|
||||
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner(state.verboseSigning);
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner(verboseSigning);
|
||||
|
||||
List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, state.start, state.expire);
|
||||
List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, start, expire);
|
||||
for (RRSIGRecord s : sigs) {
|
||||
rrset.addRR(s);
|
||||
}
|
||||
|
@ -292,9 +288,9 @@ public class SignRRset extends CLBase {
|
|||
}
|
||||
|
||||
// write out the signed zone
|
||||
ZoneUtils.writeZoneFile(signedRecords, state.outputfile);
|
||||
ZoneUtils.writeZoneFile(signedRecords, outputfile);
|
||||
|
||||
if (state.verifySigs) {
|
||||
if (verifySigs) {
|
||||
log.fine("verifying generated signatures");
|
||||
boolean res = verifySigs(signedRecords, keypairs);
|
||||
|
||||
|
@ -308,9 +304,8 @@ public class SignRRset extends CLBase {
|
|||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SignRRset tool = new SignRRset();
|
||||
tool.state = new CLIState();
|
||||
SignRRset tool = new SignRRset("signrrset", "jdnssec-signrrset [..options..] rrset_file key_file [key_file ...]");
|
||||
|
||||
tool.run(tool.state, args);
|
||||
tool.run(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
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.DNSSEC;
|
||||
import org.xbill.DNS.Name;
|
||||
|
@ -54,234 +51,237 @@ import com.verisignlabs.dnssec.security.ZoneUtils;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class SignZone extends CLBase {
|
||||
private CLIState state;
|
||||
private File keyDirectory = null;
|
||||
private File keysetDirectory = null;
|
||||
private String[] kskFiles = null;
|
||||
private String[] keyFiles = null;
|
||||
private String zonefile = null;
|
||||
private Instant start = null;
|
||||
private Instant expire = null;
|
||||
private String outputfile = null;
|
||||
private boolean verifySigs = false;
|
||||
private boolean useOptOut = false;
|
||||
private boolean fullySignKeyset = false;
|
||||
private List<Name> includeNames = null;
|
||||
private boolean useNsec3 = false;
|
||||
private byte[] salt = null;
|
||||
private int iterations = 0;
|
||||
private int digestId = DNSSEC.Digest.SHA256;
|
||||
private long nsec3paramttl = -1;
|
||||
private boolean verboseSigning = false;
|
||||
|
||||
/**
|
||||
* This is an inner class used to hold all of the command line option state.
|
||||
*/
|
||||
private static class CLIState extends CLIStateBase {
|
||||
public File keyDirectory = null;
|
||||
public File keysetDirectory = null;
|
||||
public String[] kskFiles = null;
|
||||
public String[] keyFiles = null;
|
||||
public String zonefile = null;
|
||||
public Instant start = null;
|
||||
public Instant expire = null;
|
||||
public String outputfile = null;
|
||||
public boolean verifySigs = false;
|
||||
public boolean useOptOut = false;
|
||||
public boolean fullySignKeyset = false;
|
||||
public List<Name> includeNames = null;
|
||||
public boolean useNsec3 = false;
|
||||
public byte[] salt = null;
|
||||
public int iterations = 0;
|
||||
public int digestId = DNSSEC.Digest.SHA1;
|
||||
public long nsec3paramttl = -1;
|
||||
public boolean verboseSigning = false;
|
||||
private static final Random rand = new Random();
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-signzone [..options..] zone_file [key_file ...]");
|
||||
public SignZone(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
protected void setupOptions() {
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
opts.addOption("F", "fully-sign-keyset", false,
|
||||
"sign the zone apex keyset with all available keys.");
|
||||
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
|
||||
|
||||
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keyset-directory")
|
||||
.desc("directory to find keyset files (default '.')").build());
|
||||
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
|
||||
.desc("directory to find key files (default '.'").build());
|
||||
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
|
||||
.desc("signature starting time (default is now - 1 hour)").build());
|
||||
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
|
||||
.desc("signature expiration time (default is start-time + 30 days)").build());
|
||||
opts.addOption(
|
||||
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
|
||||
opts.addOption(Option.builder("k").hasArgs().argName("KSK file").longOpt("ksk-file")
|
||||
.desc("This key is a Key-Signing Key (may repeat)").build());
|
||||
opts.addOption(Option.builder("I").hasArg().argName("file").longOpt("include-file")
|
||||
.desc("include names in the file in the NSEC/NSEC3 chain").build());
|
||||
|
||||
// NSEC3 options
|
||||
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
|
||||
opts.addOption("O", "use-opt-out", false,
|
||||
"generate a fully Opt-Out zone (only valid with NSEC3).");
|
||||
opts.addOption(
|
||||
Option.builder("S").hasArg().argName("hex value").longOpt("salt").desc("Supply a salt value").build());
|
||||
opts.addOption(Option.builder("R").hasArg().argName("length").longOpt("random-salt")
|
||||
.desc("Generate a random salt of <length>").build());
|
||||
opts.addOption(Option.builder("H").hasArg().argName("count").longOpt("iterations")
|
||||
.desc("Use this many addtional iterations in NSEC3 (default 0)").build());
|
||||
opts.addOption(Option.builder().hasArg().longOpt("nsec3paramttl").argName("ttl")
|
||||
.desc("Use this TTL for the NSEC3PARAM record (default is min(soa.min, soa.ttl))").build());
|
||||
opts.addOption(Option.builder().hasArg().argName("id").longOpt("ds-digest")
|
||||
.desc("Digest algorithm to use for generated DS records").build());
|
||||
}
|
||||
|
||||
protected void processOptions() {
|
||||
String[] verifyOptionKeys = { "verify_signatures", "verify" };
|
||||
String[] nsec3OptionKeys = { "use_nsec3", "nsec3" };
|
||||
String[] optOutOptionKeys = { "use_opt_out", "opt_out" };
|
||||
String[] verboseSigningOptionKeys = { "verbose_signing" };
|
||||
String[] fullySignKeysetOptionKeys = { "fully_sign_keyset", "fully_sign" };
|
||||
String[] keyDirectoryOptionKeys = { "key_directory", "keydir" };
|
||||
String[] inceptionOptionKeys = { "inception", "start" };
|
||||
String[] expireOptionKeys = { "expire" };
|
||||
String[] nsec3SaltOptionKeys = { "nsec3_salt", "salt" };
|
||||
String[] randomSaltOptionKeys = { "nsec3_random_salt_length", "nsec3_salt_length", "random_salt_length" };
|
||||
String[] nsec3IterationsOptionKeys = { "nsec3_iterations", "iterations" };
|
||||
String[] digestAlgOptionKeys = { "digest_algorithm", "digest_id" };
|
||||
String[] nsec3paramTTLOptionKeys = { "nsec3param_ttl" };
|
||||
String[] incudeNamesOptionKeys = { "include_names_file", "include_names" };
|
||||
|
||||
String optstr = null;
|
||||
|
||||
verifySigs = cliBooleanOption("a", verifyOptionKeys, false);
|
||||
useNsec3 = cliBooleanOption("3", nsec3OptionKeys, false);
|
||||
useOptOut = cliBooleanOption("O", optOutOptionKeys, false);
|
||||
verboseSigning = cliBooleanOption("V", verboseSigningOptionKeys, false);
|
||||
|
||||
if (useOptOut && !useNsec3) {
|
||||
System.err.println("Opt-Out not supported without NSEC3 -- ignored.");
|
||||
useOptOut = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
// boolean options
|
||||
opts.addOption("a", "verify", false, "verify generated signatures>");
|
||||
opts.addOption("F", "fully-sign-keyset", false,
|
||||
"sign the zone apex keyset with all available keys.");
|
||||
opts.addOption("V", "verbose-signing", false, "Display verbose signing activity.");
|
||||
|
||||
opts.addOption(Option.builder("d").hasArg().argName("dir").longOpt("keyset-directory")
|
||||
.desc("directory to find keyset files (default '.')").build());
|
||||
opts.addOption(Option.builder("D").hasArg().argName("dir").longOpt("key-directory")
|
||||
.desc("directory to find key files (default '.'").build());
|
||||
opts.addOption(Option.builder("s").hasArg().argName("time/offset").longOpt("start-time")
|
||||
.desc("signature starting time (default is now - 1 hour)").build());
|
||||
opts.addOption(Option.builder("e").hasArg().argName("time/offset").longOpt("expire-time")
|
||||
.desc("signature expiration time (default is start-time + 30 days)").build());
|
||||
opts.addOption(
|
||||
Option.builder("f").hasArg().argName("outfile").desc("file the the signed rrset is written to").build());
|
||||
opts.addOption(Option.builder("k").hasArgs().argName("KSK file").longOpt("ksk-file")
|
||||
.desc("This key is a Key-Signing Key (may repeat)").build());
|
||||
opts.addOption(Option.builder("I").hasArg().argName("file").longOpt("include-file")
|
||||
.desc("include names in the file in the NSEC/NSEC3 chain").build());
|
||||
|
||||
// NSEC3 options
|
||||
opts.addOption("3", "use-nsec3", false, "use NSEC3 instead of NSEC");
|
||||
opts.addOption("O", "use-opt-out", false,
|
||||
"generate a fully Opt-Out zone (only valid with NSEC3).");
|
||||
opts.addOption(
|
||||
Option.builder("S").hasArg().argName("hex value").longOpt("salt").desc("Supply a salt value").build());
|
||||
opts.addOption(Option.builder("R").hasArg().argName("length").longOpt("random-salt")
|
||||
.desc("Generate a random salt of <length>").build());
|
||||
opts.addOption(Option.builder("H").hasArg().argName("count").longOpt("iterations")
|
||||
.desc("Use this many addtional iterations in NSEC3 (default 0)").build());
|
||||
opts.addOption(Option.builder().hasArg().longOpt("nsec3paramttl").argName("ttl")
|
||||
.desc("Use this TTL for the NSEC3PARAM record (default is min(soa.min, soa.ttl))").build());
|
||||
opts.addOption(Option.builder().hasArg().argName("id").longOpt("ds-digest")
|
||||
.desc("Digest algorithm to use for generated DS records").build());
|
||||
fullySignKeyset = cliBooleanOption("F", fullySignKeysetOptionKeys, false);
|
||||
|
||||
optstr = cliOption("D", keyDirectoryOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory()) {
|
||||
fail("key directory " + optstr + " is not a directory");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli) throws ParseException {
|
||||
String optstr = null;
|
||||
|
||||
if (cli.hasOption('a'))
|
||||
verifySigs = true;
|
||||
if (cli.hasOption('3'))
|
||||
useNsec3 = true;
|
||||
if (cli.hasOption('O'))
|
||||
useOptOut = true;
|
||||
if (cli.hasOption('V'))
|
||||
verboseSigning = true;
|
||||
|
||||
if (useOptOut && !useNsec3) {
|
||||
System.err.println("Opt-Out not supported without NSEC3 -- ignored.");
|
||||
useOptOut = false;
|
||||
}
|
||||
|
||||
if (cli.hasOption('F'))
|
||||
fullySignKeyset = true;
|
||||
|
||||
if ((optstr = cli.getOptionValue('d')) != null) {
|
||||
keysetDirectory = new File(optstr);
|
||||
if (!keysetDirectory.isDirectory()) {
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('D')) != null) {
|
||||
keyDirectory = new File(optstr);
|
||||
if (!keyDirectory.isDirectory()) {
|
||||
System.err.println("error: " + optstr + " is not a directory");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('s')) != null) {
|
||||
start = CLBase.convertDuration(null, optstr);
|
||||
try {
|
||||
optstr = cliOption("s", inceptionOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
start = Utils.convertDuration(null, optstr);
|
||||
} else {
|
||||
// default is now - 1 hour.
|
||||
start = Instant.now().minusSeconds(3600);
|
||||
}
|
||||
} catch (java.text.ParseException e) {
|
||||
fail("unable to parse start time specifiction: " + e);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('e')) != null) {
|
||||
expire = CLBase.convertDuration(start, optstr);
|
||||
try {
|
||||
optstr = cliOption("e", expireOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
expire = Utils.convertDuration(start, optstr);
|
||||
} else {
|
||||
expire = CLBase.convertDuration(start, "+2592000"); // 30 days
|
||||
expire = Utils.convertDuration(start, "+2592000"); // 30 days
|
||||
}
|
||||
} catch (java.text.ParseException e) {
|
||||
fail("missing zone file and/or key files");
|
||||
}
|
||||
|
||||
outputfile = cli.getOptionValue('f');
|
||||
outputfile = cli.getOptionValue('f');
|
||||
|
||||
kskFiles = cli.getOptionValues('k');
|
||||
kskFiles = cli.getOptionValues('k');
|
||||
|
||||
if ((optstr = cli.getOptionValue('I')) != null) {
|
||||
File includeNamesFile = new File(optstr);
|
||||
try {
|
||||
includeNames = CLIState.getNameList(includeNamesFile);
|
||||
} catch (IOException e) {
|
||||
throw new ParseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('S')) != null) {
|
||||
salt = base16.fromString(optstr);
|
||||
if (salt == null && !optstr.equals("-")) {
|
||||
System.err.println("error: salt is not valid hexidecimal.");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('R')) != null) {
|
||||
int length = parseInt(optstr, 0);
|
||||
if (length > 0 && length <= 255) {
|
||||
Random random = new Random();
|
||||
salt = new byte[length];
|
||||
random.nextBytes(salt);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("iterations")) != null) {
|
||||
iterations = parseInt(optstr, iterations);
|
||||
if (iterations < 0 || iterations > 8388607) {
|
||||
System.err.println("error: iterations value is invalid");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("ds-digest")) != null) {
|
||||
digestId = parseInt(optstr, -1);
|
||||
if (digestId < 0) {
|
||||
System.err.println("error: DS digest ID is not a valid identifier");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue("nsec3paramttl")) != null) {
|
||||
nsec3paramttl = parseInt(optstr, -1);
|
||||
}
|
||||
|
||||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 1) {
|
||||
System.err.println("error: missing zone file and/or key files");
|
||||
usage();
|
||||
}
|
||||
|
||||
zonefile = files[0];
|
||||
if (files.length > 1) {
|
||||
keyFiles = new String[files.length - 1];
|
||||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
optstr = cliOption("S", nsec3SaltOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
salt = base16.fromString(optstr);
|
||||
if (salt == null && !optstr.equals("-")) {
|
||||
fail("salt is not valid hexidecimal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of DNS names from a file.
|
||||
*
|
||||
* @param nameListFile
|
||||
* the path of a file containing a bare list of DNS names.
|
||||
* @return a list of {@link org.xbill.DNS.Name} objects.
|
||||
*/
|
||||
private static List<Name> getNameList(File nameListFile) throws IOException {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(nameListFile))) {
|
||||
List<Name> res = new ArrayList<>();
|
||||
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
try {
|
||||
Name n = Name.fromString(line);
|
||||
// force the name to be absolute.
|
||||
// FIXME: we should probably get some fancy logic here to
|
||||
// detect if the name needs the origin appended, or just the
|
||||
// root.
|
||||
if (!n.isAbsolute())
|
||||
n = Name.concatenate(n, Name.root);
|
||||
|
||||
res.add(n);
|
||||
} catch (TextParseException e) {
|
||||
staticLog.severe("DNS Name parsing error:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
optstr = cliOption("R", randomSaltOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
int length = Utils.parseInt(optstr, 0);
|
||||
if (length > 0 && length <= 255) {
|
||||
salt = new byte[length];
|
||||
rand.nextBytes(salt);
|
||||
}
|
||||
}
|
||||
|
||||
iterations = cliIntOption("iterations", nsec3IterationsOptionKeys, 0);
|
||||
if (iterations > 150) {
|
||||
log.warning("NSEC3 iterations value is too high for normal use: " + iterations
|
||||
+ " is greater than current accepted threshold of 150");
|
||||
}
|
||||
|
||||
optstr = cliOption("ds-digest", digestAlgOptionKeys, Integer.toString(digestId));
|
||||
digestId = DNSSEC.Digest.value(optstr);
|
||||
|
||||
nsec3paramttl = cliIntOption("nsec3paramttl", nsec3paramTTLOptionKeys, -1);
|
||||
|
||||
optstr = cliOption("I", incudeNamesOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
File includeNamesFile = new File(optstr);
|
||||
try {
|
||||
includeNames = getNameList(includeNamesFile);
|
||||
} catch (IOException e) {
|
||||
fail("unable to load include-names file: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
String[] files = cli.getArgs();
|
||||
|
||||
if (files.length < 1) {
|
||||
fail("missing zone file and/or key files");
|
||||
}
|
||||
|
||||
zonefile = files[0];
|
||||
if (files.length > 1) {
|
||||
keyFiles = new String[files.length - 1];
|
||||
System.arraycopy(files, 1, keyFiles, 0, files.length - 1);
|
||||
}
|
||||
|
||||
log.fine("SignZone settings => key_directory: " + keyDirectory +
|
||||
", keyset_directory: " + keysetDirectory +
|
||||
", start: " + start.getEpochSecond() +
|
||||
", expire: " + expire.getEpochSecond() +
|
||||
", verify_sigs: " + verifySigs +
|
||||
", use_nsec3: " + useNsec3 +
|
||||
", use_opt_out = " + useOptOut +
|
||||
", salt: " + DnsKeyPair.toHex(salt) +
|
||||
", iterations: " + iterations +
|
||||
", nsec3param_ttl: " + nsec3paramttl +
|
||||
", fully_sign_keyset: " + fullySignKeyset +
|
||||
", digest_id: " + digestId +
|
||||
", verbose_signing: " + verboseSigning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of DNS names from a file.
|
||||
*
|
||||
* @param nameListFile the path of a file containing a bare list of DNS
|
||||
* names.
|
||||
* @return a list of {@link org.xbill.DNS.Name} objects.
|
||||
*/
|
||||
private List<Name> getNameList(File nameListFile) throws IOException {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(nameListFile))) {
|
||||
List<Name> res = new ArrayList<>();
|
||||
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
try {
|
||||
Name n = Name.fromString(line);
|
||||
// force the name to be absolute.
|
||||
if (!n.isAbsolute())
|
||||
n = Name.concatenate(n, Name.root);
|
||||
|
||||
res.add(n);
|
||||
} catch (TextParseException e) {
|
||||
log.severe("DNS Name parsing error:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the generated signatures.
|
||||
*
|
||||
* @param records
|
||||
* a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs
|
||||
* a list of keypairs used the sign the zone.
|
||||
* @param records a list of {@link org.xbill.DNS.Record}s.
|
||||
* @param keypairs a list of keypairs used the sign the zone.
|
||||
* @return true if all of the signatures validated.
|
||||
*/
|
||||
private static boolean verifyZoneSigs(List<Record> records,
|
||||
List<DnsKeyPair> keypairs) {
|
||||
private boolean verifyZoneSigs(List<Record> records,
|
||||
List<DnsKeyPair> keypairs, List<DnsKeyPair> kskpairs) {
|
||||
boolean secure = true;
|
||||
|
||||
DnsSecVerifier verifier = new DnsSecVerifier();
|
||||
|
@ -289,7 +289,9 @@ public class SignZone extends CLBase {
|
|||
for (DnsKeyPair pair : keypairs) {
|
||||
verifier.addTrustedKey(pair);
|
||||
}
|
||||
|
||||
for (DnsKeyPair pair : kskpairs) {
|
||||
verifier.addTrustedKey(pair);
|
||||
}
|
||||
verifier.setVerifyAllSigs(true);
|
||||
|
||||
List<RRset> rrsets = SignUtils.assembleIntoRRsets(records);
|
||||
|
@ -303,7 +305,8 @@ public class SignZone extends CLBase {
|
|||
boolean result = verifier.verify(rrset);
|
||||
|
||||
if (!result) {
|
||||
staticLog.fine("Signatures did not verify for RRset: " + rrset);
|
||||
System.err.println("Signatures did not verify for RRset: " + rrset);
|
||||
log.fine("Signatures did not verify for RRset: " + rrset);
|
||||
secure = false;
|
||||
}
|
||||
}
|
||||
|
@ -314,20 +317,15 @@ public class SignZone extends CLBase {
|
|||
/**
|
||||
* Load the key pairs from the key files.
|
||||
*
|
||||
* @param keyfiles
|
||||
* a string array containing the base names or paths of the
|
||||
* keys to
|
||||
* be loaded.
|
||||
* @param startIndex
|
||||
* the starting index of keyfiles string array to use. This
|
||||
* allows
|
||||
* us
|
||||
* to use the straight command line argument array.
|
||||
* @param inDirectory
|
||||
* the directory to look in (may be null).
|
||||
* @param keyfiles a string array containing the base names or paths of the
|
||||
* keys to be loaded.
|
||||
* @param startIndex the starting index of keyfiles string array to use. This
|
||||
* allows us to use the straight command line argument
|
||||
* array.
|
||||
* @param inDirectory the directory to look in (may be null).
|
||||
* @return a list of keypair objects.
|
||||
*/
|
||||
private static List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
||||
private List<DnsKeyPair> getKeys(String[] keyfiles, int startIndex,
|
||||
File inDirectory) throws IOException {
|
||||
if (keyfiles == null)
|
||||
return new ArrayList<>();
|
||||
|
@ -348,7 +346,7 @@ public class SignZone extends CLBase {
|
|||
return keys;
|
||||
}
|
||||
|
||||
private static List<DnsKeyPair> getKeys(List<Record> dnskeyrrs, File inDirectory)
|
||||
private List<DnsKeyPair> getKeys(List<Record> dnskeyrrs, File inDirectory)
|
||||
throws IOException {
|
||||
List<DnsKeyPair> res = new ArrayList<>();
|
||||
for (Record r : dnskeyrrs) {
|
||||
|
@ -382,7 +380,7 @@ public class SignZone extends CLBase {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename)
|
||||
private List<DnsKeyPair> findZoneKeys(File inDirectory, Name zonename)
|
||||
throws IOException {
|
||||
if (inDirectory == null) {
|
||||
inDirectory = new File(".");
|
||||
|
@ -418,21 +416,14 @@ public class SignZone extends CLBase {
|
|||
/**
|
||||
* Load keysets (which contain delegation point security info).
|
||||
*
|
||||
* @param inDirectory
|
||||
* the directory to look for the keyset files (may be null,
|
||||
* in
|
||||
* which
|
||||
* case it defaults to looking in the current working
|
||||
* directory).
|
||||
* @param zonename
|
||||
* the name of the zone we are signing, so we can ignore
|
||||
* keysets
|
||||
* that
|
||||
* do not belong in the zone.
|
||||
* @return a list of {@link org.xbill.DNS.Record}s found in the keyset
|
||||
* files.
|
||||
* @param inDirectory the directory to look for the keyset files (may be null,
|
||||
* in which case it defaults to looking in the current
|
||||
* working directory).
|
||||
* @param zonename the name of the zone we are signing, so we can ignore
|
||||
* keysets that do not belong in the zone.
|
||||
* @return a list of {@link org.xbill.DNS.Record}s found in the keyset files.
|
||||
*/
|
||||
private static List<Record> getKeysets(File inDirectory, Name zonename)
|
||||
private List<Record> getKeysets(File inDirectory, Name zonename)
|
||||
throws IOException {
|
||||
if (inDirectory == null) {
|
||||
inDirectory = new File(".");
|
||||
|
@ -463,13 +454,9 @@ public class SignZone extends CLBase {
|
|||
/**
|
||||
* Determine if the given keypairs can be used to sign the zone.
|
||||
*
|
||||
* @param zonename
|
||||
* the zone origin.
|
||||
* @param keypairs
|
||||
* a list of {@link DnsKeyPair} objects that will be used to
|
||||
* sign
|
||||
* the
|
||||
* zone.
|
||||
* @param zonename the zone origin.
|
||||
* @param keypairs a list of {@link DnsKeyPair} objects that will be used to
|
||||
* sign the zone.
|
||||
* @return true if the keypairs valid.
|
||||
*/
|
||||
private static boolean keyPairsValidForZone(Name zonename, List<DnsKeyPair> keypairs) {
|
||||
|
@ -487,136 +474,149 @@ public class SignZone extends CLBase {
|
|||
}
|
||||
|
||||
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
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null);
|
||||
List<Record> records = ZoneUtils.readZoneFile(zonefile, null);
|
||||
if (records == null || records.isEmpty()) {
|
||||
System.err.println("error: empty zone file");
|
||||
state.usage();
|
||||
return;
|
||||
fail("empty zone file");
|
||||
}
|
||||
|
||||
// calculate the zone name.
|
||||
Name zonename = ZoneUtils.findZoneName(records);
|
||||
if (zonename == null) {
|
||||
System.err.println("error: invalid zone file - no SOA");
|
||||
state.usage();
|
||||
return;
|
||||
fail("invalid zone file - no SOA");
|
||||
}
|
||||
|
||||
// Load the key pairs.
|
||||
|
||||
List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory);
|
||||
List<DnsKeyPair> kskpairs = getKeys(state.kskFiles, 0, state.keyDirectory);
|
||||
// Load the key pairs. Note that getKeys() always returns an ArrayList,
|
||||
// which may be empty.
|
||||
List<DnsKeyPair> keypairs = getKeys(keyFiles, 0, 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
|
||||
// any public keys.
|
||||
if (keypairs == null && kskpairs == null) {
|
||||
if (keypairs.isEmpty()) {
|
||||
List<Record> dnskeys = ZoneUtils.findRRs(records, zonename, Type.DNSKEY);
|
||||
keypairs = getKeys(dnskeys, state.keyDirectory);
|
||||
keypairs = getKeys(dnskeys, keyDirectory);
|
||||
}
|
||||
|
||||
// If we *still* don't have any key pairs, look for keys the key directory
|
||||
// that match
|
||||
if (keypairs == null && kskpairs == null) {
|
||||
keypairs = findZoneKeys(state.keyDirectory, zonename);
|
||||
if (keypairs.isEmpty()) {
|
||||
keypairs = findZoneKeys(keyDirectory, zonename);
|
||||
}
|
||||
|
||||
// If we don't have any KSKs, but we do have more than one zone
|
||||
// signing key (presumably), presume that the zone signing keys
|
||||
// are just not differentiated and try to figure out which keys
|
||||
// are actually ksks by looking at the SEP flag.
|
||||
if ((kskpairs == null || kskpairs.isEmpty()) && keypairs != null
|
||||
&& keypairs.size() > 1) {
|
||||
// are actually KSKs by looking at the SEP flag.
|
||||
if (kskpairs.isEmpty() && !keypairs.isEmpty()) {
|
||||
for (Iterator<DnsKeyPair> i = keypairs.iterator(); i.hasNext();) {
|
||||
DnsKeyPair pair = i.next();
|
||||
DNSKEYRecord kr = pair.getDNSKEYRecord();
|
||||
if ((kr.getFlags() & DNSKEYRecord.Flags.SEP_KEY) != 0) {
|
||||
if (kskpairs == null)
|
||||
kskpairs = new ArrayList<>();
|
||||
kskpairs.add(pair);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no ZSKs defined at this point (yet there are KSKs
|
||||
// provided), all KSKs will be treated as ZSKs, as well.
|
||||
if (keypairs == null || keypairs.isEmpty()) {
|
||||
keypairs = kskpairs;
|
||||
// If we have zero keypairs at all, we are stuck.
|
||||
if (keypairs.isEmpty() && kskpairs.isEmpty()) {
|
||||
fail("no zone signing keys could be determined");
|
||||
}
|
||||
|
||||
// If there *still* aren't any ZSKs defined, bail.
|
||||
if (keypairs == null || keypairs.isEmpty()) {
|
||||
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
|
||||
// "CSKs" -- Combined signing keys, so assign one set to the other.
|
||||
if (keypairs.isEmpty()) {
|
||||
keypairs = kskpairs;
|
||||
} else if (kskpairs.isEmpty()) {
|
||||
kskpairs = keypairs;
|
||||
}
|
||||
|
||||
// Output what keys we are using for what
|
||||
if (keypairs == kskpairs) {
|
||||
System.out.println("CSKs: ");
|
||||
for (DnsKeyPair kp : keypairs) {
|
||||
System.out.println(" - " + kp);
|
||||
}
|
||||
} else {
|
||||
System.out.println("KSKs: ");
|
||||
for (DnsKeyPair kp : kskpairs) {
|
||||
System.out.println(" - " + kp);
|
||||
}
|
||||
System.out.println("ZSKs: ");
|
||||
for (DnsKeyPair kp : keypairs) {
|
||||
System.out.println(" - " + kp);
|
||||
}
|
||||
}
|
||||
|
||||
// default the output file, if not set.
|
||||
if (state.outputfile == null && !state.zonefile.equals("-")) {
|
||||
if (outputfile == null && !zonefile.equals("-")) {
|
||||
if (zonename.isAbsolute()) {
|
||||
state.outputfile = zonename + "signed";
|
||||
outputfile = zonename + "signed";
|
||||
} else {
|
||||
state.outputfile = zonename + ".signed";
|
||||
outputfile = zonename + ".signed";
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the keys can be in the zone.
|
||||
if (!keyPairsValidForZone(zonename, keypairs)
|
||||
|| !keyPairsValidForZone(zonename, kskpairs)) {
|
||||
System.err.println("error: specified keypairs are not valid for the zone.");
|
||||
state.usage();
|
||||
fail("specified keypairs are not valid for the zone.");
|
||||
}
|
||||
|
||||
// We force the signing keys to be in the zone by just appending
|
||||
// them to the zone here. Currently JCEDnsSecSigner.signZone
|
||||
// removes duplicate records.
|
||||
if (kskpairs != null) {
|
||||
if (!kskpairs.isEmpty()) {
|
||||
for (DnsKeyPair pair : kskpairs) {
|
||||
records.add(pair.getDNSKEYRecord());
|
||||
}
|
||||
}
|
||||
if (keypairs != null) {
|
||||
if (!keypairs.isEmpty()) {
|
||||
for (DnsKeyPair pair : keypairs) {
|
||||
records.add(pair.getDNSKEYRecord());
|
||||
}
|
||||
}
|
||||
|
||||
// read in the keysets, if any.
|
||||
List<Record> keysetrecs = getKeysets(state.keysetDirectory, zonename);
|
||||
List<Record> keysetrecs = getKeysets(keysetDirectory, zonename);
|
||||
if (keysetrecs != null) {
|
||||
records.addAll(keysetrecs);
|
||||
}
|
||||
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner(state.verboseSigning);
|
||||
JCEDnsSecSigner signer = new JCEDnsSecSigner(verboseSigning);
|
||||
|
||||
// Sign the zone.
|
||||
List<Record> signedRecords;
|
||||
|
||||
if (state.useNsec3) {
|
||||
if (useNsec3) {
|
||||
signedRecords = signer.signZoneNSEC3(zonename, records, kskpairs, keypairs,
|
||||
state.start, state.expire,
|
||||
state.fullySignKeyset, state.useOptOut,
|
||||
state.includeNames, state.salt,
|
||||
state.iterations, state.digestId,
|
||||
state.nsec3paramttl);
|
||||
start, expire,
|
||||
fullySignKeyset, useOptOut,
|
||||
includeNames, salt,
|
||||
iterations, digestId,
|
||||
nsec3paramttl);
|
||||
} else {
|
||||
signedRecords = signer.signZone(zonename, records, kskpairs, keypairs,
|
||||
state.start, state.expire, state.fullySignKeyset,
|
||||
state.digestId);
|
||||
start, expire, fullySignKeyset,
|
||||
digestId);
|
||||
}
|
||||
|
||||
// write out the signed zone
|
||||
ZoneUtils.writeZoneFile(signedRecords, state.outputfile);
|
||||
|
||||
if (state.verifySigs) {
|
||||
// FIXME: ugh.
|
||||
if (kskpairs != null) {
|
||||
keypairs.addAll(kskpairs);
|
||||
}
|
||||
ZoneUtils.writeZoneFile(signedRecords, outputfile);
|
||||
System.out.println("zone signing complete");
|
||||
|
||||
if (verifySigs) {
|
||||
log.fine("verifying generated signatures");
|
||||
boolean res = verifyZoneSigs(signedRecords, keypairs);
|
||||
boolean res = verifyZoneSigs(signedRecords, keypairs, kskpairs);
|
||||
|
||||
if (res) {
|
||||
System.out.println("Generated signatures verified");
|
||||
|
@ -624,13 +624,11 @@ public class SignZone extends CLBase {
|
|||
System.out.println("Generated signatures did not verify.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SignZone tool = new SignZone();
|
||||
tool.state = new CLIState();
|
||||
SignZone tool = new SignZone("signzone", "jdnssec-signzone [..options..] zone_file [key_file ...]");
|
||||
|
||||
tool.run(tool.state, args);
|
||||
tool.run(args);
|
||||
}
|
||||
}
|
||||
|
|
103
src/main/java/com/verisignlabs/dnssec/cl/Utils.java
Normal file
103
src/main/java/com/verisignlabs/dnssec/cl/Utils.java
Normal file
|
@ -0,0 +1,103 @@
|
|||
package com.verisignlabs.dnssec.cl;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.verisignlabs.dnssec.security.DnsKeyAlgorithm;
|
||||
|
||||
public class Utils {
|
||||
|
||||
private Utils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string into an integer safely, using a default if the value does not
|
||||
* parse cleanly
|
||||
*
|
||||
* @param s The string to parse
|
||||
* @param def The default value
|
||||
* @return either the parsed int or the default
|
||||
*/
|
||||
public static int parseInt(String s, int def) {
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string into a long safely, using a default if the value does not
|
||||
* parse cleanly
|
||||
*
|
||||
* @param s The string to parse
|
||||
* @param def The default value
|
||||
* @return either the parsed long or the default
|
||||
*/
|
||||
public static long parseLong(String s, long def) {
|
||||
try {
|
||||
return Long.parseLong(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a DNSSEC algorithm number of mnemonic into the official algorithm number.
|
||||
* @param s The arge value
|
||||
* @return A DNSSEC algorithm number, or -1 if unrecognized.
|
||||
*/
|
||||
public static int parseAlg(String s) {
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
|
||||
int alg = Utils.parseInt(s, -1);
|
||||
if (alg > 0) {
|
||||
if (algs.supportedAlgorithm(alg))
|
||||
return alg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return algs.stringToAlgorithm(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a date/time from a command line time/offset duration string.
|
||||
*
|
||||
* @param start the start time to calculate offsets from.
|
||||
* @param duration the time/offset string to parse.
|
||||
* @return the calculated time.
|
||||
*/
|
||||
public static Instant convertDuration(Instant start, String duration) throws java.text.ParseException {
|
||||
if (start == null) {
|
||||
start = Instant.now();
|
||||
}
|
||||
|
||||
if (duration.startsWith("now")) {
|
||||
start = Instant.now();
|
||||
if (duration.indexOf("+") < 0)
|
||||
return start;
|
||||
|
||||
duration = duration.substring(3);
|
||||
}
|
||||
|
||||
if (duration.startsWith("+")) {
|
||||
long offset = parseLong(duration.substring(1), 0);
|
||||
return start.plusSeconds(offset);
|
||||
}
|
||||
|
||||
// This is a heuristic to distinguish UNIX epoch times from the zone file
|
||||
// format standard (which is length == 14)
|
||||
if (duration.length() <= 10) {
|
||||
long epoch = parseLong(duration, 0);
|
||||
return Instant.ofEpochSecond(epoch);
|
||||
}
|
||||
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
Date parsedDate = dateFormatter.parse(duration);
|
||||
return parsedDate.toInstant();
|
||||
}
|
||||
|
||||
}
|
|
@ -20,10 +20,7 @@ package com.verisignlabs.dnssec.cl;
|
|||
import java.time.Instant;
|
||||
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.ParseException;
|
||||
import org.xbill.DNS.Record;
|
||||
|
||||
import com.verisignlabs.dnssec.security.ZoneUtils;
|
||||
|
@ -35,100 +32,75 @@ import com.verisignlabs.dnssec.security.ZoneVerifier;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class VerifyZone extends CLBase {
|
||||
private String zonefile = null;
|
||||
private String[] keyfiles = null;
|
||||
private int startfudge = 0;
|
||||
private int expirefudge = 0;
|
||||
private boolean ignoreTime = false;
|
||||
private boolean ignoreDups = false;
|
||||
private Instant currentTime = null;
|
||||
|
||||
private CLIState state;
|
||||
public VerifyZone(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
protected void setupOptions() {
|
||||
opts.addOption(Option.builder("S").hasArg().argName("seconds").longOpt("sig-start-fudge")
|
||||
.desc("'fudge' RRSIG inception ties by 'seconds'").build());
|
||||
opts.addOption(Option.builder("E").hasArg().argName("seconds").longOpt("sig-expire-fudge")
|
||||
.desc("'fudge' RRSIG expiration times by 'seconds'").build());
|
||||
opts.addOption(Option.builder("t").hasArg().argName("time").longOpt("use-time")
|
||||
.desc("Use 'time' as the time for verification purposes.").build());
|
||||
|
||||
public CLIState() {
|
||||
super("jdnssec-verifyzone [..options..] zonefile");
|
||||
opts.addOption(
|
||||
Option.builder().longOpt("ignore-time").desc("Ignore RRSIG inception and expiration time errors.").build());
|
||||
opts.addOption(Option.builder().longOpt("ignore-duplicate-rrs").desc("Ignore duplicate record errors.").build());
|
||||
}
|
||||
|
||||
protected void processOptions() {
|
||||
String[] ignoreTimeOptionKeys = { "ignore_time" };
|
||||
String[] ignoreDuplicateOptionKeys = { "ingore_duplicate_rrs", "ignore_duplicates" };
|
||||
String[] startFudgeOptionKeys = { "start_fudge" };
|
||||
String[] expireFudgeOptionKeys = { "expire_fudge" };
|
||||
String[] currentTimeOptionKeys = { "current_time" };
|
||||
|
||||
ignoreTime = cliBooleanOption("ignore-time", ignoreTimeOptionKeys, false);
|
||||
ignoreDups = cliBooleanOption("ignore-duplicate-rrs", ignoreDuplicateOptionKeys, false);
|
||||
startfudge = cliIntOption("S", startFudgeOptionKeys, 0);
|
||||
expirefudge = cliIntOption("E", expireFudgeOptionKeys, 0);
|
||||
|
||||
String optstr = cliOption("t", currentTimeOptionKeys, null);
|
||||
if (optstr != null) {
|
||||
try {
|
||||
currentTime = Utils.convertDuration(null, optstr);
|
||||
} catch (java.text.ParseException e) {
|
||||
fail("could not parse timespec");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
opts.addOption(Option.builder("S").hasArg().argName("seconds").longOpt("sig-start-fudge")
|
||||
.desc("'fudge' RRSIG inception ties by 'seconds'").build());
|
||||
opts.addOption(Option.builder("E").hasArg().argName("seconds").longOpt("sig-expire-fudge")
|
||||
.desc("'fudge' RRSIG expiration times by 'seconds'").build());
|
||||
opts.addOption(Option.builder("t").hasArg().argName("time").longOpt("use-time")
|
||||
.desc("Use 'time' as the time for verification purposes.").build());
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
opts.addOption(
|
||||
Option.builder().longOpt("ignore-time").desc("Ignore RRSIG inception and expiration time errors.").build());
|
||||
opts.addOption(Option.builder().longOpt("ignore-duplicate-rrs").desc("Ignore duplicate record errors.").build());
|
||||
if (args.length < 1) {
|
||||
fail("missing zone file");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli) {
|
||||
if (cli.hasOption("ignore-time")) {
|
||||
ignoreTime = true;
|
||||
}
|
||||
zonefile = args[0];
|
||||
|
||||
if (cli.hasOption("ignore-duplicate-rrs")) {
|
||||
ignoreDups = true;
|
||||
}
|
||||
|
||||
String optstr = null;
|
||||
if ((optstr = cli.getOptionValue('S')) != null) {
|
||||
startfudge = parseInt(optstr, 0);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('E')) != null) {
|
||||
expirefudge = parseInt(optstr, 0);
|
||||
}
|
||||
|
||||
if ((optstr = cli.getOptionValue('t')) != null) {
|
||||
try {
|
||||
currentTime = convertDuration(null, optstr);
|
||||
} catch (ParseException e) {
|
||||
System.err.println("error: could not parse timespec");
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
String[] optstrs = null;
|
||||
if ((optstrs = cli.getOptionValues('A')) != null) {
|
||||
for (int i = 0; i < optstrs.length; i++) {
|
||||
addArgAlias(optstrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
System.err.println("error: missing zone file");
|
||||
usage();
|
||||
}
|
||||
|
||||
zonefile = args[0];
|
||||
|
||||
if (args.length >= 2) {
|
||||
keyfiles = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, keyfiles, 0, keyfiles.length);
|
||||
}
|
||||
if (args.length >= 2) {
|
||||
keyfiles = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, keyfiles, 0, keyfiles.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void execute() throws Exception {
|
||||
ZoneVerifier zoneverifier = new ZoneVerifier();
|
||||
zoneverifier.getVerifier().setStartFudge(state.startfudge);
|
||||
zoneverifier.getVerifier().setExpireFudge(state.expirefudge);
|
||||
zoneverifier.getVerifier().setIgnoreTime(state.ignoreTime);
|
||||
zoneverifier.getVerifier().setCurrentTime(state.currentTime);
|
||||
zoneverifier.setIgnoreDuplicateRRs(state.ignoreDups);
|
||||
zoneverifier.getVerifier().setStartFudge(startfudge);
|
||||
zoneverifier.getVerifier().setExpireFudge(expirefudge);
|
||||
zoneverifier.getVerifier().setIgnoreTime(ignoreTime);
|
||||
zoneverifier.getVerifier().setCurrentTime(currentTime);
|
||||
zoneverifier.setIgnoreDuplicateRRs(ignoreDups);
|
||||
|
||||
List<Record> records = ZoneUtils.readZoneFile(state.zonefile, null);
|
||||
List<Record> records = ZoneUtils.readZoneFile(zonefile, null);
|
||||
|
||||
log.fine("verifying zone...");
|
||||
int errors = zoneverifier.verifyZone(records);
|
||||
|
@ -144,9 +116,8 @@ public class VerifyZone extends CLBase {
|
|||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
VerifyZone tool = new VerifyZone();
|
||||
tool.state = new CLIState();
|
||||
VerifyZone tool = new VerifyZone("verifyzone", "jdnssec-verifyzone [..options..] zonefile");
|
||||
|
||||
tool.run(tool.state, args);
|
||||
tool.run(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,6 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
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.NSEC3PARAMRecord;
|
||||
import org.xbill.DNS.NSEC3Record;
|
||||
|
@ -45,43 +42,33 @@ import com.verisignlabs.dnssec.security.RecordComparator;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class ZoneFormat extends CLBase {
|
||||
private CLIState state;
|
||||
private String file;
|
||||
private boolean assignNSEC3;
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupOptions(Options opts) {
|
||||
opts.addOption("N", "nsec3", false,
|
||||
"attempt to determine the original ownernames for NSEC3 RRs.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processOptions(CommandLine cli) throws ParseException {
|
||||
if (cli.hasOption('N'))
|
||||
assignNSEC3 = true;
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
System.err.println("error: must specify a zone file");
|
||||
usage();
|
||||
}
|
||||
|
||||
file = args[0];
|
||||
}
|
||||
public ZoneFormat(String name, String usageStr) {
|
||||
super(name, usageStr);
|
||||
}
|
||||
|
||||
private static List<Record> readZoneFile(String filename) throws IOException {
|
||||
protected void setupOptions() {
|
||||
opts.addOption("N", "nsec3", false,
|
||||
"attempt to determine the original ownernames for NSEC3 RRs.");
|
||||
}
|
||||
|
||||
protected void processOptions() {
|
||||
String[] assignNsec3OwnersOptionKeys = { "assign_nsec3_owners", "assign_owners" };
|
||||
|
||||
assignNSEC3 = cliBooleanOption("N", assignNsec3OwnersOptionKeys, false);
|
||||
|
||||
String[] args = cli.getArgs();
|
||||
|
||||
if (args.length < 1) {
|
||||
fail("must specify a zone file");
|
||||
}
|
||||
|
||||
file = args[0];
|
||||
}
|
||||
|
||||
private List<Record> readZoneFile(String filename) throws IOException {
|
||||
try (Master master = new Master(filename)) {
|
||||
List<Record> res = new ArrayList<>();
|
||||
Record r = null;
|
||||
|
@ -98,15 +85,14 @@ public class ZoneFormat extends CLBase {
|
|||
}
|
||||
}
|
||||
|
||||
private static void formatZone(List<Record> zone) {
|
||||
|
||||
private void formatZone(List<Record> zone) {
|
||||
|
||||
for (Record r : zone) {
|
||||
System.out.println(r.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void determineNSEC3Owners(List<Record> zone)
|
||||
private void determineNSEC3Owners(List<Record> zone)
|
||||
throws NoSuchAlgorithmException {
|
||||
|
||||
// first, find the NSEC3PARAM record -- this is an inefficient linear
|
||||
|
@ -174,11 +160,11 @@ public class ZoneFormat extends CLBase {
|
|||
}
|
||||
|
||||
public void execute() throws IOException, NoSuchAlgorithmException {
|
||||
List<Record> z = readZoneFile(state.file);
|
||||
List<Record> z = readZoneFile(file);
|
||||
// Put the zone into a consistent (name and RR type) order.
|
||||
Collections.sort(z, new RecordComparator());
|
||||
|
||||
if (state.assignNSEC3) {
|
||||
if (assignNSEC3) {
|
||||
determineNSEC3Owners(z);
|
||||
} else {
|
||||
formatZone(z);
|
||||
|
@ -186,10 +172,9 @@ public class ZoneFormat extends CLBase {
|
|||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
ZoneFormat tool = new ZoneFormat();
|
||||
tool.state = new CLIState();
|
||||
ZoneFormat tool = new ZoneFormat("zoneformat", "jdnssec-zoneformat [..options..] zonefile");
|
||||
|
||||
tool.run(tool.state, args);
|
||||
tool.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,7 +46,8 @@ import org.xbill.DNS.utils.base64;
|
|||
*/
|
||||
public class BINDKeyUtils {
|
||||
|
||||
private BINDKeyUtils() { }
|
||||
private BINDKeyUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the BIND9 key file base name (i.e., without the ".key" or
|
||||
|
@ -60,18 +61,18 @@ public class BINDKeyUtils {
|
|||
/** Reads in the DNSKEYRecord from the public key file */
|
||||
private static DNSKEYRecord loadPublicKeyFile(File publicKeyFile)
|
||||
throws IOException {
|
||||
Master m = new Master(publicKeyFile.getAbsolutePath(), null, 600);
|
||||
try (Master m = new Master(publicKeyFile.getAbsolutePath(), null, 600)) {
|
||||
Record r;
|
||||
DNSKEYRecord result = null;
|
||||
|
||||
Record r;
|
||||
DNSKEYRecord result = null;
|
||||
|
||||
while ((r = m.nextRecord()) != null) {
|
||||
if (r.getType() == Type.DNSKEY) {
|
||||
result = (DNSKEYRecord) r;
|
||||
while ((r = m.nextRecord()) != null) {
|
||||
if (r.getType() == Type.DNSKEY) {
|
||||
result = (DNSKEYRecord) r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads in the private key verbatim from the private key file */
|
||||
|
@ -106,17 +107,12 @@ public class BINDKeyUtils {
|
|||
* Given the information necessary to construct the path to a BIND9 generated
|
||||
* key pair, load the key pair.
|
||||
*
|
||||
* @param signer
|
||||
* the DNS name of the key.
|
||||
* @param algorithm
|
||||
* the DNSSEC algorithm of the key.
|
||||
* @param keyid
|
||||
* the DNSSEC key footprint.
|
||||
* @param inDirectory
|
||||
* the directory to look for the files (may be null).
|
||||
* @param signer the DNS name of the key.
|
||||
* @param algorithm the DNSSEC algorithm of the key.
|
||||
* @param keyid the DNSSEC key footprint.
|
||||
* @param inDirectory the directory to look for the files (may be null).
|
||||
* @return the loaded key pair.
|
||||
* @throws IOException
|
||||
* if there was a problem reading the BIND9 files.
|
||||
* @throws IOException if there was a problem reading the BIND9 files.
|
||||
*/
|
||||
public static DnsKeyPair loadKeyPair(Name signer, int algorithm, int keyid,
|
||||
File inDirectory) throws IOException {
|
||||
|
@ -128,22 +124,17 @@ public class BINDKeyUtils {
|
|||
/**
|
||||
* Given a base path to a BIND9 key pair, load the key pair.
|
||||
*
|
||||
* @param keyFileBasePath
|
||||
* the base filename (or real filename for either the
|
||||
* public or
|
||||
* private key) of the key.
|
||||
* @param inDirectory
|
||||
* the directory to look in, if the keyFileBasePath is
|
||||
* @param keyFileBasePath the base filename (or real filename for either the
|
||||
* public or private key) of the key.
|
||||
* @param inDirectory the directory to look in, if the keyFileBasePath is
|
||||
* relative.
|
||||
* @return the loaded key pair.
|
||||
* @throws IOException
|
||||
* if there was a problem reading the files
|
||||
* @throws IOException if there was a problem reading the files
|
||||
*/
|
||||
public static DnsKeyPair loadKeyPair(String keyFileBasePath, File inDirectory)
|
||||
throws IOException {
|
||||
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 privateKeyFile = new File(inDirectory, keyFileBasePath + ".private");
|
||||
|
||||
|
@ -162,15 +153,12 @@ public class BINDKeyUtils {
|
|||
* Given a base path to a BIND9 key pair, load the public part (only) of the
|
||||
* key pair
|
||||
*
|
||||
* @param keyFileBasePath
|
||||
* the base or real path to the public part of a key
|
||||
* @param keyFileBasePath the base or real path to the public part of a key
|
||||
* pair.
|
||||
* @param inDirectory
|
||||
* the directory to look in if the path is relative (may
|
||||
* be null).
|
||||
* @param inDirectory the directory to look in if the path is relative
|
||||
* (may be null).
|
||||
* @return a {@link DnsKeyPair} containing just the public key information.
|
||||
* @throws IOException
|
||||
* if there was a problem reading the public key file.
|
||||
* @throws IOException if there was a problem reading the public key file.
|
||||
*/
|
||||
public static DnsKeyPair loadKey(String keyFileBasePath, File inDirectory)
|
||||
throws IOException {
|
||||
|
@ -190,15 +178,11 @@ public class BINDKeyUtils {
|
|||
* "keyset-[signer]." where [signer] is the DNS owner name of the key. The
|
||||
* keyset may be signed, but doesn't have to be.
|
||||
*
|
||||
* @param keysetFileName
|
||||
* the name of the keyset file.
|
||||
* @param inDirectory
|
||||
* the directory to look in if the path is relative (may
|
||||
* be null,
|
||||
* defaults to the current working directory).
|
||||
* @param keysetFileName the name of the keyset file.
|
||||
* @param inDirectory the directory to look in if the path is relative (may
|
||||
* be null, defaults to the current working directory).
|
||||
* @return a RRset contain the KEY records and any associated SIG records.
|
||||
* @throws IOException
|
||||
* if there was a problem reading the keyset file.
|
||||
* @throws IOException if there was a problem reading the keyset file.
|
||||
*/
|
||||
public static RRset loadKeySet(String keysetFileName, File inDirectory)
|
||||
throws IOException {
|
||||
|
@ -218,8 +202,8 @@ public class BINDKeyUtils {
|
|||
/**
|
||||
* Calculate the key file base for this key pair.
|
||||
*
|
||||
* @param pair
|
||||
* the {@link DnsKeyPair} to work from. It only needs a public key.
|
||||
* @param pair the {@link DnsKeyPair} to work from. It only needs a public
|
||||
* key.
|
||||
* @return the base name of the key files.
|
||||
*/
|
||||
public static String keyFileBase(DnsKeyPair pair) {
|
||||
|
@ -259,20 +243,17 @@ public class BINDKeyUtils {
|
|||
* Given a the contents of a BIND9 private key file, convert it into a native
|
||||
* {@link java.security.PrivateKey} object.
|
||||
*
|
||||
* @param privateKeyString
|
||||
* the contents of a BIND9 key file in string form.
|
||||
* @param privateKeyString the contents of a BIND9 key file in string form.
|
||||
* @return a {@link java.security.PrivateKey}
|
||||
*/
|
||||
public static PrivateKey convertPrivateKeyString(String privateKeyString) {
|
||||
if (privateKeyString == null)
|
||||
return null;
|
||||
|
||||
// FIXME: should this swallow exceptions or actually propagate
|
||||
// them?
|
||||
try {
|
||||
DnsKeyConverter conv = new DnsKeyConverter();
|
||||
return conv.parsePrivateKeyString(privateKeyString);
|
||||
} catch (IOException|NoSuchAlgorithmException e) {
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
@ -283,10 +264,8 @@ public class BINDKeyUtils {
|
|||
* Given a native private key, convert it into a BIND9 private key file
|
||||
* format.
|
||||
*
|
||||
* @param priv
|
||||
* the private key to convert.
|
||||
* @param pub
|
||||
* the private key's corresponding public key. Some algorithms
|
||||
* @param priv the private key to convert.
|
||||
* @param pub the private key's corresponding public key. Some algorithms
|
||||
* require information from both.
|
||||
* @return a string containing the contents of a BIND9 private key file.
|
||||
*/
|
||||
|
@ -327,16 +306,11 @@ public class BINDKeyUtils {
|
|||
/**
|
||||
* This routine will write out the BIND9 dnssec-* tool compatible files.
|
||||
*
|
||||
* @param baseFileName
|
||||
* use this base file name. If null, the standard BIND9 base
|
||||
* file
|
||||
* name will be computed.
|
||||
* @param pair
|
||||
* the keypair in question.
|
||||
* @param inDirectory
|
||||
* the directory to write to (may be null).
|
||||
* @throws IOException
|
||||
* if there is a problem writing the files.
|
||||
* @param baseFileName use this base file name. If null, the standard BIND9
|
||||
* base file name will be computed.
|
||||
* @param pair the keypair in question.
|
||||
* @param inDirectory the directory to write to (may be null).
|
||||
* @throws IOException if there is a problem writing the files.
|
||||
*/
|
||||
public static void writeKeyFiles(String baseFileName, DnsKeyPair pair,
|
||||
File inDirectory) throws IOException {
|
||||
|
@ -369,10 +343,8 @@ public class BINDKeyUtils {
|
|||
* This routine will write out the BIND9 dnssec-* tool compatible files to the
|
||||
* standard file names.
|
||||
*
|
||||
* @param pair
|
||||
* the key pair in question.
|
||||
* @param inDirectory
|
||||
* the directory to write to (may be null).
|
||||
* @param pair the key pair in question.
|
||||
* @param inDirectory the directory to write to (may be null).
|
||||
*/
|
||||
public static void writeKeyFiles(DnsKeyPair pair, File inDirectory)
|
||||
throws IOException {
|
||||
|
|
|
@ -27,7 +27,7 @@ import java.util.logging.Logger;
|
|||
* @author David Blacka
|
||||
*/
|
||||
public class ByteArrayComparator implements Comparator<byte[]> {
|
||||
private int mOffset = 0;
|
||||
private int mOffset = 0;
|
||||
private boolean mDebug = false;
|
||||
private Logger log;
|
||||
|
||||
|
|
167
src/main/java/com/verisignlabs/dnssec/security/DSAlgorithm.java
Normal file
167
src/main/java/com/verisignlabs/dnssec/security/DSAlgorithm.java
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2022 Verisign. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. The name of the author may not
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
package com.verisignlabs.dnssec.security;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.xbill.DNS.CDSRecord;
|
||||
import org.xbill.DNS.DLVRecord;
|
||||
import org.xbill.DNS.DNSKEYRecord;
|
||||
import org.xbill.DNS.DNSOutput;
|
||||
import org.xbill.DNS.DNSSEC;
|
||||
import org.xbill.DNS.DSRecord;
|
||||
|
||||
/**
|
||||
* This class handles the implementation behind converting DNSKEYs into
|
||||
* DSRecords. It primarily exists to bootstrap whatever crypto libraries we
|
||||
* might need to do so.
|
||||
*
|
||||
* @author David Blacka
|
||||
*/
|
||||
public class DSAlgorithm {
|
||||
|
||||
private Logger log = Logger.getLogger(this.getClass().toString());
|
||||
|
||||
HashSet<Integer> mSupportedAlgorithms = null;
|
||||
|
||||
private static DSAlgorithm mInstance = null;
|
||||
|
||||
public DSAlgorithm() {
|
||||
mSupportedAlgorithms = new HashSet<>();
|
||||
mSupportedAlgorithms.add(DNSSEC.Digest.SHA1);
|
||||
mSupportedAlgorithms.add(DNSSEC.Digest.SHA256);
|
||||
mSupportedAlgorithms.add(DNSSEC.Digest.SHA384);
|
||||
// Attempt to add the bouncycastle provider. This is so we can use this
|
||||
// provider if it is available, but not require the user to add it as one of
|
||||
// the java.security providers.
|
||||
try {
|
||||
Class<?> bcProviderClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
||||
Provider bcProvider = (Provider) bcProviderClass.getDeclaredConstructor().newInstance();
|
||||
Security.addProvider(bcProvider);
|
||||
log.fine("bouncycastle crypto provider loaded");
|
||||
mSupportedAlgorithms.add(DNSSEC.Digest.GOST3411);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
// do nothing, this is the normal case
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String[] supportedAlgorithmMnemonics() {
|
||||
ArrayList<String> algs = new ArrayList<>();
|
||||
|
||||
for (int digestId : mSupportedAlgorithms) {
|
||||
algs.add(DNSSEC.Digest.string(digestId));
|
||||
}
|
||||
|
||||
String[] result = new String[algs.size()];
|
||||
return algs.toArray(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a DNSKEY record, generate the DS record from it.
|
||||
*
|
||||
* @param keyrec the KEY record in question.
|
||||
* @param digestAlg The digest algorithm (SHA-1, SHA-256, etc.).
|
||||
* @param ttl the desired TTL for the generated DS record. If zero, or
|
||||
* negative, the original KEY RR's TTL will be used.
|
||||
* @return the corresponding {@link org.xbill.DNS.DSRecord}
|
||||
*/
|
||||
public DSRecord calculateDSRecord(DNSKEYRecord keyrec, int digestAlg, long ttl) {
|
||||
if (keyrec == null)
|
||||
return null;
|
||||
|
||||
if (ttl <= 0)
|
||||
ttl = keyrec.getTTL();
|
||||
|
||||
DNSOutput os = new DNSOutput();
|
||||
|
||||
os.writeByteArray(keyrec.getName().toWireCanonical());
|
||||
os.writeByteArray(keyrec.rdataToWireCanonical());
|
||||
|
||||
try {
|
||||
byte[] digest;
|
||||
MessageDigest md;
|
||||
|
||||
switch (digestAlg) {
|
||||
case DNSSEC.Digest.SHA1:
|
||||
md = MessageDigest.getInstance("SHA");
|
||||
digest = md.digest(os.toByteArray());
|
||||
break;
|
||||
case DNSSEC.Digest.SHA256:
|
||||
md = MessageDigest.getInstance("SHA-256");
|
||||
digest = md.digest(os.toByteArray());
|
||||
break;
|
||||
case DNSSEC.Digest.GOST3411:
|
||||
// The standard Java crypto providers don't have this, but bouncycastle does
|
||||
if (java.security.Security.getProviders("MessageDigest.GOST3411") != null) {
|
||||
md = MessageDigest.getInstance("GOST3411");
|
||||
digest = md.digest(os.toByteArray());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported digest id: " + digestAlg);
|
||||
}
|
||||
break;
|
||||
case DNSSEC.Digest.SHA384:
|
||||
md = MessageDigest.getInstance("SHA-384");
|
||||
digest = md.digest(os.toByteArray());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown digest id: " + digestAlg);
|
||||
}
|
||||
|
||||
return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl,
|
||||
keyrec.getFootprint(), keyrec.getAlgorithm(), digestAlg,
|
||||
digest);
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.severe(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DLVRecord dsToDLV(DSRecord ds) {
|
||||
return new DLVRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||
ds.getDigestID(), ds.getDigest());
|
||||
}
|
||||
|
||||
public CDSRecord dstoCDS(DSRecord ds) {
|
||||
return new CDSRecord(ds.getName(), ds.getDClass(), ds.getTTL(), ds.getFootprint(), ds.getAlgorithm(),
|
||||
ds.getDClass(), ds.getDigest());
|
||||
}
|
||||
|
||||
public static DSAlgorithm getInstance() {
|
||||
if (mInstance == null) {
|
||||
mInstance = new DSAlgorithm();
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
}
|
|
@ -27,22 +27,16 @@
|
|||
|
||||
package com.verisignlabs.dnssec.security;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.ECFieldFp;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.security.spec.InvalidParameterSpecException;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.security.spec.RSAKeyGenParameterSpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
@ -51,12 +45,6 @@ import java.util.logging.Logger;
|
|||
|
||||
import org.xbill.DNS.DNSSEC;
|
||||
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
// for now, we need to import the EdDSA parameter spec classes
|
||||
// because they have no generic form in java.security.spec.*
|
||||
// sadly, this will currently fail if you don't have the lib.
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
|
||||
|
||||
/**
|
||||
* This class handles translating DNS signing algorithm identifiers into various
|
||||
* usable java implementations.
|
||||
|
@ -72,21 +60,23 @@ public class DnsKeyAlgorithm {
|
|||
|
||||
// Our base algorithm numbers. This is a normalization of the DNSSEC
|
||||
// algorithms (which are really signature algorithms). Thus RSASHA1,
|
||||
// RSASHA256, etc. all boil down to 'RSA' here.
|
||||
public static final int UNKNOWN = -1;
|
||||
public static final int RSA = 1;
|
||||
public static final int DH = 2;
|
||||
public static final int DSA = 3;
|
||||
public static final int ECC_GOST = 4;
|
||||
public static final int ECDSA = 5;
|
||||
public static final int EDDSA = 6;
|
||||
// RSASHA256, etc. all boil down to 'RSA' here. Similarly, ECDSAP256SHA256 and
|
||||
// ECDSAP384SHA384 both become 'ECDSA'.
|
||||
public enum BaseAlgorithm {
|
||||
UNKNOWN,
|
||||
RSA,
|
||||
DH,
|
||||
DSA,
|
||||
ECDSA,
|
||||
EDDSA;
|
||||
}
|
||||
|
||||
private static class AlgEntry {
|
||||
public int dnssecAlgorithm;
|
||||
public String sigName;
|
||||
public int baseType;
|
||||
public BaseAlgorithm baseType;
|
||||
|
||||
public AlgEntry(int algorithm, String sigName, int baseType) {
|
||||
public AlgEntry(int algorithm, String sigName, BaseAlgorithm baseType) {
|
||||
this.dnssecAlgorithm = algorithm;
|
||||
this.sigName = sigName;
|
||||
this.baseType = baseType;
|
||||
|
@ -96,18 +86,20 @@ public class DnsKeyAlgorithm {
|
|||
private static class ECAlgEntry extends AlgEntry {
|
||||
public ECParameterSpec ecSpec;
|
||||
|
||||
public ECAlgEntry(int algorithm, String sigName, int baseType, ECParameterSpec spec) {
|
||||
public ECAlgEntry(int algorithm, String sigName, BaseAlgorithm baseType, ECParameterSpec spec) {
|
||||
super(algorithm, sigName, baseType);
|
||||
this.ecSpec = spec;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EdAlgEntry extends AlgEntry {
|
||||
public EdDSAParameterSpec edSpec;
|
||||
public String curveName;
|
||||
public NamedParameterSpec paramSpec;
|
||||
|
||||
public EdAlgEntry(int algorithm, String sigName, int baseType, EdDSAParameterSpec spec) {
|
||||
public EdAlgEntry(int algorithm, String sigName, BaseAlgorithm baseType, String curveName) {
|
||||
super(algorithm, sigName, baseType);
|
||||
this.edSpec = spec;
|
||||
this.curveName = curveName;
|
||||
this.paramSpec = new NamedParameterSpec(curveName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,12 +122,8 @@ public class DnsKeyAlgorithm {
|
|||
private KeyPairGenerator mRSAKeyGenerator;
|
||||
/** This is a cached key pair generator for DSA keys. */
|
||||
private KeyPairGenerator mDSAKeyGenerator;
|
||||
/** This is a cached key pair generator for ECC GOST keys. */
|
||||
private KeyPairGenerator mECGOSTKeyGenerator;
|
||||
/** This is a cached key pair generator for ECDSA_P256 keys. */
|
||||
private KeyPairGenerator mECKeyGenerator;
|
||||
/** This is a cached key pair generator for EdDSA keys. */
|
||||
private KeyPairGenerator mEdKeyGenerator;
|
||||
|
||||
private Logger log = Logger.getLogger(this.getClass().toString());
|
||||
|
||||
|
@ -143,27 +131,6 @@ public class DnsKeyAlgorithm {
|
|||
private static DnsKeyAlgorithm mInstance = null;
|
||||
|
||||
public DnsKeyAlgorithm() {
|
||||
// Attempt to add the bouncycastle provider.
|
||||
// This is so we can use this provider if it is available, but not require
|
||||
// the user to add it as one of the java.security providers.
|
||||
try {
|
||||
Class<?> bc_provider_class = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
||||
// Provider bc_provider = (Provider) bc_provider_class.newInstance();
|
||||
Provider bc_provider = (Provider) bc_provider_class.getDeclaredConstructor().newInstance();
|
||||
Security.addProvider(bc_provider);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
}
|
||||
|
||||
// Attempt to add the EdDSA-Java provider.
|
||||
try {
|
||||
Class<?> eddsa_provider_class = Class.forName("net.i2p.crypto.eddsa.EdDSASecurityProvider");
|
||||
// Provider eddsa_provider = (Provider) eddsa_provider_class.newInstance();
|
||||
Provider eddsa_provider = (Provider) eddsa_provider_class.getDeclaredConstructor().newInstance();
|
||||
Security.addProvider(eddsa_provider);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
log.warning("Unable to load EdDSA provider");
|
||||
}
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
@ -173,16 +140,16 @@ public class DnsKeyAlgorithm {
|
|||
mIdToMnemonicMap = new HashMap<>();
|
||||
|
||||
// Load the standard DNSSEC algorithms.
|
||||
addAlgorithm(DNSSEC.Algorithm.RSAMD5, "MD5withRSA", RSA);
|
||||
addAlgorithm(DNSSEC.Algorithm.RSAMD5, "MD5withRSA", BaseAlgorithm.RSA);
|
||||
addMnemonic("RSAMD5", DNSSEC.Algorithm.RSAMD5);
|
||||
|
||||
addAlgorithm(DNSSEC.Algorithm.DH, "", DH);
|
||||
addAlgorithm(DNSSEC.Algorithm.DH, "", BaseAlgorithm.DH);
|
||||
addMnemonic("DH", DNSSEC.Algorithm.DH);
|
||||
|
||||
addAlgorithm(DNSSEC.Algorithm.DSA, "SHA1withDSA", DSA);
|
||||
addAlgorithm(DNSSEC.Algorithm.DSA, "SHA1withDSA", BaseAlgorithm.DSA);
|
||||
addMnemonic("DSA", DNSSEC.Algorithm.DSA);
|
||||
|
||||
addAlgorithm(DNSSEC.Algorithm.RSASHA1, "SHA1withRSA", RSA);
|
||||
addAlgorithm(DNSSEC.Algorithm.RSASHA1, "SHA1withRSA", BaseAlgorithm.RSA);
|
||||
addMnemonic("RSASHA1", DNSSEC.Algorithm.RSASHA1);
|
||||
addMnemonic("RSA", DNSSEC.Algorithm.RSASHA1);
|
||||
|
||||
|
@ -194,79 +161,109 @@ public class DnsKeyAlgorithm {
|
|||
addMnemonic("NSEC3RSASHA1", DNSSEC.Algorithm.RSA_NSEC3_SHA1);
|
||||
|
||||
// Algorithms added by RFC 5702.
|
||||
addAlgorithm(DNSSEC.Algorithm.RSASHA256, "SHA256withRSA", RSA);
|
||||
addAlgorithm(DNSSEC.Algorithm.RSASHA256, "SHA256withRSA", BaseAlgorithm.RSA);
|
||||
addMnemonic("RSASHA256", DNSSEC.Algorithm.RSASHA256);
|
||||
|
||||
addAlgorithm(DNSSEC.Algorithm.RSASHA512, "SHA512withRSA", RSA);
|
||||
addAlgorithm(DNSSEC.Algorithm.RSASHA512, "SHA512withRSA", BaseAlgorithm.RSA);
|
||||
addMnemonic("RSASHA512", DNSSEC.Algorithm.RSASHA512);
|
||||
|
||||
// ECC-GOST is not supported by Java 1.8's Sun crypto provider. The
|
||||
// bouncycastle.org provider, however, does support it.
|
||||
// GostR3410-2001-CryptoPro-A is the named curve in the BC provider, but we
|
||||
// will get the parameters directly.
|
||||
addAlgorithm(DNSSEC.Algorithm.ECC_GOST, "GOST3411withECGOST3410", ECC_GOST, null);
|
||||
addMnemonic("ECCGOST", DNSSEC.Algorithm.ECC_GOST);
|
||||
addMnemonic("ECC-GOST", DNSSEC.Algorithm.ECC_GOST);
|
||||
|
||||
addAlgorithm(DNSSEC.Algorithm.ECDSAP256SHA256, "SHA256withECDSA", ECDSA, "secp256r1");
|
||||
addAlgorithm(DNSSEC.Algorithm.ECDSAP256SHA256, "SHA256withECDSA", BaseAlgorithm.ECDSA, "secp256r1");
|
||||
addMnemonic("ECDSAP256SHA256", DNSSEC.Algorithm.ECDSAP256SHA256);
|
||||
addMnemonic("ECDSA-P256", DNSSEC.Algorithm.ECDSAP256SHA256);
|
||||
|
||||
addAlgorithm(DNSSEC.Algorithm.ECDSAP384SHA384, "SHA384withECDSA", ECDSA, "secp384r1");
|
||||
addAlgorithm(DNSSEC.Algorithm.ECDSAP384SHA384, "SHA384withECDSA", BaseAlgorithm.ECDSA, "secp384r1");
|
||||
addMnemonic("ECDSAP384SHA384", DNSSEC.Algorithm.ECDSAP384SHA384);
|
||||
addMnemonic("ECDSA-P384", DNSSEC.Algorithm.ECDSAP384SHA384);
|
||||
|
||||
// EdDSA is not supported by either the Java 1.8 Sun crypto
|
||||
// provider or bouncycastle. It is added by the Ed25519-Java
|
||||
// library. We don't have a corresponding constant in
|
||||
// org.xbill.DNS.DNSSEC yet, though.
|
||||
addAlgorithm(15, "NONEwithEdDSA", EDDSA, "Ed25519");
|
||||
// For the Edwards Curve implementations, we just initialize Signature and
|
||||
// KeyPairGenerator with the curve name.
|
||||
addAlgorithm(15, "Ed25519", BaseAlgorithm.EDDSA, "Ed25519");
|
||||
addMnemonic("ED25519", 15);
|
||||
addAlgorithm(16, "Ed448", BaseAlgorithm.EDDSA, "Ed448");
|
||||
addMnemonic(("ED448"), 16);
|
||||
}
|
||||
|
||||
private void addAlgorithm(int algorithm, String sigName, int baseType) {
|
||||
private void addAlgorithm(int algorithm, String sigName, BaseAlgorithm baseType) {
|
||||
mAlgorithmMap.put(algorithm, new AlgEntry(algorithm, sigName, baseType));
|
||||
}
|
||||
|
||||
private void addAlgorithm(int algorithm, String sigName, int baseType, String curveName) {
|
||||
if (baseType == ECDSA) {
|
||||
ECParameterSpec ecSpec = ECSpecFromAlgorithm(algorithm);
|
||||
if (ecSpec == null)
|
||||
ecSpec = ECSpecFromName(curveName);
|
||||
if (ecSpec == null)
|
||||
return;
|
||||
/**
|
||||
* Add a ECDSA (algorithms 13/14) to the set, looking up the curve names.
|
||||
*
|
||||
* @param algorithm the DNSSEC algorithm number.
|
||||
* @param sigName the name of the signature scheme.
|
||||
* @param curveName the official name of the elliptic curve in our crypto
|
||||
* library (SunEC).
|
||||
*/
|
||||
private void addECDSAAlgorithm(int algorithm, String sigName, String curveName) {
|
||||
ECParameterSpec ecSpec = ECSpecFromName(curveName);
|
||||
if (ecSpec == null)
|
||||
return;
|
||||
|
||||
// Check to see if we can get a Signature object for this algorithm.
|
||||
try {
|
||||
Signature.getInstance(sigName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// for now, let's find out
|
||||
log.severe("could not get signature for " + sigName + ": " + e.getMessage());
|
||||
// If not, do not add the algorithm.
|
||||
return;
|
||||
}
|
||||
ECAlgEntry entry = new ECAlgEntry(algorithm, sigName, baseType, ecSpec);
|
||||
mAlgorithmMap.put(algorithm, entry);
|
||||
} else if (baseType == EDDSA) {
|
||||
EdDSAParameterSpec edSpec = EdDSASpecFromName(curveName);
|
||||
if (edSpec == null)
|
||||
return;
|
||||
|
||||
// Check to see if we can get a Signature object for this algorithm.
|
||||
try {
|
||||
Signature.getInstance(sigName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// for now, let's find out
|
||||
log.severe("could not get signature for " + sigName + ": " + e.getMessage());
|
||||
// If not, do not add the algorithm.
|
||||
return;
|
||||
}
|
||||
EdAlgEntry entry = new EdAlgEntry(algorithm, sigName, baseType, edSpec);
|
||||
mAlgorithmMap.put(algorithm, entry);
|
||||
// Check to see if we can get a Signature object for this algorithm.
|
||||
try {
|
||||
Signature.getInstance(sigName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// for now, let's find out
|
||||
log.severe("could not get signature for " + sigName + ": " + e.getMessage());
|
||||
// If not, do not add the algorithm.
|
||||
return;
|
||||
}
|
||||
|
||||
ECAlgEntry entry = new ECAlgEntry(algorithm, sigName, BaseAlgorithm.ECDSA, ecSpec);
|
||||
mAlgorithmMap.put(algorithm, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an EdDSA (Edwards curve algorithms, DNSSEC algorithms 15/16), looking up
|
||||
* the curve.
|
||||
*
|
||||
* @param algorithm the DNSSEC algorithm numer.
|
||||
* @param sigName the name of the signing scheme. For EdDSA, this is the same
|
||||
* as the curve.
|
||||
* @param curveName the name of the curve.
|
||||
*/
|
||||
private void addEdDSAAlgorithm(int algorithm, String sigName, String curveName) {
|
||||
// Check to see if we can get a Signature object for this algorithm.
|
||||
try {
|
||||
Signature.getInstance(sigName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// for now, let's find out
|
||||
log.severe("could not get signature for EdDSA curve" + curveName + ": " + e.getMessage());
|
||||
// If not, do not add the algorithm.
|
||||
return;
|
||||
}
|
||||
EdAlgEntry entry = new EdAlgEntry(algorithm, sigName, BaseAlgorithm.EDDSA, curveName);
|
||||
mAlgorithmMap.put(algorithm, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an Elliptic Curve algorithm given a signing scheme and curve name.
|
||||
*
|
||||
* @param algorithm the DNSSEC algorithm number
|
||||
* @param sigName the signature scheme (e.g., which crypto hash function are
|
||||
* we using?)
|
||||
* @param baseType the base type (either ECDSA or EDDSA).
|
||||
* @param curveName the name of the curve.
|
||||
*/
|
||||
private void addAlgorithm(int algorithm, String sigName, BaseAlgorithm baseType, String curveName) {
|
||||
switch (baseType) {
|
||||
case ECDSA:
|
||||
addECDSAAlgorithm(algorithm, sigName, curveName);
|
||||
break;
|
||||
case EDDSA:
|
||||
addEdDSAAlgorithm(algorithm, sigName, curveName);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Non-Ellipic curve algorithm passed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alternate mnemonic for an algorithm.
|
||||
*
|
||||
* @param m the new mnemonic.
|
||||
* @param alg the DNSSEC algorithm number.
|
||||
*/
|
||||
private void addMnemonic(String m, int alg) {
|
||||
// Do not add mnemonics for algorithms that ended up not actually being
|
||||
// supported.
|
||||
|
@ -277,19 +274,19 @@ public class DnsKeyAlgorithm {
|
|||
mIdToMnemonicMap.computeIfAbsent(alg, k -> m);
|
||||
}
|
||||
|
||||
public void addAlias(int alias, String mnemonic, int original_algorithm) {
|
||||
public void addAlias(int alias, String mnemonic, int origAlgorithm) {
|
||||
if (mAlgorithmMap.containsKey(alias)) {
|
||||
log.warning("Unable to alias algorithm " + alias + " because it already exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mAlgorithmMap.containsKey(original_algorithm)) {
|
||||
if (!mAlgorithmMap.containsKey(origAlgorithm)) {
|
||||
log.warning("Unable to alias algorithm " + alias
|
||||
+ " to unknown algorithm identifier " + original_algorithm);
|
||||
+ " to unknown algorithm identifier " + origAlgorithm);
|
||||
return;
|
||||
}
|
||||
|
||||
mAlgorithmMap.put(alias, mAlgorithmMap.get(original_algorithm));
|
||||
mAlgorithmMap.put(alias, mAlgorithmMap.get(origAlgorithm));
|
||||
|
||||
if (mnemonic != null) {
|
||||
addMnemonic(mnemonic, alias);
|
||||
|
@ -300,34 +297,12 @@ public class DnsKeyAlgorithm {
|
|||
return mAlgorithmMap.get(alg);
|
||||
}
|
||||
|
||||
// For curves where we don't (or can't) get the parameters from a standard
|
||||
// name, we can construct the parameters here. For now, we only do this for
|
||||
// the ECC-GOST curve.
|
||||
private ECParameterSpec ECSpecFromAlgorithm(int algorithm) {
|
||||
switch (algorithm) {
|
||||
case DNSSEC.Algorithm.ECC_GOST: {
|
||||
// From RFC 4357 Section 11.4
|
||||
BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", 16);
|
||||
BigInteger a = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94", 16);
|
||||
BigInteger b = new BigInteger("A6", 16);
|
||||
BigInteger gx = new BigInteger("1", 16);
|
||||
BigInteger gy = new BigInteger("8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14", 16);
|
||||
BigInteger n = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893", 16);
|
||||
|
||||
EllipticCurve curve = new EllipticCurve(new ECFieldFp(p), a, b);
|
||||
return new ECParameterSpec(curve, new ECPoint(gx, gy), n, 1);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the curve parameters from a named ECDSA curve.
|
||||
private ECParameterSpec ECSpecFromName(String stdName) {
|
||||
try {
|
||||
AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");
|
||||
ECGenParameterSpec ecg_spec = new ECGenParameterSpec(stdName);
|
||||
ap.init(ecg_spec);
|
||||
ECGenParameterSpec ecgSpec = new ECGenParameterSpec(stdName);
|
||||
ap.init(ecgSpec);
|
||||
return ap.getParameterSpec(ECParameterSpec.class);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.info("Elliptic Curve not supported by any crypto provider: " + e.getMessage());
|
||||
|
@ -337,24 +312,6 @@ public class DnsKeyAlgorithm {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Fetch the curve parameters from a named EdDSA curve.
|
||||
private EdDSAParameterSpec EdDSASpecFromName(String stdName) {
|
||||
try {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(stdName);
|
||||
if (spec != null)
|
||||
return spec;
|
||||
throw new InvalidParameterSpecException("Edwards Curve " + stdName + " not found.");
|
||||
}
|
||||
// catch (NoSuchAlgorithmException e) {
|
||||
// log.info("Edwards Curve not supported by any crypto provider: " +
|
||||
// e.getMessage());
|
||||
// }
|
||||
catch (InvalidParameterSpecException e) {
|
||||
log.info("Edwards Curve " + stdName + " not supported");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String[] supportedAlgMnemonics() {
|
||||
Set<Integer> keyset = mAlgorithmMap.keySet();
|
||||
Integer[] algs = keyset.toArray(new Integer[keyset.size()]);
|
||||
|
@ -392,11 +349,10 @@ public class DnsKeyAlgorithm {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given one of the ECDSA algorithms (ECDSAP256SHA256, etc.) return
|
||||
* the elliptic curve parameters.
|
||||
* Given one of the ECDSA algorithms (ECDSAP256SHA256, etc.) return the
|
||||
* elliptic curve parameters.
|
||||
*
|
||||
* @param algorithm
|
||||
* The DNSSEC algorithm number.
|
||||
* @param algorithm The DNSSEC algorithm number.
|
||||
* @return The calculated JCA ECParameterSpec for that DNSSEC algorithm, or
|
||||
* null if not a recognized/supported EC algorithm.
|
||||
*/
|
||||
|
@ -406,37 +362,35 @@ public class DnsKeyAlgorithm {
|
|||
return null;
|
||||
if (!(entry instanceof ECAlgEntry))
|
||||
return null;
|
||||
ECAlgEntry ec_entry = (ECAlgEntry) entry;
|
||||
ECAlgEntry ecEntry = (ECAlgEntry) entry;
|
||||
|
||||
return ec_entry.ecSpec;
|
||||
return ecEntry.ecSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one of the EdDSA algorithms (Ed25519, Ed448) return the
|
||||
* elliptic curve parameters.
|
||||
* Given one of the EdDSA algorithms (ED25519 or ED448), return the named
|
||||
* parameter spec.
|
||||
*
|
||||
* @param algorithm
|
||||
* The DNSSEC algorithm number.
|
||||
* @return The stored EdDSAParameterSpec for that algorithm, or
|
||||
* null if not a recognized/supported EdDSA algorithm.
|
||||
* @param algorithm The DNSSEC algorithm number.
|
||||
* @return The NamedParameterSpec for that DNSSEC algorithm, nor null if the
|
||||
* algorithm wasn't a supported EdDSA algorithm.
|
||||
*/
|
||||
public EdDSAParameterSpec getEdwardsCurveParams(int algorithm) {
|
||||
public NamedParameterSpec getEdwardsCurveSpec(int algorithm) {
|
||||
AlgEntry entry = getEntry(algorithm);
|
||||
if (entry == null)
|
||||
return null;
|
||||
if (!(entry instanceof EdAlgEntry))
|
||||
return null;
|
||||
EdAlgEntry ed_entry = (EdAlgEntry) entry;
|
||||
EdAlgEntry edEntry = (EdAlgEntry) entry;
|
||||
|
||||
return ed_entry.edSpec;
|
||||
return edEntry.paramSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a possible algorithm alias back to the original DNSSEC algorithm
|
||||
* number
|
||||
*
|
||||
* @param algorithm
|
||||
* a DNSSEC algorithm that may be an alias.
|
||||
* @param algorithm a DNSSEC algorithm that may be an alias.
|
||||
* @return -1 if the algorithm isn't recognised, the orignal algorithm number
|
||||
* if it is.
|
||||
*/
|
||||
|
@ -462,8 +416,7 @@ public class DnsKeyAlgorithm {
|
|||
* Given an algorithm mnemonic, convert the mnemonic to a DNSSEC algorithm
|
||||
* number.
|
||||
*
|
||||
* @param s
|
||||
* The mnemonic string. This is case-insensitive.
|
||||
* @param s The mnemonic string. This is case-insensitive.
|
||||
* @return -1 if the mnemonic isn't recognized or supported, the algorithm
|
||||
* number if it is.
|
||||
*/
|
||||
|
@ -477,8 +430,7 @@ public class DnsKeyAlgorithm {
|
|||
/**
|
||||
* Given a DNSSEC algorithm number, return the "preferred" mnemonic.
|
||||
*
|
||||
* @param algorithm
|
||||
* A DNSSEC algorithm number.
|
||||
* @param algorithm A DNSSEC algorithm number.
|
||||
* @return The preferred mnemonic string, or null if not supported or
|
||||
* recognized.
|
||||
*/
|
||||
|
@ -486,15 +438,15 @@ public class DnsKeyAlgorithm {
|
|||
return mIdToMnemonicMap.get(algorithm);
|
||||
}
|
||||
|
||||
public int baseType(int algorithm) {
|
||||
public BaseAlgorithm baseType(int algorithm) {
|
||||
AlgEntry entry = getEntry(algorithm);
|
||||
if (entry != null)
|
||||
return entry.baseType;
|
||||
return UNKNOWN;
|
||||
return BaseAlgorithm.UNKNOWN;
|
||||
}
|
||||
|
||||
public boolean isDSA(int algorithm) {
|
||||
return (baseType(algorithm) == DSA);
|
||||
return (baseType(algorithm) == BaseAlgorithm.DSA);
|
||||
}
|
||||
|
||||
public KeyPair generateKeyPair(int algorithm, int keysize, boolean useLargeExp)
|
||||
|
@ -506,14 +458,14 @@ public class DnsKeyAlgorithm {
|
|||
mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
}
|
||||
|
||||
RSAKeyGenParameterSpec rsa_spec;
|
||||
RSAKeyGenParameterSpec rsaSpec;
|
||||
if (useLargeExp) {
|
||||
rsa_spec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F4);
|
||||
rsaSpec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F4);
|
||||
} else {
|
||||
rsa_spec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F0);
|
||||
rsaSpec = new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F0);
|
||||
}
|
||||
try {
|
||||
mRSAKeyGenerator.initialize(rsa_spec);
|
||||
mRSAKeyGenerator.initialize(rsaSpec);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
// Fold the InvalidAlgorithmParameterException into our existing
|
||||
// thrown exception. Ugly, but requires less code change.
|
||||
|
@ -531,22 +483,6 @@ public class DnsKeyAlgorithm {
|
|||
pair = mDSAKeyGenerator.generateKeyPair();
|
||||
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: {
|
||||
if (mECKeyGenerator == null) {
|
||||
mECKeyGenerator = KeyPairGenerator.getInstance("EC");
|
||||
|
@ -564,19 +500,10 @@ public class DnsKeyAlgorithm {
|
|||
break;
|
||||
}
|
||||
case EDDSA: {
|
||||
if (mEdKeyGenerator == null) {
|
||||
mEdKeyGenerator = KeyPairGenerator.getInstance("EdDSA");
|
||||
}
|
||||
EdAlgEntry entry = (EdAlgEntry) getEntry(algorithm);
|
||||
KeyPairGenerator edKeyGenerator = KeyPairGenerator.getInstance(entry.curveName);
|
||||
|
||||
EdDSAParameterSpec edSpec = getEdwardsCurveParams(algorithm);
|
||||
try {
|
||||
mEdKeyGenerator.initialize(edSpec, new SecureRandom());
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
// Fold the InvalidAlgorithmParameterException into our existing
|
||||
// thrown exception. Ugly, but requires less code change.
|
||||
throw new NoSuchAlgorithmException("invalid key parameter spec");
|
||||
}
|
||||
pair = mEdKeyGenerator.generateKeyPair();
|
||||
pair = edKeyGenerator.generateKeyPair();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -30,15 +30,20 @@ import java.security.interfaces.DSAPrivateKey;
|
|||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.EdECPrivateKey;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.spec.DSAPrivateKeySpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPrivateKeySpec;
|
||||
import java.security.spec.EdECPrivateKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.RSAPrivateCrtKeySpec;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.crypto.interfaces.DHPrivateKey;
|
||||
import javax.crypto.interfaces.DHPublicKey;
|
||||
|
@ -50,13 +55,6 @@ import org.xbill.DNS.DNSSEC.DNSSECException;
|
|||
import org.xbill.DNS.Name;
|
||||
import org.xbill.DNS.utils.base64;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
// For now, just import the native EdDSA classes
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
/**
|
||||
* This class handles conversions between JCA key formats and DNSSEC and BIND9
|
||||
* key formats.
|
||||
|
@ -70,6 +68,8 @@ public class DnsKeyConverter {
|
|||
private KeyFactory mECKeyFactory;
|
||||
private KeyFactory mEdKeyFactory;
|
||||
private DnsKeyAlgorithm mAlgorithms;
|
||||
|
||||
private Logger log = Logger.getLogger(this.getClass().toString());
|
||||
|
||||
public DnsKeyConverter() {
|
||||
mAlgorithms = DnsKeyAlgorithm.getInstance();
|
||||
|
@ -87,7 +87,6 @@ public class DnsKeyConverter {
|
|||
|
||||
// Because we have arbitrarily aliased algorithms, we need to possibly
|
||||
// translate the aliased algorithm back to the actual algorithm.
|
||||
|
||||
int originalAlgorithm = mAlgorithms.originalAlgorithm(pKeyRecord.getAlgorithm());
|
||||
|
||||
if (originalAlgorithm <= 0)
|
||||
|
@ -101,16 +100,6 @@ public class DnsKeyConverter {
|
|||
pKeyRecord.getKey());
|
||||
}
|
||||
|
||||
// do not rely on DNSJava's method for EdDSA for now.
|
||||
if (mAlgorithms.baseType(originalAlgorithm) == DnsKeyAlgorithm.EDDSA) {
|
||||
try {
|
||||
return parseEdDSADNSKEYRecord(pKeyRecord);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
// just to be expedient, recast this as a NoSuchAlgorithmException.
|
||||
throw new NoSuchAlgorithmException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// This uses DNSJava's DNSSEC.toPublicKey() method.
|
||||
return pKeyRecord.getPublicKey();
|
||||
|
@ -119,46 +108,27 @@ public class DnsKeyConverter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since we don't (yet) have support in DNSJava for parsing the
|
||||
* newer EdDSA algorithms, here is a local version.
|
||||
*/
|
||||
private PublicKey parseEdDSADNSKEYRecord(DNSKEYRecord pKeyRecord)
|
||||
throws IllegalArgumentException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
byte[] seed = pKeyRecord.getKey();
|
||||
|
||||
EdDSAPublicKeySpec spec = new EdDSAPublicKeySpec(seed,
|
||||
mAlgorithms.getEdwardsCurveParams(pKeyRecord.getAlgorithm()));
|
||||
|
||||
KeyFactory factory = KeyFactory.getInstance("EdDSA");
|
||||
return factory.generatePublic(spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a JCA public key and the ancillary data, generate a DNSKEY record.
|
||||
*/
|
||||
public DNSKEYRecord generateDNSKEYRecord(Name name, int dclass, long ttl,
|
||||
int flags, int alg, PublicKey key) {
|
||||
try {
|
||||
if (mAlgorithms.baseType(alg) == DnsKeyAlgorithm.EDDSA) {
|
||||
return generateEdDSADNSKEYRecord(name, dclass, ttl, flags, alg, key);
|
||||
}
|
||||
return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg,
|
||||
int origAlgorithm = mAlgorithms.originalAlgorithm(alg);
|
||||
DNSKEYRecord keyrec = new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, origAlgorithm,
|
||||
key);
|
||||
if (origAlgorithm == alg) {
|
||||
return keyrec;
|
||||
}
|
||||
return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg, keyrec.getKey());
|
||||
} catch (DNSSECException e) {
|
||||
// FIXME: this mimics the behavior of KEYConverter.buildRecord(), which would
|
||||
log.severe("Unable to generated a DNSKEYRecord: " + e);
|
||||
// This mimics the behavior of KEYConverter.buildRecord(), which would
|
||||
// return null if the algorithm was unknown.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DNSKEYRecord generateEdDSADNSKEYRecord(Name name, int dclass, long ttl,
|
||||
int flags, int alg, PublicKey key) {
|
||||
EdDSAPublicKey ed_key = (EdDSAPublicKey) key;
|
||||
byte[] key_data = ed_key.getAbyte();
|
||||
return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg,
|
||||
key_data);
|
||||
}
|
||||
// Private Key Specific Parsing routines
|
||||
|
||||
/**
|
||||
|
@ -168,9 +138,9 @@ public class DnsKeyConverter {
|
|||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
|
||||
try {
|
||||
switch (mAlgorithms.baseType(algorithm)) {
|
||||
case DnsKeyAlgorithm.RSA:
|
||||
case RSA:
|
||||
return mRSAKeyFactory.generatePrivate(spec);
|
||||
case DnsKeyAlgorithm.DSA:
|
||||
case DSA:
|
||||
return mDSAKeyFactory.generatePrivate(spec);
|
||||
default:
|
||||
return null;
|
||||
|
@ -225,17 +195,15 @@ public class DnsKeyConverter {
|
|||
int alg = parseInt(val, -1);
|
||||
|
||||
switch (mAlgorithms.baseType(alg)) {
|
||||
case DnsKeyAlgorithm.RSA:
|
||||
case RSA:
|
||||
return parsePrivateRSA(lines);
|
||||
case DnsKeyAlgorithm.DSA:
|
||||
case DSA:
|
||||
return parsePrivateDSA(lines);
|
||||
case DnsKeyAlgorithm.DH:
|
||||
case DH:
|
||||
return parsePrivateDH(lines);
|
||||
case DnsKeyAlgorithm.ECC_GOST:
|
||||
case ECDSA:
|
||||
return parsePrivateECDSA(lines, alg);
|
||||
case DnsKeyAlgorithm.ECDSA:
|
||||
return parsePrivateECDSA(lines, alg);
|
||||
case DnsKeyAlgorithm.EDDSA:
|
||||
case EDDSA:
|
||||
return parsePrivateEdDSA(lines, alg);
|
||||
default:
|
||||
throw new IOException("unsupported private key algorithm: " + val);
|
||||
|
@ -272,12 +240,12 @@ public class DnsKeyConverter {
|
|||
private PrivateKey parsePrivateRSA(StringTokenizer lines)
|
||||
throws NoSuchAlgorithmException {
|
||||
BigInteger modulus = null;
|
||||
BigInteger public_exponent = null;
|
||||
BigInteger private_exponent = null;
|
||||
BigInteger prime_p = null;
|
||||
BigInteger prime_q = null;
|
||||
BigInteger prime_p_exponent = null;
|
||||
BigInteger prime_q_exponent = null;
|
||||
BigInteger publicExponent = null;
|
||||
BigInteger privateExponent = null;
|
||||
BigInteger primeP = null;
|
||||
BigInteger primeQ = null;
|
||||
BigInteger primePExponent = null;
|
||||
BigInteger primeQExponent = null;
|
||||
BigInteger coefficient = null;
|
||||
|
||||
while (lines.hasMoreTokens()) {
|
||||
|
@ -296,33 +264,28 @@ public class DnsKeyConverter {
|
|||
|
||||
if (line.startsWith("Modulus: ")) {
|
||||
modulus = new BigInteger(1, data);
|
||||
// printBigIntCompare(data, modulus);
|
||||
} else if (line.startsWith("PublicExponent: ")) {
|
||||
public_exponent = new BigInteger(1, data);
|
||||
// printBigIntCompare(data, public_exponent);
|
||||
publicExponent = new BigInteger(1, data);
|
||||
} else if (line.startsWith("PrivateExponent: ")) {
|
||||
private_exponent = new BigInteger(1, data);
|
||||
// printBigIntCompare(data, private_exponent);
|
||||
privateExponent = new BigInteger(1, data);
|
||||
} else if (line.startsWith("Prime1: ")) {
|
||||
prime_p = new BigInteger(1, data);
|
||||
// printBigIntCompare(data, prime_p);
|
||||
primeP = new BigInteger(1, data);
|
||||
} else if (line.startsWith("Prime2: ")) {
|
||||
prime_q = new BigInteger(1, data);
|
||||
// printBigIntCompare(data, prime_q);
|
||||
primeQ = new BigInteger(1, data);
|
||||
} else if (line.startsWith("Exponent1: ")) {
|
||||
prime_p_exponent = new BigInteger(1, data);
|
||||
primePExponent = new BigInteger(1, data);
|
||||
} else if (line.startsWith("Exponent2: ")) {
|
||||
prime_q_exponent = new BigInteger(1, data);
|
||||
primeQExponent = new BigInteger(1, data);
|
||||
} else if (line.startsWith("Coefficient: ")) {
|
||||
coefficient = new BigInteger(1, data);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
KeySpec spec = new RSAPrivateCrtKeySpec(modulus, public_exponent,
|
||||
private_exponent, prime_p,
|
||||
prime_q, prime_p_exponent,
|
||||
prime_q_exponent, coefficient);
|
||||
KeySpec spec = new RSAPrivateCrtKeySpec(modulus, publicExponent,
|
||||
privateExponent, primeP,
|
||||
primeQ, primePExponent,
|
||||
primeQExponent, coefficient);
|
||||
if (mRSAKeyFactory == null) {
|
||||
mRSAKeyFactory = KeyFactory.getInstance("RSA");
|
||||
}
|
||||
|
@ -466,13 +429,13 @@ public class DnsKeyConverter {
|
|||
if (mECKeyFactory == null) {
|
||||
mECKeyFactory = KeyFactory.getInstance("EC");
|
||||
}
|
||||
ECParameterSpec ec_spec = mAlgorithms.getEllipticCurveParams(algorithm);
|
||||
if (ec_spec == null) {
|
||||
ECParameterSpec ecSpec = mAlgorithms.getEllipticCurveParams(algorithm);
|
||||
if (ecSpec == null) {
|
||||
throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm +
|
||||
" is not a recognized Elliptic Curve algorithm");
|
||||
}
|
||||
|
||||
KeySpec spec = new ECPrivateKeySpec(s, ec_spec);
|
||||
KeySpec spec = new ECPrivateKeySpec(s, ecSpec);
|
||||
|
||||
try {
|
||||
return mECKeyFactory.generatePrivate(spec);
|
||||
|
@ -516,13 +479,13 @@ public class DnsKeyConverter {
|
|||
if (mEdKeyFactory == null) {
|
||||
mEdKeyFactory = KeyFactory.getInstance("EdDSA");
|
||||
}
|
||||
EdDSAParameterSpec ed_spec = mAlgorithms.getEdwardsCurveParams(algorithm);
|
||||
if (ed_spec == null) {
|
||||
NamedParameterSpec namedSpec = mAlgorithms.getEdwardsCurveSpec(algorithm);
|
||||
if (namedSpec == null) {
|
||||
throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm +
|
||||
" is not a recognized Edwards Curve algorithm");
|
||||
}
|
||||
|
||||
KeySpec spec = new EdDSAPrivateKeySpec(seed, ed_spec);
|
||||
EdECPrivateKeySpec spec = new EdECPrivateKeySpec(namedSpec, seed);
|
||||
|
||||
try {
|
||||
return mEdKeyFactory.generatePrivate(spec);
|
||||
|
@ -545,8 +508,8 @@ public class DnsKeyConverter {
|
|||
return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub, alg);
|
||||
} else if (priv instanceof ECPrivateKey && pub instanceof ECPublicKey) {
|
||||
return generatePrivateEC((ECPrivateKey) priv, (ECPublicKey) pub, alg);
|
||||
} else if (priv instanceof EdDSAPrivateKey && pub instanceof EdDSAPublicKey) {
|
||||
return generatePrivateED((EdDSAPrivateKey) priv, (EdDSAPublicKey) pub, alg);
|
||||
} else if (priv instanceof EdECPrivateKey && pub instanceof EdECPublicKey) {
|
||||
return generatePrivateED((EdECPrivateKey) priv, (EdECPublicKey) pub, alg);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -556,16 +519,16 @@ public class DnsKeyConverter {
|
|||
* Convert from 'unsigned' big integer to original 'signed format' in Base64
|
||||
*/
|
||||
private static String b64BigInt(BigInteger i) {
|
||||
byte[] orig_bytes = i.toByteArray();
|
||||
byte[] origBytes = i.toByteArray();
|
||||
|
||||
if (orig_bytes[0] != 0 || orig_bytes.length == 1) {
|
||||
return base64.toString(orig_bytes);
|
||||
if (origBytes[0] != 0 || origBytes.length == 1) {
|
||||
return base64.toString(origBytes);
|
||||
}
|
||||
|
||||
byte[] signed_bytes = new byte[orig_bytes.length - 1];
|
||||
System.arraycopy(orig_bytes, 1, signed_bytes, 0, signed_bytes.length);
|
||||
byte[] signedBytes = new byte[origBytes.length - 1];
|
||||
System.arraycopy(origBytes, 1, signedBytes, 0, signedBytes.length);
|
||||
|
||||
return base64.toString(signed_bytes);
|
||||
return base64.toString(signedBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -668,7 +631,7 @@ public class DnsKeyConverter {
|
|||
* Given an edwards curve key pair, and the actual algorithm (which will
|
||||
* describe the curve used), return the BIND9-style text encoding.
|
||||
*/
|
||||
private String generatePrivateED(EdDSAPrivateKey priv, EdDSAPublicKey pub, int alg) {
|
||||
private String generatePrivateED(EdECPrivateKey priv, EdECPublicKey pub, int alg) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter out = new PrintWriter(sw);
|
||||
|
||||
|
@ -676,7 +639,8 @@ public class DnsKeyConverter {
|
|||
out.println("Algorithm: " + alg + " (" + mAlgorithms.algToString(alg)
|
||||
+ ")");
|
||||
out.print("PrivateKey: ");
|
||||
out.println(base64.toString(priv.getSeed()));
|
||||
byte[] keyBytes = priv.getBytes().orElse("null".getBytes());
|
||||
out.println(base64.toString(keyBytes));
|
||||
|
||||
return sw.toString();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.verisignlabs.dnssec.security;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
|
@ -127,6 +128,12 @@ public class DnsKeyPair {
|
|||
setPrivateKeyString(pair.getPrivateKeyString());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
return this.getDNSKEYName() + "/" + this.getDNSKEYAlgorithm() + "/" + this.getDNSKEYFootprint() + "/"
|
||||
+ this.getDNSKEYPublicPrefix(6);
|
||||
}
|
||||
|
||||
/** @return cached DnsKeyConverter object. */
|
||||
protected DnsKeyConverter getKeyConverter() {
|
||||
if (mKeyConverter == null) {
|
||||
|
@ -257,18 +264,22 @@ public class DnsKeyPair {
|
|||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public Signature getVerifier() {
|
||||
if (mVerifier == null) {
|
||||
mVerifier = getSignature();
|
||||
PublicKey pk = getPublic();
|
||||
if (mVerifier != null && pk != null) {
|
||||
try {
|
||||
mVerifier.initVerify(pk);
|
||||
} catch (InvalidKeyException e) {
|
||||
}
|
||||
} else {
|
||||
// do not return an uninitialized verifier
|
||||
return null;
|
||||
}
|
||||
if (mVerifier != null)
|
||||
return mVerifier;
|
||||
|
||||
mVerifier = getSignature();
|
||||
PublicKey pk = getPublic();
|
||||
|
||||
if (mVerifier == null || pk == null) {
|
||||
log.warning("Could not get a Signature object for this key pair" + this);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
mVerifier.initVerify(pk);
|
||||
} catch (InvalidKeyException e) {
|
||||
log.warning("Key pair cannot initialize with public key: " + this);
|
||||
return null;
|
||||
}
|
||||
|
||||
return mVerifier;
|
||||
|
@ -319,4 +330,27 @@ public class DnsKeyPair {
|
|||
return kr.getFootprint();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This is from a StackOverflow answer. There are number of bytes-to-hex
|
||||
// converters in the ecosystem, but this avoid extra dependencies
|
||||
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
public static String toHex(byte[] bytes) {
|
||||
byte[] hexChars = new byte[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String getDNSKEYPublicPrefix(int length) {
|
||||
DNSKEYRecord kr = getDNSKEYRecord();
|
||||
if (kr == null) {
|
||||
return "";
|
||||
}
|
||||
String hexKey = toHex(kr.getKey());
|
||||
return hexKey.substring(0, length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,20 +70,19 @@ public class DnsSecVerifier {
|
|||
add(pair);
|
||||
}
|
||||
|
||||
public DnsKeyPair find(Name name, int algorithm, int keyid) {
|
||||
public List<DnsKeyPair> find(Name name, int algorithm, int keyid) {
|
||||
String n = name.toString().toLowerCase();
|
||||
List<DnsKeyPair> l = mKeyMap.get(n);
|
||||
List<DnsKeyPair> result = new ArrayList<>();
|
||||
if (l == null)
|
||||
return null;
|
||||
return result;
|
||||
|
||||
// FIXME: this algorithm assumes that name+alg+footprint is
|
||||
// unique, which isn't necessarily true.
|
||||
for (DnsKeyPair p : l) {
|
||||
if (p.getDNSKEYAlgorithm() == algorithm && p.getDNSKEYFootprint() == keyid) {
|
||||
return p;
|
||||
result.add(p);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +137,7 @@ public class DnsSecVerifier {
|
|||
mCurrentTime = time;
|
||||
}
|
||||
|
||||
private DnsKeyPair findKey(Name name, int algorithm, int footprint) {
|
||||
private List<DnsKeyPair> findKey(Name name, int algorithm, int footprint) {
|
||||
return mKeyStore.find(name, algorithm, footprint);
|
||||
}
|
||||
|
||||
|
@ -219,43 +218,51 @@ public class DnsSecVerifier {
|
|||
if (!result)
|
||||
return result;
|
||||
|
||||
DnsKeyPair keypair = findKey(sigrec.getSigner(), sigrec.getAlgorithm(),
|
||||
List<DnsKeyPair> keypairs = findKey(sigrec.getSigner(), sigrec.getAlgorithm(),
|
||||
sigrec.getFootprint());
|
||||
|
||||
if (keypair == null) {
|
||||
if (keypairs.isEmpty()) {
|
||||
if (reasons != null)
|
||||
reasons.add("Could not find matching trusted key");
|
||||
log.fine("could not find matching trusted key");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] data = SignUtils.generateSigData(rrset, sigrec);
|
||||
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
|
||||
Signature signer = keypair.getVerifier();
|
||||
signer.update(data);
|
||||
// Tolerate duplicate keytags, so we can have more than one DnsKeyPair
|
||||
List<String> localReasons = new ArrayList<>();
|
||||
boolean validated = false;
|
||||
for (DnsKeyPair keypair : keypairs) {
|
||||
Signature signer = keypair.getVerifier();
|
||||
signer.update(data);
|
||||
|
||||
byte[] sig = sigrec.getSignature();
|
||||
byte[] sig = sigrec.getSignature();
|
||||
|
||||
if (algs.baseType(sigrec.getAlgorithm()) == DnsKeyAlgorithm.DSA) {
|
||||
sig = SignUtils.convertDSASignature(sig);
|
||||
if (algs.baseType(sigrec.getAlgorithm()) == DnsKeyAlgorithm.BaseAlgorithm.DSA) {
|
||||
sig = SignUtils.convertDSASignature(sig);
|
||||
}
|
||||
|
||||
if (sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP256SHA256 ||
|
||||
sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP384SHA384) {
|
||||
sig = SignUtils.convertECDSASignature(sig);
|
||||
}
|
||||
if (signer.verify(sig)) {
|
||||
validated = true;
|
||||
break;
|
||||
}
|
||||
log.fine("Signature failed to validate cryptographically with " + keypair);
|
||||
if (localReasons != null) {
|
||||
localReasons.add("Signature failed to verify cryptographically with " + keypair);
|
||||
}
|
||||
}
|
||||
|
||||
if (sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP256SHA256 ||
|
||||
sigrec.getAlgorithm() == DNSSEC.Algorithm.ECDSAP384SHA384) {
|
||||
sig = SignUtils.convertECDSASignature(sig);
|
||||
if (!validated) {
|
||||
reasons.addAll(localReasons);
|
||||
}
|
||||
|
||||
if (!signer.verify(sig)) {
|
||||
if (reasons != null)
|
||||
reasons.add("Signature failed to verify cryptographically");
|
||||
log.fine("Signature failed to verify cryptographically");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return validated;
|
||||
} catch (IOException e) {
|
||||
log.severe("I/O error: " + e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
|
|
|
@ -68,21 +68,14 @@ public class JCEDnsSecSigner {
|
|||
/**
|
||||
* Cryptographically generate a new DNSSEC key.
|
||||
*
|
||||
* @param owner
|
||||
* the KEY RR's owner name.
|
||||
* @param ttl
|
||||
* the KEY RR's TTL.
|
||||
* @param dclass
|
||||
* the KEY RR's DNS class.
|
||||
* @param algorithm
|
||||
* the DNSSEC algorithm (RSASHA258, RSASHA512,
|
||||
* @param owner the KEY RR's owner name.
|
||||
* @param ttl the KEY RR's TTL.
|
||||
* @param dclass the KEY RR's DNS class.
|
||||
* @param algorithm the DNSSEC algorithm (RSASHA258, RSASHA512,
|
||||
* ECDSAP256, etc.)
|
||||
* @param flags
|
||||
* any flags for the KEY RR.
|
||||
* @param keysize
|
||||
* the size of the key to generate.
|
||||
* @param useLargeExponent
|
||||
* if generating an RSA key, use the large exponent.
|
||||
* @param flags any flags for the KEY RR.
|
||||
* @param keysize the size of the key to generate.
|
||||
* @param useLargeExponent if generating an RSA key, use the large exponent.
|
||||
* @return a DnsKeyPair with the public and private keys populated.
|
||||
*/
|
||||
public DnsKeyPair generateKey(Name owner, long ttl, int dclass, int algorithm,
|
||||
|
@ -113,29 +106,25 @@ public class JCEDnsSecSigner {
|
|||
/**
|
||||
* Sign an RRset.
|
||||
*
|
||||
* @param rrset
|
||||
* the RRset to sign -- any existing signatures are ignored.
|
||||
* @param keypars
|
||||
* a list of DnsKeyPair objects containing private keys.
|
||||
* @param start
|
||||
* the inception time for the resulting RRSIG records.
|
||||
* @param expire
|
||||
* the expiration time for the resulting RRSIG records.
|
||||
* @param rrset the RRset to sign -- any existing signatures are ignored.
|
||||
* @param keypars a list of DnsKeyPair objects containing private keys.
|
||||
* @param start the inception time for the resulting RRSIG records.
|
||||
* @param expire the expiration time for the resulting RRSIG records.
|
||||
* @return a list of RRSIGRecord objects.
|
||||
*/
|
||||
public List<RRSIGRecord> signRRset(RRset rrset, List<DnsKeyPair> keypairs, Instant start,
|
||||
Instant expire) throws IOException,
|
||||
GeneralSecurityException {
|
||||
if (rrset == null || keypairs == null)
|
||||
return null;
|
||||
return new ArrayList<>();
|
||||
|
||||
// default start to now, expire to start + 1 second.
|
||||
if (start == null)
|
||||
start = Instant.now();
|
||||
if (expire == null)
|
||||
expire = start.plusSeconds(1);
|
||||
if (keypairs.size() == 0)
|
||||
return null;
|
||||
if (keypairs.isEmpty())
|
||||
return new ArrayList<>();
|
||||
|
||||
if (mVerboseSigning) {
|
||||
log.info("Signing RRset:");
|
||||
|
@ -143,9 +132,9 @@ public class JCEDnsSecSigner {
|
|||
}
|
||||
|
||||
// first, pre-calculate the RRset bytes.
|
||||
byte[] rrset_data = SignUtils.generateCanonicalRRsetData(rrset, 0, 0);
|
||||
byte[] rrsetData = SignUtils.generateCanonicalRRsetData(rrset, 0, 0);
|
||||
|
||||
ArrayList<RRSIGRecord> sigs = new ArrayList<RRSIGRecord>(keypairs.size());
|
||||
ArrayList<RRSIGRecord> sigs = new ArrayList<>(keypairs.size());
|
||||
|
||||
// for each keypair, sign the RRset.
|
||||
for (DnsKeyPair pair : keypairs) {
|
||||
|
@ -155,13 +144,13 @@ public class JCEDnsSecSigner {
|
|||
|
||||
RRSIGRecord presig = SignUtils.generatePreRRSIG(rrset, keyrec, start, expire,
|
||||
rrset.getTTL());
|
||||
byte[] sign_data = SignUtils.generateSigData(rrset_data, presig);
|
||||
byte[] signData = SignUtils.generateSigData(rrsetData, presig);
|
||||
|
||||
if (mVerboseSigning) {
|
||||
log.info("Canonical pre-signature data to sign with key "
|
||||
+ keyrec.getName().toString() + "/" + keyrec.getAlgorithm() + "/"
|
||||
+ keyrec.getFootprint() + ":");
|
||||
log.info(hexdump.dump(null, sign_data));
|
||||
log.info(hexdump.dump(null, signData));
|
||||
}
|
||||
|
||||
Signature signer = pair.getSigner();
|
||||
|
@ -174,7 +163,7 @@ public class JCEDnsSecSigner {
|
|||
}
|
||||
|
||||
// sign the data.
|
||||
signer.update(sign_data);
|
||||
signer.update(signData);
|
||||
byte[] sig = signer.sign();
|
||||
|
||||
if (mVerboseSigning) {
|
||||
|
@ -184,7 +173,7 @@ public class JCEDnsSecSigner {
|
|||
|
||||
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
|
||||
// Convert to RFC 2536 format, if necessary.
|
||||
if (algs.baseType(pair.getDNSKEYAlgorithm()) == DnsKeyAlgorithm.DSA) {
|
||||
if (algs.baseType(pair.getDNSKEYAlgorithm()) == DnsKeyAlgorithm.BaseAlgorithm.DSA) {
|
||||
DSAPublicKey pk = (DSAPublicKey) pair.getPublic();
|
||||
sig = SignUtils.convertDSASignature(pk.getParams(), sig);
|
||||
}
|
||||
|
@ -206,12 +195,9 @@ public class JCEDnsSecSigner {
|
|||
/**
|
||||
* Create a completely self-signed DNSKEY RRset.
|
||||
*
|
||||
* @param keypairs
|
||||
* the public & private keypairs to use in the keyset.
|
||||
* @param start
|
||||
* the RRSIG inception time.
|
||||
* @param expire
|
||||
* the RRSIG expiration time.
|
||||
* @param keypairs the public & private keypairs to use in the keyset.
|
||||
* @param start the RRSIG inception time.
|
||||
* @param expire the RRSIG expiration time.
|
||||
* @return a signed RRset.
|
||||
*/
|
||||
public RRset makeKeySet(List<DnsKeyPair> keypairs, Instant start, Instant expire)
|
||||
|
@ -236,67 +222,55 @@ public class JCEDnsSecSigner {
|
|||
/**
|
||||
* Conditionally sign an RRset and add it to the toList.
|
||||
*
|
||||
* @param toList
|
||||
* the list to which we are adding the processed RRsets.
|
||||
* @param zonename
|
||||
* the zone apex name.
|
||||
* @param rrset
|
||||
* the RRset under consideration.
|
||||
* @param kskpairs
|
||||
* the List of KSKs..
|
||||
* @param zskpairs
|
||||
* the List of zone keys.
|
||||
* @param start
|
||||
* the RRSIG inception time.
|
||||
* @param expire
|
||||
* the RRSIG expiration time.
|
||||
* @param fullySignKeyset
|
||||
* if true, sign the zone apex keyset with both KSKs and
|
||||
* ZSKs.
|
||||
* @param last_cut
|
||||
* the name of the last delegation point encountered.
|
||||
* @param toList the list to which we are adding the processed RRsets.
|
||||
* @param zonename the zone apex name.
|
||||
* @param rrset the RRset under consideration.
|
||||
* @param kskpairs the List of KSKs..
|
||||
* @param zskpairs the List of zone keys.
|
||||
* @param start the RRSIG inception time.
|
||||
* @param expire the RRSIG expiration time.
|
||||
* @param fullySignKeyset if true, sign the zone apex keyset with both KSKs
|
||||
* and ZSKs.
|
||||
* @param lastCut the name of the last delegation point encountered.
|
||||
*
|
||||
* @return the name of the new last_cut.
|
||||
*/
|
||||
private Name addRRset(List<Record> toList, Name zonename, RRset rrset,
|
||||
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs, Instant start,
|
||||
Instant expire, boolean fullySignKeyset, Name last_cut,
|
||||
Name last_dname) throws IOException, GeneralSecurityException {
|
||||
Instant expire, boolean fullySignKeyset, Name lastCut,
|
||||
Name lastDname) throws IOException, GeneralSecurityException {
|
||||
// add the records themselves
|
||||
rrset.rrs().forEach(record -> {
|
||||
toList.add(record);
|
||||
});
|
||||
rrset.rrs().forEach(toList::add);
|
||||
|
||||
int type = SignUtils.recordSecType(zonename, rrset.getName(), rrset.getType(),
|
||||
last_cut, last_dname);
|
||||
lastCut, lastDname);
|
||||
|
||||
// we don't sign non-normal sets (delegations, glue, invalid).
|
||||
if (type == SignUtils.RR_DELEGATION) {
|
||||
return rrset.getName();
|
||||
}
|
||||
if (type == SignUtils.RR_GLUE || type == SignUtils.RR_INVALID) {
|
||||
return last_cut;
|
||||
return lastCut;
|
||||
}
|
||||
|
||||
// check for the zone apex keyset.
|
||||
if (rrset.getName().equals(zonename) && rrset.getType() == Type.DNSKEY) {
|
||||
if (rrset.getName().equals(zonename) && rrset.getType() == Type.DNSKEY && kskpairs != null && !kskpairs.isEmpty()) {
|
||||
// if we have ksks, sign the keyset with them, otherwise we will just sign
|
||||
// them with the zsks.
|
||||
if (kskpairs != null && kskpairs.size() > 0) {
|
||||
List<RRSIGRecord> sigs = signRRset(rrset, kskpairs, start, expire);
|
||||
toList.addAll(sigs);
|
||||
List<RRSIGRecord> sigs = signRRset(rrset, kskpairs, start, expire);
|
||||
toList.addAll(sigs);
|
||||
|
||||
// If we aren't going to sign with all the keys, bail out now.
|
||||
if (!fullySignKeyset)
|
||||
return lastCut;
|
||||
|
||||
// If we aren't going to sign with all the keys, bail out now.
|
||||
if (!fullySignKeyset)
|
||||
return last_cut;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, we are OK to sign this set.
|
||||
List<RRSIGRecord> sigs = signRRset(rrset, zskpairs, start, expire);
|
||||
toList.addAll(sigs);
|
||||
|
||||
return last_cut;
|
||||
return lastCut;
|
||||
}
|
||||
|
||||
// Various NSEC/NSEC3 generation modes
|
||||
|
@ -311,49 +285,31 @@ public class JCEDnsSecSigner {
|
|||
* Opt-Out, etc.) External users of this class are expected to use the
|
||||
* appropriate public signZone* methods instead of this.
|
||||
*
|
||||
* @param zonename
|
||||
* The name of the zone
|
||||
* @param records
|
||||
* The records comprising the zone. They do not have to
|
||||
* be in any
|
||||
* particular order, as this method will order them as
|
||||
* necessary.
|
||||
* @param kskpairs
|
||||
* The key pairs designated as "key signing keys"
|
||||
* @param zskpairs
|
||||
* The key pairs designated as "zone signing keys"
|
||||
* @param start
|
||||
* The RRSIG inception time
|
||||
* @param expire
|
||||
* The RRSIG expiration time
|
||||
* @param fullySignKeyset
|
||||
* If true, all keys (ksk or zsk) will sign the DNSKEY
|
||||
* RRset. If
|
||||
* false, only the ksks will sign it.
|
||||
* @param ds_digest_alg
|
||||
* The hash algorithm to use for generating DS records
|
||||
* @param zonename The name of the zone
|
||||
* @param records The records comprising the zone. They do not have to
|
||||
* be in any particular order, as this method will
|
||||
* order them as necessary.
|
||||
* @param kskpairs The key pairs designated as "key signing keys"
|
||||
* @param zskpairs The key pairs designated as "zone signing keys"
|
||||
* @param start The RRSIG inception time
|
||||
* @param expire The RRSIG expiration time
|
||||
* @param fullySignKeyset If true, all keys (ksk or zsk) will sign the DNSKEY
|
||||
* RRset. If false, only the ksks will sign it.
|
||||
* @param dsDigestAlg The hash algorithm to use for generating DS records
|
||||
* (DSRecord.SHA1_DIGEST_ID, e.g.)
|
||||
* @param mode
|
||||
* The NSEC/NSEC3 generation mode: NSEC_MODE, NSEC3_MODE,
|
||||
* NSEC3_OPTOUT_MODE, etc.
|
||||
* @param includedNames
|
||||
* When using an Opt-In/Opt-Out mode, the names listed
|
||||
* here will be
|
||||
* included in the NSEC/NSEC3 chain regardless
|
||||
* @param salt
|
||||
* When using an NSEC3 mode, use this salt.
|
||||
* @param iterations
|
||||
* When using an NSEC3 mode, use this number of
|
||||
* @param mode The NSEC/NSEC3 generation mode: NSEC_MODE,
|
||||
* NSEC3_MODE, NSEC3_OPTOUT_MODE, etc.
|
||||
* @param includedNames When using an Opt-In/Opt-Out mode, the names listed
|
||||
* here will be included in the NSEC/NSEC3 chain
|
||||
* regardless
|
||||
* @param salt When using an NSEC3 mode, use this salt.
|
||||
* @param iterations When using an NSEC3 mode, use this number of
|
||||
* iterations
|
||||
* @param beConservative
|
||||
* If true, then only turn on the Opt-In flag when there
|
||||
* are insecure
|
||||
* delegations in the span. Currently this only works for
|
||||
* NSEC_EXP_OPT_IN mode.
|
||||
* @param nsec3paramttl
|
||||
* The TTL to use for the generated NSEC3PARAM record.
|
||||
* Negative
|
||||
* values will use the SOA TTL.
|
||||
* @param beConservative If true, then only turn on the Opt-In flag when
|
||||
* there are insecure delegations in the span.
|
||||
* Currently this only works for NSEC_EXP_OPT_IN mode.
|
||||
* @param nsec3paramttl The TTL to use for the generated NSEC3PARAM record.
|
||||
* Negative values will use the SOA TTL.
|
||||
* @return an ordered list of {@link org.xbill.DNS.Record} objects,
|
||||
* representing the signed zone.
|
||||
*
|
||||
|
@ -363,7 +319,7 @@ public class JCEDnsSecSigner {
|
|||
private List<Record> signZone(Name zonename, List<Record> records,
|
||||
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
|
||||
Instant start, Instant expire, boolean fullySignKeyset,
|
||||
int ds_digest_alg, int mode, List<Name> includedNames,
|
||||
int dsDigestAlg, int mode, List<Name> includedNames,
|
||||
byte[] salt, int iterations, long nsec3paramttl,
|
||||
boolean beConservative) throws IOException,
|
||||
GeneralSecurityException {
|
||||
|
@ -380,7 +336,7 @@ public class JCEDnsSecSigner {
|
|||
|
||||
// Generate DS records. This replaces any non-zone-apex DNSKEY RRs with DS
|
||||
// RRs.
|
||||
SignUtils.generateDSRecords(zonename, records, ds_digest_alg);
|
||||
SignUtils.generateDSRecords(zonename, records, dsDigestAlg);
|
||||
|
||||
// Generate the NSEC or NSEC3 records based on 'mode'
|
||||
switch (mode) {
|
||||
|
@ -398,6 +354,8 @@ public class JCEDnsSecSigner {
|
|||
SignUtils.generateOptInNSECRecords(zonename, records, includedNames,
|
||||
beConservative);
|
||||
break;
|
||||
default:
|
||||
throw new NoSuchAlgorithmException("Unknown NSEC/NSEC3 mode: " + mode);
|
||||
}
|
||||
|
||||
// Re-sort so we can assemble into rrsets.
|
||||
|
@ -405,9 +363,9 @@ public class JCEDnsSecSigner {
|
|||
|
||||
// Assemble into RRsets and sign.
|
||||
RRset rrset = new RRset();
|
||||
ArrayList<Record> signed_records = new ArrayList<Record>();
|
||||
Name last_cut = null;
|
||||
Name last_dname = null;
|
||||
ArrayList<Record> signedRecords = new ArrayList<>();
|
||||
Name lastCut = null;
|
||||
Name lastDname = null;
|
||||
|
||||
for (ListIterator<Record> i = records.listIterator(); i.hasNext();) {
|
||||
Record r = i.next();
|
||||
|
@ -430,48 +388,38 @@ public class JCEDnsSecSigner {
|
|||
|
||||
// add the RRset to the list of signed_records, regardless of
|
||||
// whether or not we actually end up signing the set.
|
||||
last_cut = addRRset(signed_records, zonename, rrset, kskpairs, zskpairs, start,
|
||||
expire, fullySignKeyset, last_cut, last_dname);
|
||||
lastCut = addRRset(signedRecords, zonename, rrset, kskpairs, zskpairs, start,
|
||||
expire, fullySignKeyset, lastCut, lastDname);
|
||||
if (rrset.getType() == Type.DNAME)
|
||||
last_dname = rrset.getName();
|
||||
lastDname = rrset.getName();
|
||||
|
||||
rrset.clear();
|
||||
rrset.addRR(r);
|
||||
}
|
||||
|
||||
// add the last RR set
|
||||
addRRset(signed_records, zonename, rrset, kskpairs, zskpairs, start, expire,
|
||||
fullySignKeyset, last_cut, last_dname);
|
||||
addRRset(signedRecords, zonename, rrset, kskpairs, zskpairs, start, expire,
|
||||
fullySignKeyset, lastCut, lastDname);
|
||||
|
||||
return signed_records;
|
||||
return signedRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a zone, sign it using standard NSEC records.
|
||||
*
|
||||
* @param zonename
|
||||
* The name of the zone.
|
||||
* @param records
|
||||
* The records comprising the zone. They do not have to
|
||||
* be in any
|
||||
* particular order, as this method will order them as
|
||||
* necessary.
|
||||
* @param kskpairs
|
||||
* The key pairs that are designated as "key signing
|
||||
* @param zonename The name of the zone.
|
||||
* @param records The records comprising the zone. They do not have to
|
||||
* be in any particular order, as this method will
|
||||
* order them as necessary.
|
||||
* @param kskpairs The key pairs that are designated as "key signing
|
||||
* keys".
|
||||
* @param zskpairs
|
||||
* This key pairs that are designated as "zone signing
|
||||
* @param zskpairs This key pairs that are designated as "zone signing
|
||||
* keys".
|
||||
* @param start
|
||||
* The RRSIG inception time.
|
||||
* @param expire
|
||||
* The RRSIG expiration time.
|
||||
* @param fullySignKeyset
|
||||
* Sign the zone apex keyset with all available keys
|
||||
* (instead of just
|
||||
* the key signing keys).
|
||||
* @param ds_digest_alg
|
||||
* The digest algorithm to use when generating DS
|
||||
* @param start The RRSIG inception time.
|
||||
* @param expire The RRSIG expiration time.
|
||||
* @param fullySignKeyset Sign the zone apex keyset with all available keys
|
||||
* (instead of just the key signing keys).
|
||||
* @param dsDigestAlg The digest algorithm to use when generating DS
|
||||
* records.
|
||||
*
|
||||
* @return an ordered list of {@link org.xbill.DNS.Record} objects,
|
||||
|
@ -480,58 +428,42 @@ public class JCEDnsSecSigner {
|
|||
public List<Record> signZone(Name zonename, List<Record> records,
|
||||
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
|
||||
Instant start, Instant expire, boolean fullySignKeyset,
|
||||
int ds_digest_alg) throws IOException,
|
||||
int dsDigestAlg) throws IOException,
|
||||
GeneralSecurityException {
|
||||
return signZone(zonename, records, kskpairs, zskpairs, start, expire,
|
||||
fullySignKeyset, ds_digest_alg, NSEC_MODE, null, null, 0, 0, false);
|
||||
fullySignKeyset, dsDigestAlg, NSEC_MODE, null, null, 0, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a zone, sign it using NSEC3 records.
|
||||
*
|
||||
* @param signer
|
||||
* A signer (utility) object used to actually sign stuff.
|
||||
* @param zonename
|
||||
* The name of the zone being signed.
|
||||
* @param records
|
||||
* The records comprising the zone. They do not have to
|
||||
* be in any
|
||||
* particular order, as this method will order them as
|
||||
* necessary.
|
||||
* @param kskpairs
|
||||
* The key pairs that are designated as "key signing
|
||||
* @param signer A signer (utility) object used to actually sign
|
||||
* stuff.
|
||||
* @param zonename The name of the zone being signed.
|
||||
* @param records The records comprising the zone. They do not have to
|
||||
* be in any particular order, as this method will
|
||||
* order them as necessary.
|
||||
* @param kskpairs The key pairs that are designated as "key signing
|
||||
* keys".
|
||||
* @param zskpairs
|
||||
* This key pairs that are designated as "zone signing
|
||||
* @param zskpairs This key pairs that are designated as "zone signing
|
||||
* keys".
|
||||
* @param start
|
||||
* The RRSIG inception time.
|
||||
* @param expire
|
||||
* The RRSIG expiration time.
|
||||
* @param fullySignKeyset
|
||||
* If true then the DNSKEY RRset will be signed by all
|
||||
* available
|
||||
* keys, if false, only the key signing keys.
|
||||
* @param useOptOut
|
||||
* If true, insecure delegations will be omitted from the
|
||||
* NSEC3
|
||||
* chain, and all NSEC3 records will have the Opt-Out
|
||||
* flag set.
|
||||
* @param includedNames
|
||||
* A list of names to include in the NSEC3 chain
|
||||
* @param start The RRSIG inception time.
|
||||
* @param expire The RRSIG expiration time.
|
||||
* @param fullySignKeyset If true then the DNSKEY RRset will be signed by all
|
||||
* available keys, if false, only the key signing keys.
|
||||
* @param useOptOut If true, insecure delegations will be omitted from
|
||||
* the NSEC3 chain, and all NSEC3 records will have the
|
||||
* Opt-Out flag set.
|
||||
* @param includedNames A list of names to include in the NSEC3 chain
|
||||
* regardless.
|
||||
* @param salt
|
||||
* The salt to use for the NSEC3 hashing. null means no
|
||||
* @param salt The salt to use for the NSEC3 hashing. null means no
|
||||
* salt.
|
||||
* @param iterations
|
||||
* The number of iterations to use for the NSEC3 hashing.
|
||||
* @param ds_digest_alg
|
||||
* The digest algorithm to use when generating DS
|
||||
* @param iterations The number of iterations to use for the NSEC3
|
||||
* hashing.
|
||||
* @param dsDigestAlg The digest algorithm to use when generating DS
|
||||
* records.
|
||||
* @param nsec3paramttl
|
||||
* The TTL to use for the generated NSEC3PARAM record.
|
||||
* Negative
|
||||
* values will use the SOA TTL.
|
||||
* @param nsec3paramttl The TTL to use for the generated NSEC3PARAM record.
|
||||
* Negative values will use the SOA TTL.
|
||||
* @return an ordered list of {@link org.xbill.DNS.Record} objects,
|
||||
* representing the signed zone.
|
||||
*
|
||||
|
@ -542,16 +474,16 @@ public class JCEDnsSecSigner {
|
|||
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
|
||||
Instant start, Instant expire, boolean fullySignKeyset,
|
||||
boolean useOptOut, List<Name> includedNames,
|
||||
byte[] salt, int iterations, int ds_digest_alg,
|
||||
byte[] salt, int iterations, int dsDigestAlg,
|
||||
long nsec3paramttl) throws IOException,
|
||||
GeneralSecurityException {
|
||||
if (useOptOut) {
|
||||
return signZone(zonename, records, kskpairs, zskpairs, start, expire,
|
||||
fullySignKeyset, ds_digest_alg, NSEC3_OPTOUT_MODE, includedNames,
|
||||
fullySignKeyset, dsDigestAlg, NSEC3_OPTOUT_MODE, includedNames,
|
||||
salt, iterations, nsec3paramttl, false);
|
||||
} else {
|
||||
return signZone(zonename, records, kskpairs, zskpairs, start, expire,
|
||||
fullySignKeyset, ds_digest_alg, NSEC3_MODE, null, salt, iterations,
|
||||
fullySignKeyset, dsDigestAlg, NSEC3_MODE, null, salt, iterations,
|
||||
nsec3paramttl, false);
|
||||
}
|
||||
}
|
||||
|
@ -560,37 +492,25 @@ public class JCEDnsSecSigner {
|
|||
* Given a zone, sign it using experimental Opt-In NSEC records (see RFC
|
||||
* 4956).
|
||||
*
|
||||
* @param zonename
|
||||
* the name of the zone.
|
||||
* @param records
|
||||
* the records comprising the zone. They do not have
|
||||
* to be in any
|
||||
* particular order, as this method will order them
|
||||
* as necessary.
|
||||
* @param kskpairs
|
||||
* the key pairs that are designated as "key signing
|
||||
* keys".
|
||||
* @param zskpairs
|
||||
* this key pairs that are designated as "zone
|
||||
* @param zonename the name of the zone.
|
||||
* @param records the records comprising the zone. They do not
|
||||
* have to be in any particular order, as this
|
||||
* method will order them as necessary.
|
||||
* @param kskpairs the key pairs that are designated as "key
|
||||
* signing keys".
|
||||
* @param start
|
||||
* the RRSIG inception time.
|
||||
* @param expire
|
||||
* the RRSIG expiration time.
|
||||
* @param useConservativeOptIn
|
||||
* if true, Opt-In NSEC records will only be
|
||||
* generated if there are
|
||||
* insecure, unsigned delegations in the span.
|
||||
* @param fullySignKeyset
|
||||
* sign the zone apex keyset with all available
|
||||
* @param zskpairs this key pairs that are designated as "zone
|
||||
* signing keys".
|
||||
* @param start the RRSIG inception time.
|
||||
* @param expire the RRSIG expiration time.
|
||||
* @param useConservativeOptIn if true, Opt-In NSEC records will only be
|
||||
* generated if there are insecure, unsigned
|
||||
* delegations in the span.
|
||||
* @param fullySignKeyset sign the zone apex keyset with all available
|
||||
* keys.
|
||||
* @param ds_digest_alg
|
||||
* The digest algorithm to use when generating DS
|
||||
* @param dsDigestAlg The digest algorithm to use when generating DS
|
||||
* records.
|
||||
* @param NSECIncludeNames
|
||||
* names that are to be included in the NSEC chain
|
||||
* regardless. This
|
||||
* may be null.
|
||||
* @param nsecIncludeNames names that are to be included in the NSEC chain
|
||||
* regardless. This may be null.
|
||||
* @return an ordered list of {@link org.xbill.DNS.Record} objects,
|
||||
* representing the signed zone.
|
||||
*/
|
||||
|
@ -598,12 +518,12 @@ public class JCEDnsSecSigner {
|
|||
List<DnsKeyPair> kskpairs, List<DnsKeyPair> zskpairs,
|
||||
Instant start, Instant expire,
|
||||
boolean useConservativeOptIn,
|
||||
boolean fullySignKeyset, List<Name> NSECIncludeNames,
|
||||
int ds_digest_alg) throws IOException,
|
||||
boolean fullySignKeyset, List<Name> nsecIncludeNames,
|
||||
int dsDigestAlg) throws IOException,
|
||||
GeneralSecurityException {
|
||||
|
||||
return signZone(zonename, records, kskpairs, zskpairs, start, expire,
|
||||
fullySignKeyset, ds_digest_alg, NSEC_EXP_OPT_IN, NSECIncludeNames,
|
||||
fullySignKeyset, dsDigestAlg, NSEC_EXP_OPT_IN, nsecIncludeNames,
|
||||
null, 0, 0, useConservativeOptIn);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.xbill.DNS.Type;
|
|||
|
||||
public class RecordComparator implements Comparator<Record> {
|
||||
public RecordComparator() {
|
||||
// nothing to initialize
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,15 +66,15 @@ public class RecordComparator implements Comparator<Record> {
|
|||
}
|
||||
|
||||
private int compareRDATA(Record a, Record b) {
|
||||
byte[] a_rdata = a.rdataToWireCanonical();
|
||||
byte[] b_rdata = b.rdataToWireCanonical();
|
||||
byte[] aRdata = a.rdataToWireCanonical();
|
||||
byte[] bRdata = b.rdataToWireCanonical();
|
||||
|
||||
for (int i = 0; i < a_rdata.length && i < b_rdata.length; i++) {
|
||||
int n = (a_rdata[i] & 0xFF) - (b_rdata[i] & 0xFF);
|
||||
for (int i = 0; i < aRdata.length && i < bRdata.length; i++) {
|
||||
int n = (aRdata[i] & 0xFF) - (bRdata[i] & 0xFF);
|
||||
if (n != 0)
|
||||
return n;
|
||||
}
|
||||
return (a_rdata.length - b_rdata.length);
|
||||
return (aRdata.length - bRdata.length);
|
||||
}
|
||||
|
||||
public int compare(Record a, Record b) {
|
||||
|
@ -88,27 +89,27 @@ public class RecordComparator implements Comparator<Record> {
|
|||
if (res != 0)
|
||||
return res;
|
||||
|
||||
int a_type = a.getType();
|
||||
int b_type = b.getType();
|
||||
int sig_type = 0;
|
||||
int aType = a.getType();
|
||||
int bType = b.getType();
|
||||
int sigType = 0;
|
||||
|
||||
if (a_type == Type.RRSIG) {
|
||||
a_type = ((RRSIGRecord) a).getTypeCovered();
|
||||
if (b_type != Type.RRSIG)
|
||||
sig_type = 1;
|
||||
if (aType == Type.RRSIG) {
|
||||
aType = ((RRSIGRecord) a).getTypeCovered();
|
||||
if (bType != Type.RRSIG)
|
||||
sigType = 1;
|
||||
}
|
||||
if (b_type == Type.RRSIG) {
|
||||
b_type = ((RRSIGRecord) b).getTypeCovered();
|
||||
if (bType == Type.RRSIG) {
|
||||
bType = ((RRSIGRecord) b).getTypeCovered();
|
||||
if (a.getType() != Type.RRSIG)
|
||||
sig_type = -1;
|
||||
sigType = -1;
|
||||
}
|
||||
|
||||
res = compareTypes(a_type, b_type);
|
||||
res = compareTypes(aType, bType);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
if (sig_type != 0)
|
||||
return sig_type;
|
||||
if (sigType != 0)
|
||||
return sigType;
|
||||
|
||||
return compareRDATA(a, b);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,7 +37,7 @@ public class TypeMap {
|
|||
private Set<Integer> typeSet;
|
||||
|
||||
public TypeMap() {
|
||||
this.typeSet = new HashSet<Integer>();
|
||||
this.typeSet = new HashSet<>();
|
||||
}
|
||||
|
||||
/** Add the given type to the typemap. */
|
||||
|
@ -78,20 +78,20 @@ public class TypeMap {
|
|||
TypeMap typemap = new TypeMap();
|
||||
|
||||
int page;
|
||||
int byte_length;
|
||||
int byteLength;
|
||||
|
||||
while (m < map.length) {
|
||||
page = map[m++];
|
||||
byte_length = map[m++];
|
||||
byteLength = map[m++];
|
||||
|
||||
for (int i = 0; i < byte_length; i++) {
|
||||
for (int i = 0; i < byteLength; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if ((map[m + i] & (1 << (7 - j))) != 0) {
|
||||
if (((map[m + i] & 0xFF) & (1 << (7 - j))) != 0) {
|
||||
typemap.set((page << 8) + (i * 8) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
m += byte_length;
|
||||
m += byteLength;
|
||||
}
|
||||
|
||||
return typemap;
|
||||
|
@ -115,7 +115,7 @@ public class TypeMap {
|
|||
int[] types = getTypes();
|
||||
Arrays.sort(types);
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (i > 0)
|
||||
|
@ -129,16 +129,16 @@ public class TypeMap {
|
|||
protected static void mapToWire(DNSOutput out, int[] types, int base, int start, int end) {
|
||||
// calculate the length of this map by looking at the largest
|
||||
// typecode in this section.
|
||||
int max_type = types[end - 1] & 0xFF;
|
||||
int map_length = (max_type / 8) + 1;
|
||||
int maxType = types[end - 1] & 0xFF;
|
||||
int mapLength = (maxType / 8) + 1;
|
||||
|
||||
// write the map "header" -- the base and the length of the map.
|
||||
out.writeU8(base & 0xFF);
|
||||
out.writeU8(map_length & 0xFF);
|
||||
out.writeU8(mapLength & 0xFF);
|
||||
|
||||
// allocate a temporary scratch space for caculating the actual
|
||||
// bitmap.
|
||||
byte[] map = new byte[map_length];
|
||||
byte[] map = new byte[mapLength];
|
||||
|
||||
// for each type in our sub-array, set its corresponding bit in the map.
|
||||
for (int i = start; i < end; i++) {
|
||||
|
@ -179,7 +179,7 @@ public class TypeMap {
|
|||
}
|
||||
|
||||
public int[] getTypes() {
|
||||
Integer[] a = (Integer[]) typeSet.toArray(integerArray);
|
||||
Integer[] a = typeSet.toArray(integerArray);
|
||||
|
||||
int[] res = new int[a.length];
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
|
@ -189,8 +189,8 @@ public class TypeMap {
|
|||
return res;
|
||||
}
|
||||
|
||||
public static int[] fromWireToTypes(byte[] wire_fmt) {
|
||||
return TypeMap.fromBytes(wire_fmt).getTypes();
|
||||
public static int[] fromWireToTypes(byte[] wireFmt) {
|
||||
return TypeMap.fromBytes(wireFmt).getTypes();
|
||||
}
|
||||
|
||||
public static byte[] fromTypesToWire(int[] types) {
|
||||
|
|
|
@ -39,6 +39,10 @@ import org.xbill.DNS.Type;
|
|||
*/
|
||||
|
||||
public class ZoneUtils {
|
||||
|
||||
private ZoneUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a zone file.
|
||||
*
|
||||
|
@ -53,19 +57,10 @@ public class ZoneUtils {
|
|||
* if something goes wrong reading the zone file.
|
||||
*/
|
||||
public static List<Record> readZoneFile(String zonefile, Name origin) throws IOException {
|
||||
ArrayList<Record> records = new ArrayList<Record>();
|
||||
Master m;
|
||||
try {
|
||||
if (zonefile.equals("-")) {
|
||||
m = new Master(System.in);
|
||||
} else {
|
||||
m = new Master(zonefile, origin);
|
||||
}
|
||||
|
||||
ArrayList<Record> records = new ArrayList<>();
|
||||
try (Master m = zonefile.equals("-") ? new Master(System.in) : new Master(zonefile, origin)) {
|
||||
Record r = null;
|
||||
|
||||
while ((r = m.nextRecord()) != null) {
|
||||
|
||||
records.add(r);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -120,7 +115,7 @@ public class ZoneUtils {
|
|||
}
|
||||
|
||||
public static List<Record> findRRs(List<Record> records, Name name, int type) {
|
||||
List<Record> res = new ArrayList<Record>();
|
||||
List<Record> res = new ArrayList<>();
|
||||
for (Record r : records) {
|
||||
if (r.getName().equals(name) && r.getType() == type) {
|
||||
res.add(r);
|
||||
|
|
|
@ -88,10 +88,12 @@ public class ZoneVerifier {
|
|||
public boolean equals(Object o) {
|
||||
return super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
boolean getMark() {
|
||||
return mIsMarked;
|
||||
}
|
||||
|
@ -148,7 +150,8 @@ public class ZoneVerifier {
|
|||
/**
|
||||
* Add a record to the various maps.
|
||||
*
|
||||
* @return TODO
|
||||
* @return true if the RR was added, false if it wasn't (because it was a
|
||||
* duplicate)
|
||||
*/
|
||||
private boolean addRR(Record r) {
|
||||
Name n = r.getName();
|
||||
|
@ -206,8 +209,8 @@ public class ZoneVerifier {
|
|||
* Given an unsorted list of records, load the node and rrset maps, as well as
|
||||
* determine the NSEC3 parameters and signing type.
|
||||
*
|
||||
* @param records
|
||||
* @return TODO
|
||||
* @param records an unsorted list of {@link org.xbill.DNS.Record} objects.
|
||||
* @return the number of errors encountered.
|
||||
*/
|
||||
private int calculateNodes(List<Record> records) {
|
||||
mNodeMap = new TreeMap<>();
|
||||
|
@ -251,7 +254,7 @@ public class ZoneVerifier {
|
|||
* Given a name, typeset, and name of the last zone cut, determine the node
|
||||
* type.
|
||||
*/
|
||||
private NodeType determineNodeType(Name n, Set<Integer> typeset, Name last_cut) {
|
||||
private NodeType determineNodeType(Name n, Set<Integer> typeset, Name lastCut) {
|
||||
// All RRs at the zone apex are normal
|
||||
if (n.equals(mZoneName))
|
||||
return NodeType.NORMAL;
|
||||
|
@ -263,7 +266,7 @@ public class ZoneVerifier {
|
|||
}
|
||||
// If the node is below a zone cut (either a delegation or DNAME), it is
|
||||
// glue.
|
||||
if (last_cut != null && n.subdomain(last_cut) && !n.equals(last_cut)) {
|
||||
if (lastCut != null && n.subdomain(lastCut) && !n.equals(lastCut)) {
|
||||
return NodeType.GLUE;
|
||||
}
|
||||
|
||||
|
@ -294,13 +297,13 @@ public class ZoneVerifier {
|
|||
*/
|
||||
private int processNodes() throws NoSuchAlgorithmException, TextParseException {
|
||||
int errors = 0;
|
||||
Name last_cut = null;
|
||||
Name lastCut = null;
|
||||
|
||||
for (Map.Entry<Name, Set<Integer>> entry : mNodeMap.entrySet()) {
|
||||
Name n = entry.getKey();
|
||||
Set<Integer> typeset = entry.getValue();
|
||||
|
||||
NodeType ntype = determineNodeType(n, typeset, last_cut);
|
||||
NodeType ntype = determineNodeType(n, typeset, lastCut);
|
||||
log.finest("Node " + n + " is type " + ntype);
|
||||
|
||||
// we can ignore glue/invalid RRs.
|
||||
|
@ -309,7 +312,7 @@ public class ZoneVerifier {
|
|||
|
||||
// record the last zone cut if this node is a zone cut.
|
||||
if (ntype == NodeType.DELEGATION || typeset.contains(Type.DNAME)) {
|
||||
last_cut = n;
|
||||
lastCut = n;
|
||||
}
|
||||
|
||||
// check all of the RRsets that should be signed
|
||||
|
|
Loading…
Reference in New Issue
Block a user