commit eb93599f0b975356271e4c40c143efac258d6be9 Author: David Blacka Date: Sat Aug 13 23:18:03 2005 +0000 initial import git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@13 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..4b0b18a --- /dev/null +++ b/.cvsignore @@ -0,0 +1,2 @@ +build +*.tar.gz diff --git a/README b/README new file mode 100644 index 0000000..3a0e770 --- /dev/null +++ b/README @@ -0,0 +1,51 @@ +java-dnssec-tools + +http://www.dnssec.verisignlabs.com/ + +Author: David Blacka (davidb@verisignlabs.com) + +This is a collection of DNSSEC tools written in Java. They are +intended to be an addition or replacement for the DNSSEC tools that +are part of BIND 9. + +These tools depend upon DNSjava (http://www.xbill.org/dnsjava), the +Jakarta Commons CLI and Logging libraries (http://jakarta.apache.org), +and Sun's Java Cryptography extensions. A copy of each of these +libraries is included in the distribution. + +See the "licenses" directory for the licensing information of this +package and the other packages that are distributed with it. + +Getting started: + +1. unpack the binary distribution: + +% tar zxvf java-dnssec-tools-x.x.x.tar.gz + +2. run the various tools from their unpacked location: + +% cd java-dnssec-tools-x.x.x +% ./bin/signZone.sh -h + +Building from source: + +1. unpack the source distribution, preferably into the same directory +that the binary distribution was unpacked. + +% tar zxvf java-dnssec-tools-x.x.x-src.tar.gz + +2. edit the build.properties file to suit your environment, +particularly the use of the jikes compiler. + +2. run Ant (see http://jakara.apache.org for information about the Ant +build tool). + +% ant + +--- + +Questions or comments may be directed to the author +(mailto:davidb@verisignlabs.com) or sent to the +dnssec@verisignlabs.com mailing list +(http://lists.verisignlabs.com/mailman/listinfo/dnssec). + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..176df46 --- /dev/null +++ b/VERSION @@ -0,0 +1,3 @@ +version=0.3.0 +tagversion=0_3_0 + diff --git a/bin/_makeDsSet.sh b/bin/_makeDsSet.sh new file mode 100755 index 0000000..d0e3bc5 --- /dev/null +++ b/bin/_makeDsSet.sh @@ -0,0 +1,27 @@ +#! /bin/sh + +thisdir=`dirname $0` +basedir=`cd $thisdir/..; pwd` + +ulimit -n `ulimit -H -n` + +if [ x$JAVA_HOME = x ]; then + JAVA_HOME=/usr/local/jdk1.3 + export JAVA_HOME +fi + +LD_LIBRARY_PATH=${basedir}/obj:${LD_LIBRARY_PATH} +export LD_LIBRARY_PATH + +# set the classpath +CLASSPATH=\ +$JAVA_HOME/jre/lib/rt.jar:\ +$basedir/obj/classes:\ +$basedir/lib/dnsjava.jar:\ +$basedir/lib/protomatter-1.1.5.jar:\ +$basedir/lib/jdom-B6.jar:\ +$basedir/lib/jce1_2_1.jar + +export CLASSPATH + +exec $JAVA_HOME/bin/java -Xmx64m com.nsi.dnssec.cl.MakeDSSet "$@" diff --git a/bin/_makeKeySet.sh b/bin/_makeKeySet.sh new file mode 100755 index 0000000..7a69b50 --- /dev/null +++ b/bin/_makeKeySet.sh @@ -0,0 +1,27 @@ +#! /bin/sh + +thisdir=`dirname $0` +basedir=`cd $thisdir/..; pwd` + +ulimit -n `ulimit -H -n` + +if [ x$JAVA_HOME = x ]; then + JAVA_HOME=/usr/local/jdk1.3 + export JAVA_HOME +fi + +LD_LIBRARY_PATH=${basedir}/obj:${LD_LIBRARY_PATH} +export LD_LIBRARY_PATH + +# set the classpath +CLASSPATH=\ +$JAVA_HOME/jre/lib/rt.jar:\ +$basedir/obj/classes:\ +$basedir/lib/dnsjava.jar:\ +$basedir/lib/protomatter-1.1.5.jar:\ +$basedir/lib/jdom-B6.jar:\ +$basedir/lib/jce1_2_1.jar + +export CLASSPATH + +exec $JAVA_HOME/bin/java -Xmx64m com.nsi.dnssec.cl.MakeKeySet "$@" diff --git a/bin/jdnssec-keygen b/bin/jdnssec-keygen new file mode 100755 index 0000000..6edaaf0 --- /dev/null +++ b/bin/jdnssec-keygen @@ -0,0 +1,14 @@ +#! /bin/sh + +thisdir=`dirname $0` +basedir=`cd $thisdir/..; pwd` + +ulimit -n `ulimit -H -n` + +# set the classpath +for i in $basedir/lib/*.jar $basedir/lib/*.zip; do + CLASSPATH="$CLASSPATH":"$i" +done +export CLASSPATH + +exec java com.verisignlabs.dnssec.cl.KeyGen "$@" diff --git a/bin/jdnssec-signzone b/bin/jdnssec-signzone new file mode 100755 index 0000000..726e7d6 --- /dev/null +++ b/bin/jdnssec-signzone @@ -0,0 +1,14 @@ +#! /bin/sh + +thisdir=`dirname $0` +basedir=`cd $thisdir/..; pwd` + +ulimit -n `ulimit -H -n` + +# set the classpath +for i in $basedir/lib/*.jar $basedir/lib/*.zip; do + CLASSPATH="$CLASSPATH":"$i" +done +export CLASSPATH + +exec java com.verisignlabs.dnssec.cl.SignZone "$@" diff --git a/bin/jdnssec-verifyzone b/bin/jdnssec-verifyzone new file mode 100755 index 0000000..c63165b --- /dev/null +++ b/bin/jdnssec-verifyzone @@ -0,0 +1,14 @@ +#! /bin/sh + +thisdir=`dirname $0` +basedir=`cd $thisdir/..; pwd` + +ulimit -n `ulimit -H -n` + +# set the classpath +for i in $basedir/lib/*.jar $basedir/lib/*.zip; do + CLASSPATH="$CLASSPATH":"$i" +done +export CLASSPATH + +exec java com.verisignlabs.dnssec.cl.VerifyZone "$@" diff --git a/build.properties b/build.properties new file mode 100644 index 0000000..f034b8c --- /dev/null +++ b/build.properties @@ -0,0 +1,3 @@ +build.compiler=jikes +#build.compiler=modern +build.deprecation=true diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..e6beaab --- /dev/null +++ b/build.xml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/classes/com/verisignlabs/dnssec/cl/KeyGen$CLIState.class b/build/classes/com/verisignlabs/dnssec/cl/KeyGen$CLIState.class new file mode 100644 index 0000000..b96173c Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/KeyGen$CLIState.class differ diff --git a/build/classes/com/verisignlabs/dnssec/cl/KeyGen.class b/build/classes/com/verisignlabs/dnssec/cl/KeyGen.class new file mode 100644 index 0000000..c06ac11 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/KeyGen.class differ diff --git a/build/classes/com/verisignlabs/dnssec/cl/SignZone$1.class b/build/classes/com/verisignlabs/dnssec/cl/SignZone$1.class new file mode 100644 index 0000000..767f80c Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/SignZone$1.class differ diff --git a/build/classes/com/verisignlabs/dnssec/cl/SignZone$CLIState.class b/build/classes/com/verisignlabs/dnssec/cl/SignZone$CLIState.class new file mode 100644 index 0000000..ee6098a Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/SignZone$CLIState.class differ diff --git a/build/classes/com/verisignlabs/dnssec/cl/SignZone$KeysetFileFilter.class b/build/classes/com/verisignlabs/dnssec/cl/SignZone$KeysetFileFilter.class new file mode 100644 index 0000000..caa9f30 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/SignZone$KeysetFileFilter.class differ diff --git a/build/classes/com/verisignlabs/dnssec/cl/SignZone.class b/build/classes/com/verisignlabs/dnssec/cl/SignZone.class new file mode 100644 index 0000000..dbc8a70 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/SignZone.class differ diff --git a/build/classes/com/verisignlabs/dnssec/cl/VerifyZone$CLIState.class b/build/classes/com/verisignlabs/dnssec/cl/VerifyZone$CLIState.class new file mode 100644 index 0000000..ab0eba4 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/VerifyZone$CLIState.class differ diff --git a/build/classes/com/verisignlabs/dnssec/cl/VerifyZone.class b/build/classes/com/verisignlabs/dnssec/cl/VerifyZone.class new file mode 100644 index 0000000..91821ac Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/cl/VerifyZone.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/BINDKeyUtils.class b/build/classes/com/verisignlabs/dnssec/security/BINDKeyUtils.class new file mode 100644 index 0000000..bfa50a2 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/BINDKeyUtils.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/ByteArrayComparator.class b/build/classes/com/verisignlabs/dnssec/security/ByteArrayComparator.class new file mode 100644 index 0000000..3cb8f59 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/ByteArrayComparator.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/DnsKeyConverter.class b/build/classes/com/verisignlabs/dnssec/security/DnsKeyConverter.class new file mode 100644 index 0000000..aced629 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/DnsKeyConverter.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/DnsKeyPair.class b/build/classes/com/verisignlabs/dnssec/security/DnsKeyPair.class new file mode 100644 index 0000000..0530d9b Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/DnsKeyPair.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/DnsSecVerifier$TrustedKeyStore.class b/build/classes/com/verisignlabs/dnssec/security/DnsSecVerifier$TrustedKeyStore.class new file mode 100644 index 0000000..8f481aa Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/DnsSecVerifier$TrustedKeyStore.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/DnsSecVerifier.class b/build/classes/com/verisignlabs/dnssec/security/DnsSecVerifier.class new file mode 100644 index 0000000..3a82b84 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/DnsSecVerifier.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/JCEDnsSecSigner.class b/build/classes/com/verisignlabs/dnssec/security/JCEDnsSecSigner.class new file mode 100644 index 0000000..5becfa9 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/JCEDnsSecSigner.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/RecordComparator.class b/build/classes/com/verisignlabs/dnssec/security/RecordComparator.class new file mode 100644 index 0000000..7073f34 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/RecordComparator.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/SignUtils$NodeInfo.class b/build/classes/com/verisignlabs/dnssec/security/SignUtils$NodeInfo.class new file mode 100644 index 0000000..227f49d Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/SignUtils$NodeInfo.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/SignUtils.class b/build/classes/com/verisignlabs/dnssec/security/SignUtils.class new file mode 100644 index 0000000..1bd841f Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/SignUtils.class differ diff --git a/build/classes/com/verisignlabs/dnssec/security/ZoneUtils.class b/build/classes/com/verisignlabs/dnssec/security/ZoneUtils.class new file mode 100644 index 0000000..4483412 Binary files /dev/null and b/build/classes/com/verisignlabs/dnssec/security/ZoneUtils.class differ diff --git a/build/lib/java-dnssec-tools.jar b/build/lib/java-dnssec-tools.jar new file mode 100644 index 0000000..dc1e161 Binary files /dev/null and b/build/lib/java-dnssec-tools.jar differ diff --git a/docs/.cvsignore b/docs/.cvsignore new file mode 100644 index 0000000..863d673 --- /dev/null +++ b/docs/.cvsignore @@ -0,0 +1 @@ +javadoc diff --git a/lib/commons-cli-1.0.jar b/lib/commons-cli-1.0.jar new file mode 100644 index 0000000..22a004e Binary files /dev/null and b/lib/commons-cli-1.0.jar differ diff --git a/lib/commons-logging.jar b/lib/commons-logging.jar new file mode 100644 index 0000000..aca1e41 Binary files /dev/null and b/lib/commons-logging.jar differ diff --git a/lib/dnsjava-1.5.2.jar b/lib/dnsjava-1.5.2.jar new file mode 100644 index 0000000..9abc126 Binary files /dev/null and b/lib/dnsjava-1.5.2.jar differ diff --git a/lib/jce1_2_2.jar b/lib/jce1_2_2.jar new file mode 100644 index 0000000..666a7e7 Binary files /dev/null and b/lib/jce1_2_2.jar differ diff --git a/licenses/commons-cli-LICENSE.txt b/licenses/commons-cli-LICENSE.txt new file mode 100644 index 0000000..58c4c55 --- /dev/null +++ b/licenses/commons-cli-LICENSE.txt @@ -0,0 +1,60 @@ +/* + * $Header: /home/radcvs/dnssec_pilot/sectools/licenses/commons-cli-LICENSE.txt,v 1.1 2003/04/02 22:40:49 davidb Exp $ + * $Revision: 1.1 $ + * $Date: 2003/04/02 22:40:49 $ + * + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2001 The Apache Software Foundation. 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 end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ diff --git a/licenses/commons-logging-LICENSE.txt b/licenses/commons-logging-LICENSE.txt new file mode 100644 index 0000000..c76c408 --- /dev/null +++ b/licenses/commons-logging-LICENSE.txt @@ -0,0 +1,60 @@ +/* + * $Header: /home/radcvs/dnssec_pilot/sectools/licenses/commons-logging-LICENSE.txt,v 1.1 2003/04/02 22:40:49 davidb Exp $ + * $Revision: 1.1 $ + * $Date: 2003/04/02 22:40:49 $ + * + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2001 The Apache Software Foundation. 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 end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ \ No newline at end of file diff --git a/licenses/dnsjava-LICENSE.txt b/licenses/dnsjava-LICENSE.txt new file mode 100644 index 0000000..0bfe227 --- /dev/null +++ b/licenses/dnsjava-LICENSE.txt @@ -0,0 +1,36 @@ +dnsjava is placed under the BSD license. Several files are also under +additional licenses; see the individual files for details. + +Copyright (c) 1999-2003, Brian Wellington +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + * Neither the name of the dnsjava project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + + +Final notes: + + - Thanks to Network Associates, Inc. for sponsoring some of the original + dnsjava work in 1999-2000. + + - Thanks to Nominum, Inc. for sponsoring some work on dnsjava from 2000-2003. diff --git a/licenses/java-dnssec-tools-LICENSE.txt b/licenses/java-dnssec-tools-LICENSE.txt new file mode 100644 index 0000000..6c0a161 --- /dev/null +++ b/licenses/java-dnssec-tools-LICENSE.txt @@ -0,0 +1,508 @@ +jDNSSECtools Copyright (c) 2002 VeriSign, Inc. +jDNSSECtools is released under the following license: + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/licenses/jce-LICENSE.txt b/licenses/jce-LICENSE.txt new file mode 100644 index 0000000..cd19cee --- /dev/null +++ b/licenses/jce-LICENSE.txt @@ -0,0 +1,169 @@ + Sun Microsystems, Inc. + Binary Code License Agreement + +READ THE TERMS OF THIS AGREEMENT AND ANY PROVIDED SUPPLEMENTAL LICENSE +TERMS (COLLECTIVELY "AGREEMENT") CAREFULLY BEFORE OPENING THE SOFTWARE +MEDIA PACKAGE. BY OPENING THE SOFTWARE MEDIA PACKAGE, YOU AGREE TO THE +TERMS OF THIS AGREEMENT. IF YOU ARE ACCESSING THE SOFTWARE ELECTRONICALLY, +INDICATE YOUR ACCEPTANCE OF THESE TERMS BY SELECTING THE "ACCEPT" BUTTON AT +THE END OF THIS AGREEMENT. IF YOU DO NOT AGREE TO ALL THESE TERMS, +PROMPTLY RETURN THE UNUSED SOFTWARE TO YOUR PLACE OF PURCHASE FOR A REFUND +OR, IF THE SOFTWARE IS ACCESSED ELECTRONICALLY, SELECT THE "DECLINE" BUTTON +AT THE END OF THIS AGREEMENT. + +1. LICENSE TO USE. Sun grants you a non-exclusive and non-transferable +license for the internal use only of the accompanying software and +documentation and any error corrections provided by Sun (collectively +"Software"), by the number of users and the class of computer hardware for +which the corresponding fee has been paid. + +2. RESTRICTIONS. Software is confidential and copyrighted. Title to +Software and all associated intellectual property rights is retained by Sun +and/or its licensors. Except as specifically authorized in any +Supplemental License Terms, you may not make copies of Software, other than +a single copy of Software for archival purposes. Unless enforcement is +prohibited by applicable law, you may not modify, decompile, or reverse +engineer Software. You acknowledge that Software is not designed, licensed +or intended for use in the design, construction, operation or maintenance +of any nuclear facility. Sun disclaims any express or implied warranty of +fitness for such uses. No right, title or interest in or to any trademark, +service mark, logo or trade name of Sun or its licensors is granted under +this Agreement. + +3. LIMITED WARRANTY. Sun warrants to you that for a period of ninety (90) +days from the date of purchase, as evidenced by a copy of the receipt, the +media on which Software is furnished (if any) will be free of defects in +materials and workmanship under normal use. Except for the foregoing, +Software is provided "AS IS". Your exclusive remedy and Sun's entire +liability under this limited warranty will be at Sun's option to replace +Software media or refund the fee paid for Software. + +4. DISCLAIMER OF WARRANTY. UNLESS SPECIFIED IN THIS AGREEMENT, ALL EXPRESS +OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY +IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR +NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT THESE +DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. + +5. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR +DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE +DAMAGES, HOWEVER CAUSED REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT +OF OR RELATED TO THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In no event will Sun's +liability to you, whether in contract, tort (including negligence), or +otherwise, exceed the amount paid by you for Software under this +Agreement. The foregoing limitations will apply even if the above stated +warranty fails of its essential purpose. + +6. Termination. This Agreement is effective until terminated. You may +terminate this Agreement at any time by destroying all copies of Software. +This Agreement will terminate immediately without notice from Sun if you +fail to comply with any provision of this Agreement. Upon Termination, you +must destroy all copies of Software. + +7. Export Regulations. All Software and technical data delivered under this +Agreement are subject to US export control laws and may be subject to +export or import regulations in other countries. You agree to comply +strictly with all such laws and regulations and acknowledge that you have +the responsibility to obtain such licenses to export, re-export, or import +as may be required after delivery to you. + +8. U.S. Government Restricted Rights. If Software is being acquired by or +on behalf of the U.S. Government or by a U.S. Government prime contractor +or subcontractor (at any tier), then the Government's rights in Software +and accompanying documentation will be only as set forth in this Agreement; +this is in accordance with 48 CFR 227.7201 through 227.7202-4 (for +Department of Defense (DOD) acquisitions) and with 48 CFR 2.101 and 12.212 +(for non-DOD acquisitions). + +9. Governing Law. Any action related to this Agreement will be governed by +California law and controlling U.S. federal law. No choice of law rules of +any jurisdiction will apply. + +10. Severability. If any provision of this Agreement is held to be +unenforceable, this Agreement will remain in effect with the provision +omitted, unless omission would frustrate the intent of the parties, in +which case this Agreement will immediately terminate. + +11. Integration. This Agreement is the entire agreement between you and +Sun relating to its subject matter. It supersedes all prior or +contemporaneous oral or written communications, proposals, representations +and warranties and prevails over any conflicting or additional terms of any +quote, order, acknowledgment, or other communication between the parties +relating to its subject matter during the term of this Agreement. No +modification of this Agreement will be binding, unless in writing and +signed by an authorized representative of each party. + + JAVA OPTIONAL PACKAGE + JAVATM CRYPTOGRAPHY EXTENSION, VERSION 1.2.2 + + SUPPLEMENTAL LICENSE TERMS + +These supplemental license terms ("Supplemental Terms") add to or modify +the terms of the Binary Code License Agreement (collectively, the +"Agreement"). Capitalized terms not defined in these Supplemental Terms +shall have the same meanings ascribed to them in the Agreement. These +Supplemental Terms shall supersede any inconsistent or conflicting terms in +the Agreement, or in any license contained within the Software. + +1. Software Internal Use and Development License Grant. Subject to the +terms and conditions of this Agreement, including, but not limited to +Section 3 (Java(TM) Technology Restrictions) of these Supplemental Terms, +Sun grants you a non-exclusive, non-transferable, limited license to +reproduce internally and use internally the binary form of the Software, +complete and unmodified, for the sole purpose of designing, developing and +testing your Java applets and applications ("Programs"). + +2. License to Distribute Software. In addition to the license granted in +Section 1 (Software Internal Use and Development License Grant) of these +Supplemental Terms, subject to the terms and conditions of this Agreement, +including but not limited to, Section 3 (Java Technology Restrictions) of +these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, +limited license to reproduce and distribute the Software in binary code +form only, provided that you (i) distribute the Software complete and +unmodified and only bundled as part of your Programs, (ii) do not +distribute additional software intended to replace any component(s) of the +Software, (iii) do not remove or alter any proprietary legends or notices +contained in the Software, (iv) only distribute the Software subject to a +license agreement that protects Sun's interests consistent with the terms +contained in this Agreement, and (v) agree to defend and indemnify Sun and +its licensors from and against any damages, costs, liabilities, settlement +amounts and/or expenses (including attorneys' fees) incurred in connection +with any claim, lawsuit or action by any third party that arises or results +from the use or distribution of any and all Programs and/or Software. + +3. Java Technology Restrictions. You may not modify the Java Platform +Interface ("JPI", identified as classes contained within the "java" package +or any subpackages of the "java" package), by creating additional classes +within the JPI or otherwise causing the addition to or modification of the +classes in the JPI. In the event that you create an additional class and +associated API(s) which (i) extends the functionality of the Java platform, +and (ii) is exposed to third party software developers for the purpose of +developing additional software which invokes such additional API, you must +promptly publish broadly an accurate specification for such API for free +use by all developers. You may not create, or authorize your licensees to +create additional classes, interfaces, or subpackages that are in any way +identified as "java", "javax", "sun" or similar convention as specified by +Sun in any naming convention designation. + +4. Trademarks and Logos. You acknowledge and agree as between you and Sun +that Sun owns the SUN, SOLARIS, JAVA, JINI, FORTE, STAROFFICE, STARPORTAL +and iPLANET trademarks and all SUN, SOLARIS, JAVA, JINI, FORTE, STAROFFICE, +STARPORTAL and iPLANET-related trademarks, service marks, logos and other +brand designations ("Sun Marks"), and you agree to comply with the Sun +Trademark and Logo Usage Requirements currently located at +http://www.sun.com/policies/trademarks. Any use you make of the Sun Marks +inures to Sun's benefit. + +5. Source Code. Software may contain source code that is provided solely +for reference purposes pursuant to the terms of this Agreement. Source +code may not be redistributed unless expressly provided for in this +Agreement. + +6. Termination for Infringement. Either party may terminate this Agreement +immediately should any Software become, or in either party's opinion be +likely to become, the subject of a claim of infringement of any +intellectual property right. +For inquiries please contact: Sun Microsystems, Inc. 4150 Network Circle, +Santa Clara, California 95054 +(LFI#113313/Form ID#011801) diff --git a/src/com/verisignlabs/dnssec/cl/KeyGen.java b/src/com/verisignlabs/dnssec/cl/KeyGen.java new file mode 100644 index 0000000..6a195c1 --- /dev/null +++ b/src/com/verisignlabs/dnssec/cl/KeyGen.java @@ -0,0 +1,352 @@ +// $Id: KeyGen.java,v 1.2 2004/01/16 17:56:17 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.cl; + +import java.util.*; +import java.io.*; +import java.text.SimpleDateFormat; +import java.text.ParseException; +import java.security.GeneralSecurityException; + +import org.xbill.DNS.*; + +import com.verisignlabs.dnssec.security.*; + +import org.apache.commons.cli.*; +import org.apache.commons.cli.Options; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** This class forms the command line implementation of a DNSSEC key + * generator + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.2 $ + */ +public class KeyGen +{ + private static Log log; + + /** This is a small inner class used to hold all of the command line + * option state. */ + private static class CLIState + { + public int algorithm = 5; + public int keylength = 1024; + public String outputfile = null; + public File keydir = null; + public boolean zoneKey = true; + public boolean kskFlag = false; + public String owner = null; + public long ttl = 86400; + + public CLIState() { } + + public void parseCommandLine(Options opts, String[] args) + throws org.apache.commons.cli.ParseException, ParseException, + IOException + { + CommandLineParser cli_parser = new PosixParser(); + CommandLine cli = cli_parser.parse(opts, args); + + String optstr = null; + + if (cli.hasOption('h')) usage(opts); + + if (cli.hasOption('v')) + { + int value = parseInt(cli.getOptionValue('v'), 5); + + switch (value) + { + case 0: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "fatal"); + break; + case 5: + default: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "debug"); + break; + case 6: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "trace"); + break; + } + } + + if (cli.hasOption('k')) kskFlag = true; + + outputfile = cli.getOptionValue('f'); + + if ((optstr = cli.getOptionValue('d')) != null) + { + keydir = new File(optstr); + } + + if ((optstr = cli.getOptionValue('n')) != null) + { + if (! optstr.equalsIgnoreCase("ZONE")) + { + zoneKey = false; + } + } + if ((optstr = cli.getOptionValue('a')) != null) + { + algorithm = parseAlg(optstr); + } + + if ((optstr = cli.getOptionValue('b')) != null) + { + keylength = parseInt(optstr, 1024); + } + + if ((optstr = cli.getOptionValue("ttl")) != null) + { + ttl = parseInt(optstr, 86400); + } + + String[] cl_args = cli.getArgs(); + + if (cl_args.length < 1) + { + System.err.println("error: missing key owner name"); + usage(opts); + } + + owner = cl_args[0]; + } + } + + /** This is just a convenience method for parsing integers from + * strings. + * + * @param s the string to parse. + * @param def the default value, if the string doesn't parse. + * @return the parsed integer, or the default. + */ + private static int parseInt(String s, int def) + { + try + { + int v = Integer.parseInt(s); + return v; + } + catch (NumberFormatException e) + { + return def; + } + } + + private static int parseAlg(String s) + { + int alg = parseInt(s, -1); + if (alg > 0) return alg; + + s = s.toUpperCase(); + + if (s.equals("RSA")) + { + return DNSSEC.RSASHA1; + } + else if (s.equals("RSAMD5")) + { + return DNSSEC.RSA; + } + else if (s.equals("DH")) + { + return DNSSEC.DH; + } + else if (s.equals("DSA")) + { + return DNSSEC.DSA; + } + else if (s.equals("RSASHA1")) + { + return DNSSEC.RSASHA1; + } + + // default + return DNSSEC.RSASHA1; + } + + /** Set up the command line options. + * + * @return a set of command line options. + */ + private static Options setupCLI() + { + Options options = new Options(); + + // boolean options + options.addOption("h", "help", false, "Print this message."); + options.addOption("k", "kskflag", false, + "Key is a key-signing-key (sets the SEP flag)."); + + // Argument options + options.addOption(OptionBuilder.hasArg() + .withLongOpt("nametype") + .withArgName("type") + .withDescription("ZONE | OTHER (default ZONE)") + .create('n')); + options.addOption(OptionBuilder.hasOptionalArg() + .withLongOpt("verbose") + .withArgName("level") + .withDescription("verbosity level -- 0 is silence, " + + "5 is debug information, " + + "6 is trace information.\n"+ + "default is level 5.") + .create('v')); + options.addOption(OptionBuilder.hasArg() + .withArgName("algorithm") + .withDescription("RSA | RSASHA1 | RSAMD5 | DH | DSA, " + + "RSASHA1 is default.") + .create('a')); + options.addOption(OptionBuilder.hasArg() + .withArgName("size") + .withDescription + ("key size, in bits. (default = 1024)\n" + + "RSA|RSASHA1|RSAMD5: [512..4096]\n" + + "DSA: [512..1024]\n" + + "DH: [128..4096]") + .create('b')); + options.addOption(OptionBuilder.hasArg() + .withArgName("file") + .withLongOpt("output-file") + .withDescription + ("base filename for the public/private key files") + .create('f')); + options.addOption(OptionBuilder.hasArg() + .withLongOpt("keydir") + .withArgName("dir") + .withDescription + ("place generated key files in this directory") + .create('d')); + + return options; + } + + /** Print out the usage and help statements, then quit. */ + private static void usage(Options opts) + { + HelpFormatter f = new HelpFormatter(); + + PrintWriter out = new PrintWriter(System.err); + + // print our own usage statement: + f.printHelp(out, 75, "keyGen.sh [..options..] name", null, opts, + HelpFormatter.DEFAULT_LEFT_PAD, + HelpFormatter.DEFAULT_DESC_PAD, null); + + out.flush(); + System.exit(64); + } + + + public static void execute(CLIState state, Options opts) + throws Exception + { + JCEDnsSecSigner signer = new JCEDnsSecSigner(); + + // Minor hack to make the owner name absolute. + if (! state.owner.endsWith(".")) + { + state.owner = state.owner + "."; + } + + Name owner_name = Name.fromString(state.owner); + + // Calculate our flags + int flags = 0; + if (state.zoneKey) flags |= DNSKEYRecord.OWNER_ZONE; + if (state.kskFlag) flags |= DNSKEYRecord.FLAG_SEP; + + log.debug("create key pair with (name = " + owner_name + ", ttl = " + + state.ttl + ", alg = " + state.algorithm + ", flags = " + + flags + ", length = " + state.keylength + ")"); + + + DnsKeyPair pair = signer.generateKey(owner_name, + state.ttl, + DClass.IN, + state.algorithm, + flags, + state.keylength); + + if (state.outputfile != null) + { + BINDKeyUtils.writeKeyFiles(state.outputfile, pair, state.keydir); + } + else + { + BINDKeyUtils.writeKeyFiles(pair, state.keydir); + } + } + + public static void main(String[] args) + { + // set up logging. + // For now, we force the commons logging to use the built-in + // SimpleLog. + System.setProperty("org.apache.commons.logging.Log", + "org.apache.commons.logging.impl.SimpleLog"); + + // set up the command line options + Options opts = setupCLI(); + + CLIState state = new CLIState(); + + try + { + state.parseCommandLine(opts, args); + } + catch (UnrecognizedOptionException e) + { + System.err.println("error: unknown option encountered: " + + e.getMessage()); + usage(opts); + } + catch (AlreadySelectedException e) + { + System.err.println("error: mutually exclusive options have " + + "been selected:\n " + e.getMessage()); + usage(opts); + } + catch (Exception e) + { + System.err.println("error: unknown command line parsing exception:"); + e.printStackTrace(); + usage(opts); + } + + log = LogFactory.getLog(KeyGen.class); + + try + { + execute(state, opts); + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/src/com/verisignlabs/dnssec/cl/SignZone.java b/src/com/verisignlabs/dnssec/cl/SignZone.java new file mode 100644 index 0000000..8884acb --- /dev/null +++ b/src/com/verisignlabs/dnssec/cl/SignZone.java @@ -0,0 +1,719 @@ +// $Id: SignZone.java,v 1.4 2004/01/16 17:57:47 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.cl; + +import java.util.*; +import java.io.*; +import java.text.SimpleDateFormat; +import java.text.ParseException; +import java.security.GeneralSecurityException; + +import org.xbill.DNS.*; + +import com.verisignlabs.dnssec.security.*; + +import org.apache.commons.cli.*; +import org.apache.commons.cli.Options; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** This class forms the command line implementation of a DNSSEC zone + * signer. + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.4 $ + */ +public class SignZone +{ + private static Log log; + + /** This is a small inner class used to hold all of the command line + * option state. */ + private static class CLIState + { + private File keyDirectory = null; + public File keysetDirectory = null; + public String[] kskFiles = null; + public String[] keyFiles = null; + public String zonefile = null; + public Date start = null; + public Date expire = null; + public String outputfile = null; + public boolean verifySigs = false; + public boolean selfSignKeys = true; + public boolean useOptIn = false; + public boolean optInConserve = false; + public boolean fullySignKeyset = false; + public List includeNames = null; + + public CLIState() { } + + public void parseCommandLine(Options opts, String[] args) + throws org.apache.commons.cli.ParseException, ParseException, + IOException + { + CommandLineParser cli_parser = new PosixParser(); + CommandLine cli = cli_parser.parse(opts, args); + + String optstr = null; + + if (cli.hasOption('h')) usage(opts); + + if (cli.hasOption('v')) + { + int value = parseInt(cli.getOptionValue('v'), 5); + + switch (value) + { + case 0: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "fatal"); + break; + case 5: + default: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "debug"); + break; + case 6: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "trace"); + break; + } + } + + if (cli.hasOption('a')) verifySigs = true; + if (cli.hasOption('O')) useOptIn = true; + if (cli.hasOption('C')) + { + useOptIn = true; + optInConserve = true; + } + + 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(opts); + + } + } + + if ((optstr = cli.getOptionValue('D')) != null) + { + keyDirectory = new File(optstr); + if (! keyDirectory.isDirectory()) + { + System.err.println("error: " + optstr + " is not a directory"); + usage(opts); + } + } + + if ((optstr = cli.getOptionValue('s')) != null) + { + start = convertDuration(null, optstr); + } + else + { + // default is now - 1 hour. + start = new Date(System.currentTimeMillis() - (3600 * 1000)); + } + + if ((optstr = cli.getOptionValue('e')) != null) + { + expire = convertDuration(start, optstr); + } + else + { + expire = convertDuration(start, "+2592000"); // 30 days + } + + outputfile = cli.getOptionValue('f'); + + kskFiles = cli.getOptionValues('k'); + + if ((optstr = cli.getOptionValue('I')) != null) + { + File includeNamesFile = new File(optstr); + includeNames = getNameList(includeNamesFile); + } + + String[] files = cli.getArgs(); + + if (files.length < 2) + { + System.err.println("error: missing zone file and/or key files"); + usage(opts); + } + + zonefile = files[0]; + keyFiles = new String[files.length - 1]; + System.arraycopy(files, 1, keyFiles, 0, files.length - 1); + } + } + + /** This is just a convenience method for parsing integers from + * strings. + * + * @param s the string to parse. + * @param def the default value, if the string doesn't parse. + * @return the parsed integer, or the default. + */ + private static int parseInt(String s, int def) + { + try + { + int v = Integer.parseInt(s); + return v; + } + catch (NumberFormatException e) + { + return def; + } + } + + /** Verify the generated signatures. + * + * @param zonename the origin name of 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(Name zonename, List records, + List keypairs) + { + boolean secure = true; + + DnsSecVerifier verifier = new DnsSecVerifier(); + + for (Iterator i = keypairs.iterator(); i.hasNext(); ) + { + verifier.addTrustedKey((DnsKeyPair) i.next()); + } + + verifier.setVerifyAllSigs(true); + + List rrsets = SignUtils.assembleIntoRRsets(records); + + for (Iterator i = rrsets.iterator(); i.hasNext(); ) + { + RRset rrset = (RRset) i.next(); + + // skip unsigned rrsets. + if (!rrset.sigs().hasNext()) continue; + + byte result = verifier.verify(rrset, null); + + if (result != DNSSEC.Secure) + { + log.debug("Signatures did not verify for RRset: (" + result + "): " + + rrset); + secure = false; + } + } + + return secure; + } + + /** 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 start_index 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 getKeys(String[] keyfiles, int start_index, + File inDirectory) + throws IOException + { + if (keyfiles == null) return null; + + int len = keyfiles.length - start_index; + if (len <= 0) return null; + + ArrayList keys = new ArrayList(len); + + for (int i = start_index; i < keyfiles.length; i++) + { + DnsKeyPair k = BINDKeyUtils.loadKeyPair(keyfiles[i], inDirectory); + if (k != null) keys.add(k); + } + + return keys; + } + + /** Load a single key from a given keyfile. + * + * @param keyfile the keyfile. + * @param inDirectory the default directory to look in (may be + * null). + * @return a list containing one or zero keypair objects. + */ + private static List getKeys(File keyfile, File inDirectory) + throws IOException + { + if (keyfile == null) return null; + + DnsKeyPair k = BINDKeyUtils.loadKeyPair(keyfile.getPath(), + inDirectory); + if (k != null) + { + ArrayList keys = new ArrayList(1); + keys.add(k); + return keys; + } + + return null; + } + + /** This is an implementation of a file filter used for finding BIND + * 9-style keyset-* files. */ + private static class KeysetFileFilter implements FileFilter + { + public boolean accept(File pathname) + { + if (! pathname.isFile()) return false; + String name = pathname.getName(); + if (name.startsWith("keyset-")) return true; + return false; + } + } + + /** 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. + */ + private static List getKeysets(File inDirectory, Name zonename) + throws IOException + { + if (inDirectory == null) + { + // FIXME: dunno how cross-platform this is + inDirectory = new File("."); + } + + // get the list of "keyset-" files. + FileFilter filter = new KeysetFileFilter(); + File[] files = inDirectory.listFiles(filter); + + // read in all of the records + ArrayList keysetRecords = new ArrayList(); + for (int i = 0; i < files.length; i++) + { + List l = ZoneUtils.readZoneFile(files[i].getAbsolutePath(), zonename); + keysetRecords.addAll(l); + } + + // discard records that do not belong to the zone in question. + for (Iterator i = keysetRecords.iterator(); i.hasNext(); ) + { + Record r = (Record) i.next(); + if (!r.getName().subdomain(zonename)) + { + i.remove(); + } + } + + return keysetRecords; + } + + /** 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 getNameList(File nameListFile) + throws IOException + { + BufferedReader br = new BufferedReader(new FileReader(nameListFile)); + List 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) + { + log.error("DNS Name parsing error", e); + } + } + + if (res.size() == 0) return null; + return res; + } + + /** 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. + */ + private static Date convertDuration(Date start, String duration) + throws ParseException + { + if (start == null) start = new Date(); + if (duration.startsWith("now")) + { + start = new Date(); + if (duration.indexOf("+") < 0) return start; + + duration = duration.substring(3); + } + + if (duration.startsWith("+")) + { + long offset = (long) parseInt(duration.substring(1), 0) * 1000; + return new Date(start.getTime() + offset); + } + + SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss"); + dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); + return dateFormatter.parse(duration); + } + + /** 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. + * @return true if the keypairs valid. + */ + private static boolean keyPairsValidForZone(Name zonename, List keypairs) + { + if (keypairs == null) return true; // technically true, I guess. + + for (Iterator i = keypairs.iterator(); i.hasNext(); ) + { + DnsKeyPair kp = (DnsKeyPair) i.next(); + Name keyname = kp.getDNSKEYRecord().getName(); + if (!keyname.equals(zonename)) + { + return false; + } + } + + return true; + } + + /** Set up the command line options. + * + * @return a set of command line options. + */ + private static Options setupCLI() + { + Options options = new Options(); + + // boolean options + options.addOption("h", "help", false, "Print this message."); + options.addOption("a", false, "verify generated signatures>"); + options.addOption("F", "fully-sign-keyset", false, + "sign the zone apex keyset with all " + + "available keys, instead of just key-signing-keys."); + + // Opt-In generation switches + OptionGroup opt_in_opts = new OptionGroup(); + opt_in_opts.addOption(new Option + ("O", "generate a fully Opt-In zone.")); + opt_in_opts.addOption(new Option + ("C", "generate a conservative Opt-In zone.")); + options.addOptionGroup(opt_in_opts); + + // Argument options + options.addOption(OptionBuilder.hasOptionalArg() + .withArgName("level") + .withDescription("verbosity level -- 0 is silence, " + + "5 is debug information, " + + "6 is trace information. " + + "No argument means 5.") + .create('v')); + options.addOption(OptionBuilder.hasArg() + .withArgName("dir") + .withLongOpt("keyset-directory") + .withDescription + ("directory to find keyset files (default '.').") + .create('d')); + options.addOption(OptionBuilder.hasArg() + .withArgName("dir") + .withLongOpt("key-directory") + .withDescription + ("directory to find key files (default '.').") + .create('D')); + options.addOption(OptionBuilder.hasArg() + .withArgName("time/offset") + .withLongOpt("start-time") + .withDescription + ("signature starting time (default is now - 1 hour)") + .create('s')); + options.addOption(OptionBuilder.hasArg() + .withArgName("time/offset") + .withLongOpt("expire-time") + .withDescription + ("signature expiration time (default is " + + "start-time + 30 days") + .create('e')); + options.addOption(OptionBuilder.hasArg() + .withArgName("outfile") + .withDescription("file the signed zone is written " + + "to (default is .signed).") + .create('f')); + options.addOption(OptionBuilder.hasArgs() + .withArgName("KSK file") + .withLongOpt("ksk-file") + .withDescription("this key is a key signing key " + + "(may repeat)") + .create('k')); + options.addOption(OptionBuilder.hasArg() + .withArgName("file") + .withLongOpt("include-file") + .withDescription("include names in this file " + + "in the NSEC chain") + .create('I')); + + return options; + } + + /** Print out the usage and help statements, then quit. */ + private static void usage(Options opts) + { + HelpFormatter f = new HelpFormatter(); + + PrintWriter out = new PrintWriter(System.err); + + // print our own usage statement: + out.println("usage: signZone.sh [..options..] zone_file [key_file ...] "); + f.printHelp(out, 75, "signZone.sh", null, opts, + HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, + "\ntime/offset = YYYYMMDDHHmmss|+offset|\"now\"+offset\n"); + + out.flush(); + System.exit(64); + } + + + public static void execute(CLIState state, Options opts) + throws Exception + { + // Load the key pairs. + + // FIXME: should we do what BIND 9.3.x snapshots do and look at + // zone apex DNSKEY RRs, and from that be able to load all of the + // keys? + + List keypairs = getKeys(state.keyFiles, 0, state.keyDirectory); + List kskpairs = getKeys(state.kskFiles, 0, state.keyDirectory); + + // 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.size() == 0) && + keypairs != null && keypairs.size() > 1) + { + for (Iterator i = keypairs.iterator(); i.hasNext(); ) + { + DnsKeyPair pair = (DnsKeyPair) i.next(); + DNSKEYRecord kr = pair.getDNSKEYRecord(); + if ((kr.getFlags() & DNSKEYRecord.FLAG_SEP) != 0) + { + if (kskpairs == null) kskpairs = new ArrayList(); + kskpairs.add(pair); + i.remove(); + } + } + } + + // Read in the zone + List records = ZoneUtils.readZoneFile(state.zonefile, null); + if (records == null || records.size() == 0) + { + System.err.println("error: empty zone file"); + usage(opts); + } + + // calculate the zone name. + Name zonename = ZoneUtils.findZoneName(records); + if (zonename == null) + { + System.err.println("error: invalid zone file - no SOA"); + usage(opts); + } + + // default the output file, if not set. + if (state.outputfile == null) + { + if (zonename.isAbsolute()) + { + state.outputfile = zonename + "signed"; + } + else + { + state.outputfile = zonename + ".signed"; + } + } + + // Verify that the keys can be in the zone. + List kpairs = keypairs; + + if (!keyPairsValidForZone(zonename, keypairs) || + !keyPairsValidForZone(zonename, kskpairs)) + { + usage(opts); + } + + // 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) + { + for (Iterator i = kskpairs.iterator(); i.hasNext(); ) + { + records.add( ((DnsKeyPair) i.next()).getDNSKEYRecord() ); + } + } + if (keypairs != null) + { + for (Iterator i = keypairs.iterator(); i.hasNext(); ) + { + records.add( ((DnsKeyPair) i.next()).getDNSKEYRecord() ); + } + } + + // read in the keysets, if any. + List keysetrecs = getKeysets(state.keysetDirectory, zonename); + if (keysetrecs != null) + { + records.addAll(keysetrecs); + } + + JCEDnsSecSigner signer = new JCEDnsSecSigner(); + + // Sign the zone. + List signed_records = signer.signZone(zonename, + records, + kskpairs, + keypairs, + state.start, + state.expire, + state.useOptIn, + state.optInConserve, + state.fullySignKeyset, + state.includeNames); + + // write out the signed zone + // force multiline mode for now + org.xbill.DNS.Options.set("multiline"); + ZoneUtils.writeZoneFile(signed_records, state.outputfile); + + if (state.verifySigs) + { + // FIXME: ugh. + if (kskpairs != null) + { + keypairs.addAll(kskpairs); + } + + log.debug("verifying generated signatures"); + boolean res = verifyZoneSigs(zonename, signed_records, keypairs); + + if (res) + { + System.out.println("Generated signatures verified"); + // log.info("Generated signatures verified"); + } + else + { + System.out.println("Generated signatures did not verify."); + // log.warn("Generated signatures did not verify."); + } + } + + } + + public static void main(String[] args) + { + // set up logging. + // For now, we force the commons logging to use the built-in + // SimpleLog. + System.setProperty("org.apache.commons.logging.Log", + "org.apache.commons.logging.impl.SimpleLog"); + + // set up the command line options + Options opts = setupCLI(); + + CLIState state = new CLIState(); + try + { + state.parseCommandLine(opts, args); + } + catch (UnrecognizedOptionException e) + { + System.err.println("error: unknown option encountered: " + + e.getMessage()); + usage(opts); + } + catch (AlreadySelectedException e) + { + System.err.println("error: mutually exclusive options have " + + "been selected:\n " + e.getMessage()); + usage(opts); + } + catch (Exception e) + { + System.err.println("error: unknown command line parsing exception:"); + e.printStackTrace(); + usage(opts); + } + + log = LogFactory.getLog(SignZone.class); + + try + { + execute(state, opts); + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/src/com/verisignlabs/dnssec/cl/VerifyZone.java b/src/com/verisignlabs/dnssec/cl/VerifyZone.java new file mode 100644 index 0000000..7586ec7 --- /dev/null +++ b/src/com/verisignlabs/dnssec/cl/VerifyZone.java @@ -0,0 +1,331 @@ +// $Id: VerifyZone.java,v 1.1 2004/01/16 17:57:59 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.cl; + +import java.util.*; +import java.io.*; +import java.text.SimpleDateFormat; +import java.text.ParseException; +import java.security.GeneralSecurityException; + +import org.xbill.DNS.*; + +import com.verisignlabs.dnssec.security.*; + +import org.apache.commons.cli.*; +import org.apache.commons.cli.Options; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** This class forms the command line implementation of a DNSSEC zone + * validator. + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.1 $ + */ +public class VerifyZone +{ + private static Log log; + + /** This is a small inner class used to hold all of the command line + * option state. */ + private static class CLIState + { + + public boolean strict = false; + public File keydir = null; + public String zonefile = null; + public String[] keyfiles = null; + + public CLIState() { } + + public void parseCommandLine(Options opts, String[] args) + throws org.apache.commons.cli.ParseException, ParseException, + IOException + { + CommandLineParser cli_parser = new PosixParser(); + CommandLine cli = cli_parser.parse(opts, args); + + String optstr = null; + + if (cli.hasOption('h')) usage(opts); + + if (cli.hasOption('v')) + { + int value = parseInt(cli.getOptionValue('v'), 5); + + switch (value) + { + case 0: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "fatal"); + break; + case 5: + default: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "debug"); + break; + case 6: + System.setProperty("org.apache.commons.logging.simplelog.defaultlog", + "trace"); + break; + } + } + + if (cli.hasOption('s')) strict = true; + + if ((optstr = cli.getOptionValue('d')) != null) + { + keydir = new File(optstr); + } + + + String[] cl_args = cli.getArgs(); + + if (cl_args.length < 1) + { + System.err.println("error: missing zone file"); + usage(opts); + } + + zonefile = cl_args[0]; + + if (cl_args.length < 2) + { + System.err.println("error: at least one trusted key is required"); + usage(opts); + } + + keyfiles = new String[cl_args.length - 1]; + System.arraycopy(cl_args, 1, keyfiles, 0, keyfiles.length); + } + } + + /** This is just a convenience method for parsing integers from + * strings. + * + * @param s the string to parse. + * @param def the default value, if the string doesn't parse. + * @return the parsed integer, or the default. + */ + private static int parseInt(String s, int def) + { + try + { + int v = Integer.parseInt(s); + return v; + } + catch (NumberFormatException e) + { + return def; + } + } + + /** Set up the command line options. + * + * @return a set of command line options. + */ + private static Options setupCLI() + { + Options options = new Options(); + + // boolean options + options.addOption("h", "help", false, "Print this message."); + options.addOption("s", "strict", false, + "Zone will only be considered valid if all " + + "signatures could be cryptographically verified"); + + // Argument options + options.addOption(OptionBuilder.hasArg() + .withLongOpt("keydir") + .withArgName("dir") + .withDescription("directory to find trusted key files") + .create('d')); + + options.addOption(OptionBuilder.hasOptionalArg() + .withLongOpt("verbose") + .withArgName("level") + .withDescription("verbosity level -- 0 is silence, " + + "5 is debug information, " + + "6 is trace information.\n" + + "default is level 5.") + .create('v')); + + return options; + } + + /** Print out the usage and help statements, then quit. */ + private static void usage(Options opts) + { + HelpFormatter f = new HelpFormatter(); + + PrintWriter out = new PrintWriter(System.err); + + // print our own usage statement: + f.printHelp(out, 75, + "verifyZone.sh [..options..] zonefile " + + "keyfile [keyfile...]", null, opts, + HelpFormatter.DEFAULT_LEFT_PAD, + HelpFormatter.DEFAULT_DESC_PAD, null); + + out.flush(); + System.exit(64); + } + + + private static byte verifyZoneSignatures(List records, List keypairs) + { + // Zone is secure until proven otherwise. + byte result = DNSSEC.Secure; + + DnsSecVerifier verifier = new DnsSecVerifier(); + + for (Iterator i = keypairs.iterator(); i.hasNext(); ) + { + verifier.addTrustedKey((DnsKeyPair) i.next()); + } + + List rrsets = SignUtils.assembleIntoRRsets(records); + + for (Iterator i = rrsets.iterator(); i.hasNext(); ) + { + RRset rrset = (RRset) i.next(); + + // We verify each signature separately so that we can report + // which exact signature failed. + for (Iterator j = rrset.sigs(); j.hasNext(); ) + { + Object o = j.next(); + if (! (o instanceof RRSIGRecord)) + { + log.debug("found " + o + " where expecting a RRSIG"); + continue; + } + RRSIGRecord sigrec = (RRSIGRecord) o; + + byte res = verifier.verifySignature(rrset, sigrec, null); + if (res != DNSSEC.Secure) + { + log.info("Signature failed to verify RRset: " + rrset + "\nsig: " + sigrec); + } + if (res < result) result = res; + } + } + + return result; + } + + private static List getTrustedKeys(String[] keyfiles, File inDirectory) + throws IOException + { + if (keyfiles == null) return null; + + List keys = new ArrayList(keyfiles.length); + + for (int i = 0; i < keyfiles.length; i++) + { + DnsKeyPair pair = BINDKeyUtils.loadKeyPair(keyfiles[i], inDirectory); + if (pair != null) keys.add(pair); + } + + return keys; + } + + public static void execute(CLIState state, Options opts) + throws Exception + { + + List keypairs = getTrustedKeys(state.keyfiles, state.keydir); + + List records = ZoneUtils.readZoneFile(state.zonefile, null); + Collections.sort(records, new RecordComparator()); + + log.debug("verifying signatures..."); + byte result = verifyZoneSignatures(records, keypairs); + log.debug("completed verification process."); + + switch (result) + { + case DNSSEC.Failed: + System.out.println("zone did not verify."); + System.exit(1); + break; + case DNSSEC.Insecure: + if (state.strict) + { + System.out.println("zone did not verify."); + System.exit(1); + } + case DNSSEC.Secure: + System.out.println("zone verified."); + break; + } + System.exit(0); + } + + public static void main(String[] args) + { + // set up logging. + // For now, we force the commons logging to use the built-in + // SimpleLog. + System.setProperty("org.apache.commons.logging.Log", + "org.apache.commons.logging.impl.SimpleLog"); + + // set up the command line options + Options opts = setupCLI(); + + CLIState state = new CLIState(); + + try + { + state.parseCommandLine(opts, args); + } + catch (UnrecognizedOptionException e) + { + System.err.println("error: unknown option encountered: " + + e.getMessage()); + usage(opts); + } + catch (AlreadySelectedException e) + { + System.err.println("error: mutually exclusive options have " + + "been selected:\n " + e.getMessage()); + usage(opts); + } + catch (Exception e) + { + System.err.println("error: unknown command line parsing exception:"); + e.printStackTrace(); + usage(opts); + } + + log = LogFactory.getLog(VerifyZone.class); + + try + { + execute(state, opts); + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/src/com/verisignlabs/dnssec/cl/package.html b/src/com/verisignlabs/dnssec/cl/package.html new file mode 100644 index 0000000..a240662 --- /dev/null +++ b/src/com/verisignlabs/dnssec/cl/package.html @@ -0,0 +1,33 @@ + + +java-dnssec-tools is a collection of Java-based command line tools for +managing DNSSEC zones and keys. +

+ +

+ + +
+ +Copyright © 2003 Verisign, Inc. by +David Blacka

+ +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version.

+ +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details.

+ +You should have received a copy of the +GNU Library General Public License +along with this library; if not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +

+

+ + diff --git a/src/com/verisignlabs/dnssec/security/BINDKeyUtils.java b/src/com/verisignlabs/dnssec/security/BINDKeyUtils.java new file mode 100644 index 0000000..b04f956 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/BINDKeyUtils.java @@ -0,0 +1,398 @@ +// $Id: BINDKeyUtils.java,v 1.5 2004/02/25 20:46:14 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.security; + +import java.io.*; +import java.security.*; +import java.text.NumberFormat; + +import org.xbill.DNS.*; +import org.xbill.DNS.utils.base64; + +/** This class contains a series of static methods used for + * manipulating BIND 9.x.x-style DNSSEC key files. + * + * In this class, the "base" key path or name is the file name + * without the trailing ".key" or ".private". + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.5 $ + */ +public class BINDKeyUtils +{ + // formatters used to generated the BIND key file names + private static NumberFormat mAlgNumberFormatter; + private static NumberFormat mKeyIdNumberFormatter; + + /** Calculate the BIND9 key file base name (i.e., without the ".key" + * or ".private" extensions) */ + private static String getKeyFileBase(Name signer, int algorithm, + int keyid) + { + if (mAlgNumberFormatter == null) + { + mAlgNumberFormatter = NumberFormat.getNumberInstance(); + mAlgNumberFormatter.setMaximumIntegerDigits(3); + mAlgNumberFormatter.setMinimumIntegerDigits(3); + } + if (mKeyIdNumberFormatter == null) + { + mKeyIdNumberFormatter = NumberFormat.getNumberInstance(); + mKeyIdNumberFormatter.setMaximumIntegerDigits(5); + mKeyIdNumberFormatter.setMinimumIntegerDigits(5); + mKeyIdNumberFormatter.setGroupingUsed(false); + + } + + keyid &= 0xFFFF; + + String fn = "K" + signer + "+" + + mAlgNumberFormatter.format(algorithm) + + "+" + + mKeyIdNumberFormatter.format(keyid); + + return fn; + } + + /** Reads in the KEYRecord from the public key file */ + private static DNSKEYRecord loadPublicKeyFile(File publicKeyFile) + throws IOException + { + Master m = new Master(publicKeyFile.getAbsolutePath(), null, 600); + + Record r; + DNSKEYRecord result = null; + + while ( (r = m.nextRecord()) != null ) + { + if (r.getType() == Type.DNSKEY) + { + result = (DNSKEYRecord) r; + } + } + + return result; + } + + /** Reads in the private key verbatim from the private key file */ + private static String loadPrivateKeyFile(File privateKeyFile) + throws IOException + { + BufferedReader in = new BufferedReader(new FileReader(privateKeyFile)); + StringBuffer key_buf = new StringBuffer(); + + String line; + + while ( (line = in.readLine()) != null) + { + key_buf.append(line); + key_buf.append('\n'); + } + + return key_buf.toString().trim(); + } + + /** Given an actual path for one of the key files, return the base + * name */ + private static String fixKeyFileBasePath(String basePath) + { + if (basePath == null) throw new IllegalArgumentException(); + if (basePath.endsWith(".key") || + basePath.endsWith(".private")) + { + basePath = basePath.substring + (0, basePath.lastIndexOf(".")); + } + + return basePath; + } + + /** 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). + * @return the loaded key pair. + * @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 + { + String keyFileBase = getKeyFileBase(signer, algorithm, keyid); + + return loadKeyPair(keyFileBase, inDirectory); + } + + /** 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 relative. + * @return the loaded key pair. + * @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"); + + DnsKeyPair kp = new DnsKeyPair(); + + DNSKEYRecord kr = loadPublicKeyFile(publicKeyFile); + kp.setDNSKEYRecord(kr); + + String pk = loadPrivateKeyFile(privateKeyFile); + kp.setPrivateKeyString(pk); + + return kp; + } + + /** 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 pair. + * @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. + */ + public static DnsKeyPair loadKey(String keyFileBasePath, File inDirectory) + throws IOException + { + keyFileBasePath = fixKeyFileBasePath(keyFileBasePath); + File publicKeyFile = new File(inDirectory, keyFileBasePath + ".key"); + + DnsKeyPair kp = new DnsKeyPair(); + + DNSKEYRecord kr = loadPublicKeyFile(publicKeyFile); + kp.setDNSKEYRecord(kr); + + return kp; + } + + /** Load a BIND keyset file. The BIND 9 dnssec tools typically call + * these files "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). + * @return a RRset contain the KEY records and any associated SIG records. + * @throws IOException if there was a problem reading the keyset + * file. + */ + public static RRset loadKeySet(String keysetFileName, File inDirectory) + throws IOException + { + File keysetFile = new File(inDirectory, keysetFileName); + + Master m = new Master(keysetFile.getAbsolutePath()); + + Record r; + RRset keyset = new RRset(); + while ( (r = m.nextRecord()) != null ) + { + keyset.addRR(r); + } + + return keyset; + } + + /** Calculate the key file base for this key pair. + * + * @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) + { + DNSKEYRecord keyrec = pair.getDNSKEYRecord(); + if (keyrec == null) return null; + + return getKeyFileBase(keyrec.getName(), + keyrec.getAlgorithm(), + keyrec.getFootprint()); + } + + + /** @return a {@link java.io.File} object representing the BIND9 + * public key file. */ + public static File getPublicKeyFile(DnsKeyPair pair, File inDirectory) + { + String keyfilebase = keyFileBase(pair); + if (keyfilebase == null) return null; + + return new File(inDirectory, keyfilebase + ".key"); + } + + /** @return a {@link java.io.File} object representing the BIND9 + * private key file */ + public static File getPrivateKeyFile(DnsKeyPair pair, File inDirectory) + { + String keyfilebase = keyFileBase(pair); + if (keyfilebase == null) return null; + + return new File(inDirectory, keyfilebase + ".private"); + } + + /** 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. + * @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 e) + { + e.printStackTrace(); + } + catch (NoSuchAlgorithmException e) + { + e.printStackTrace(); + } + + return null; + } + + /** 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 require information from both. + * @return a string containing the contents of a BIND9 private key + * file. + */ + public static String convertPrivateKey(PrivateKey priv, PublicKey pub, + int alg) + { + if (priv != null) + { + // debug + // System.out.println("converting from privatekey to bind9 string"); + DnsKeyConverter keyconv = new DnsKeyConverter(); + String priv_string + = keyconv.generatePrivateKeyString(priv, pub, alg); + + return priv_string; + } + + return null; + } + + + /** Convert the KEY record to the exact string format that the + * dnssec-* routines need. Currently, the DNSJAVA package uses a + * multiline mode for its record formatting. The BIND9 tools + * require everything on a single line. */ + private static String DNSKEYtoString(DNSKEYRecord rec) + { + StringBuffer buf = new StringBuffer(); + + buf.append(rec.getName()); + buf.append(" IN DNSKEY "); + buf.append(rec.getFlags() & 0xFFFF); + buf.append(" "); + buf.append(rec.getProtocol()); + buf.append(" "); + buf.append(rec.getAlgorithm()); + buf.append(" "); + buf.append(base64.toString(rec.getKey())); + + return buf.toString(); + } + + /** 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. + */ + public static void writeKeyFiles(String baseFileName, DnsKeyPair pair, + File inDirectory) + throws IOException + { + DNSKEYRecord pub = pair.getDNSKEYRecord(); + String priv = pair.getPrivateKeyString(); + + if (priv == null) + { + priv = convertPrivateKey(pair.getPrivate(), pair.getPublic(), + pair.getDNSKEYAlgorithm()); + } + + if (pub == null || priv == null) return; + + // Write the public key file + File pubkeyfile = new File(inDirectory, baseFileName + ".key"); + PrintWriter out + = new PrintWriter(new FileWriter(pubkeyfile)); + out.println(DNSKEYtoString(pub)); + out.close(); + + // Write the private key file + File privkeyfile = new File(inDirectory, baseFileName + ".private"); + out = new PrintWriter(new FileWriter(privkeyfile)); + out.print(priv); + out.close(); + + } + + /** 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). + */ + public static void writeKeyFiles(DnsKeyPair pair, File inDirectory) + throws IOException + { + String base = keyFileBase(pair); + writeKeyFiles(base, pair, inDirectory); + } + +} diff --git a/src/com/verisignlabs/dnssec/security/ByteArrayComparator.java b/src/com/verisignlabs/dnssec/security/ByteArrayComparator.java new file mode 100644 index 0000000..ca3a6ac --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/ByteArrayComparator.java @@ -0,0 +1,66 @@ +// $Id: ByteArrayComparator.java,v 1.2 2004/02/25 20:46:14 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.verisignlabs.dnssec.security; + +import java.util.*; + + +/** This class implements a basic comparitor for byte arrays. It is + * primarily useful for comparing RDATA portions of DNS records in + * doing DNSSEC canonical ordering. + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.2 $ + */ +public class ByteArrayComparator implements Comparator +{ + private int mOffset = 0; + private boolean mDebug = false; + + public ByteArrayComparator() + {} + + public ByteArrayComparator(int offset, boolean debug) + { + mOffset = offset; + mDebug = debug; + } + + public int compare(Object o1, Object o2) throws ClassCastException + { + byte[] b1 = (byte[]) o1; + byte[] b2 = (byte[]) o2; + + for (int i = mOffset; i < b1.length && i < b2.length; i++) + { + if (b1[i] != b2[i]) + { + if (mDebug) + { + System.out.println("offset " + i + " differs (this is " + + (i - mOffset) +" bytes in from our offset.)"); + } + return (b1[i] & 0xFF) - (b2[i] & 0xFF); + } + } + + return b1.length - b2.length; + } +} diff --git a/src/com/verisignlabs/dnssec/security/DnsKeyConverter.java b/src/com/verisignlabs/dnssec/security/DnsKeyConverter.java new file mode 100644 index 0000000..2587f63 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/DnsKeyConverter.java @@ -0,0 +1,471 @@ +// $Id: DnsKeyConverter.java,v 1.4 2004/02/23 15:06:15 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.verisignlabs.dnssec.security; + +import java.io.*; +import java.util.StringTokenizer; +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; +import javax.crypto.spec.DHPrivateKeySpec; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; +import java.security.interfaces.*; + +import org.xbill.DNS.*; +import org.xbill.DNS.security.KEYConverter; +import org.xbill.DNS.utils.base64; + +/** This class handles conversions between JCA key formats and DNSSEC + * and BIND9 key formats. + * + * @author David Blacka (original) + * @author $Author: davidb $ (latest) + * @version $Revision: 1.4 $ + */ +public class DnsKeyConverter +{ + private KeyFactory mRSAKeyFactory; + private KeyFactory mDSAKeyFactory; + private KeyFactory mDHKeyFactory; + + public DnsKeyConverter() + { + } + + /** Given a DNS KEY record, return the JCA public key */ + public PublicKey parseDNSKEYRecord(DNSKEYRecord pKeyRecord) + { + if (pKeyRecord.getKey() == null) return null; + + return KEYConverter.parseRecord(pKeyRecord); + } + + /** 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) + { + // FIXME: currenty org.xbill.DNS.security.KEYConverter will only + // convert to KEYRecords, and even then, assume that an RSA + // PublicKey means alg 1. + KEYRecord kr = KEYConverter.buildRecord(name, dclass, ttl, flags, + KEYRecord.PROTOCOL_DNSSEC, key); + + return new DNSKEYRecord(name, dclass, ttl, flags, + DNSKEYRecord.Protocol.DNSSEC, alg, + kr.getKey()); + } + + + // Private Key Specific Parsing routines + + /** Convert a PKCS#8 encoded private key into a PrivateKey + * object. */ + public PrivateKey convertEncodedPrivateKey(byte[] key, int algorithm) + { + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key); + + try + { + switch (algorithm) + { + case DNSSEC.RSAMD5: + case DNSSEC.RSASHA1: + return mRSAKeyFactory.generatePrivate(spec); + case DNSSEC.DSA: + return mDSAKeyFactory.generatePrivate(spec); + } + } + catch (GeneralSecurityException e) + { + } + + return null; + } + + /** @return a JCA private key, given a BIND9-style textual + * encoding */ + public PrivateKey parsePrivateKeyString(String key) + throws IOException, NoSuchAlgorithmException + { + StringTokenizer lines = new StringTokenizer(key, "\n"); + + while (lines.hasMoreTokens()) + { + String line = lines.nextToken(); + if (line == null) continue; + + if (line.startsWith("#")) continue; + + String val = value(line); + if (val == null) continue; + + if (line.startsWith("Private-key-format: ")) + { + if (! val.equals("v1.2")) + { + throw new IOException("unsupported private key format: " + val); + } + } + else if (line.startsWith("Algorithm: ")) + { + if (val.startsWith("1 ")) return parsePrivateRSA(lines); + if (val.startsWith("5 ")) return parsePrivateRSA(lines); + if (val.startsWith("2 ")) return parsePrivateDH(lines); + if (val.startsWith("3 ")) return parsePrivateDSA(lines); + throw new IOException("unsupported private key algorithm: " + val); + } + } + return null; + } + + /** @return the value part of an "attribute:value" pair. The value + * is trimmed. */ + private String value(String av) + { + if (av == null) return null; + + int pos = av.indexOf(':'); + if (pos < 0) return av; + + if (pos >= av.length()) return null; + + return av.substring(pos + 1).trim(); + } + + /** prints the bytes of an original byte array and the BigInteger + * copy */ + private void printBigIntCompare(byte[] orig, BigInteger copy) + { + byte[] copy_bytes = copy.toByteArray(); + + for (int i = 0; i < 10 && i < orig.length; i++) { + System.err.print((int) orig[i] & 0xFF); + System.err.print(" "); + } + System.err.println(); + + for (int i = 0; i < 10 && i < copy_bytes.length; i++) { + System.err.print((int) copy_bytes[i] & 0xFF); + System.err.print(" "); + } + System.err.println(); + } + + /** Given the rest of the RSA BIND9 string format private key, parse + * and translate into a JCA private key + * + * @throws NoSuchAlgorithmException if the RSA algorithm is not + * available. */ + 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 coefficient = null; + + while (lines.hasMoreTokens()) + { + String line = lines.nextToken(); + if (line == null) continue; + + if (line.startsWith("#")) continue; + + String val = value(line); + if (val == null) continue; + + byte[] data = base64.fromString(val); + + if (line.startsWith("Modulus: ")) { + modulus = new BigInteger(1, data); + // printBigIntCompare(data, modulus); + } else if (line.startsWith("PublicExponent: ")) { + public_exponent = new BigInteger(1, data); + // printBigIntCompare(data, public_exponent); + } else if (line.startsWith("PrivateExponent: ")) { + private_exponent = new BigInteger(1, data); + // printBigIntCompare(data, private_exponent); + } else if (line.startsWith("Prime1: ")) { + prime_p = new BigInteger(1, data); + // printBigIntCompare(data, prime_p); + } else if (line.startsWith("Prime2: ")) { + prime_q = new BigInteger(1, data); + // printBigIntCompare(data, prime_q); + } else if (line.startsWith("Exponent1: ")) { + prime_p_exponent = new BigInteger(1, data); + } else if (line.startsWith("Exponent2: ")) { + prime_q_exponent = 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); + if (mRSAKeyFactory == null) + { + mRSAKeyFactory = KeyFactory.getInstance("RSA"); + } + return mRSAKeyFactory.generatePrivate(spec); + } + catch (InvalidKeySpecException e) + { + e.printStackTrace(); + return null; + } + } + + /** Given the remaining lines in a BIND9 style DH private key, parse + * the key info and translate it into a JCA private key. + * + * @throws NoSuchAlgorithmException if the DH algorithm is not + * available. */ + private PrivateKey parsePrivateDH(StringTokenizer lines) + throws NoSuchAlgorithmException + { + BigInteger p = null; + BigInteger x = null; + BigInteger g = null; + + while (lines.hasMoreTokens()) + { + String line = lines.nextToken(); + if (line == null) continue; + + if (line.startsWith("#")) continue; + + String val = value(line); + if (val == null) continue; + + byte[] data = base64.fromString(val); + + if (line.startsWith("Prime(p): ")) { + p = new BigInteger(1, data); + } else if (line.startsWith("Generator(g): ")) { + g = new BigInteger(1, data); + } else if (line.startsWith("Private_value(x): ")) { + x = new BigInteger(1, data); + } + } + + try + { + KeySpec spec = new DHPrivateKeySpec(x, p, g); + if (mDHKeyFactory == null) + { + mDHKeyFactory = KeyFactory.getInstance("DH"); + } + return mDHKeyFactory.generatePrivate(spec); + } + catch (InvalidKeySpecException e) + { + e.printStackTrace(); + return null; + } + } + + /** Given the remaining lines in a BIND9 style DSA private key, + * parse the key info and translate it into a JCA private key. + * + * @throws NoSuchAlgorithmException if the DSA algorithm is not + * available. */ + private PrivateKey parsePrivateDSA(StringTokenizer lines) + throws NoSuchAlgorithmException + { + BigInteger p = null; + BigInteger q = null; + BigInteger g = null; + BigInteger x = null; + + while (lines.hasMoreTokens()) + { + String line = lines.nextToken(); + if (line == null) continue; + + if (line.startsWith("#")) continue; + + String val = value(line); + if (val == null) continue; + + byte[] data = base64.fromString(val); + + if (line.startsWith("Prime(p): ")) { + p = new BigInteger(1, data); + } else if (line.startsWith("Subprime(q): ")) { + q = new BigInteger(1, data); + } else if (line.startsWith("Base(g): ")) { + g = new BigInteger(1, data); + } else if (line.startsWith("Private_value(x): ")) { + x = new BigInteger(1, data); + } + } + + try + { + KeySpec spec = new DSAPrivateKeySpec(x, p, q, g); + if (mDSAKeyFactory == null) + { + mDSAKeyFactory = KeyFactory.getInstance("DSA"); + } + return mDSAKeyFactory.generatePrivate(spec); + } + catch (InvalidKeySpecException e) + { + e.printStackTrace(); + return null; + } + } + + /** Given a private key and public key, generate the BIND9 style + * private key format. */ + public String generatePrivateKeyString(PrivateKey priv, PublicKey pub, + int alg) + { + if (priv instanceof RSAPrivateCrtKey) + { + return generatePrivateRSA((RSAPrivateCrtKey) priv, alg); + } + else if (priv instanceof DSAPrivateKey && + pub instanceof DSAPublicKey) + { + return generatePrivateDSA((DSAPrivateKey) priv, (DSAPublicKey) pub); + } + else if (priv instanceof DHPrivateKey && + pub instanceof DHPublicKey) + { + return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub); + } + + return null; + } + + /** Convert from 'unsigned' big integer to original 'signed format' + * in Base64 */ + private String b64BigInt(BigInteger i) + { + byte[] orig_bytes = i.toByteArray(); + + if (orig_bytes[0] != 0 || orig_bytes.length == 1) + { + return base64.toString(orig_bytes); + } + + byte[] signed_bytes = new byte[orig_bytes.length - 1]; + System.arraycopy(orig_bytes, 1, signed_bytes, 0, signed_bytes.length); + + return base64.toString(signed_bytes); + } + + /** Given a RSA private key (in Crt format), return the BIND9-style + * text encoding. */ + private String generatePrivateRSA(RSAPrivateCrtKey key, int algorithm) + { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + + out.println("Private-key-format: v1.2"); + if (algorithm == DNSSEC.RSAMD5) + { + out.println("Algorithm: 1 (RSAMD5)"); + } + else + { + out.println("Algorithm: 5 (RSASHA1)"); + } + out.print("Modulus: "); + out.println(b64BigInt(key.getModulus())); + out.print("PublicExponent: "); + out.println(b64BigInt(key.getPublicExponent())); + out.print("PrivateExponent: "); + out.println(b64BigInt(key.getPrivateExponent())); + out.print("Prime1: "); + out.println(b64BigInt(key.getPrimeP())); + out.print("Prime2: "); + out.println(b64BigInt(key.getPrimeQ())); + out.print("Exponent1: "); + out.println(b64BigInt(key.getPrimeExponentP())); + out.print("Exponent2: "); + out.println(b64BigInt(key.getPrimeExponentQ())); + out.print("Coefficient: "); + out.println(b64BigInt(key.getCrtCoefficient())); + + return sw.toString(); + } + + /** Given a DH key pair, return the BIND9-style text encoding */ + private String generatePrivateDH(DHPrivateKey key, DHPublicKey pub) + { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + + DHParameterSpec p = key.getParams(); + + out.println("Private-key-format: v1.2"); + out.println("Algorithm: 2 (DH)"); + out.print("Prime(p): "); + out.println(b64BigInt(p.getP())); + out.print("Generator(g): "); + out.println(b64BigInt(p.getG())); + out.print("Private_value(x): "); + out.println(b64BigInt(key.getX())); + out.print("Public_value(y): "); + out.println(b64BigInt(pub.getY())); + + return sw.toString(); + } + + /** Given a DSA key pair, return the BIND9-style text encoding */ + private String generatePrivateDSA(DSAPrivateKey key, DSAPublicKey pub) + { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + + DSAParams p = key.getParams(); + + out.println("Private-key-format: v1.2"); + out.println("Algorithm: 3 (DSA)"); + out.print("Prime(p): "); + out.println(b64BigInt(p.getP())); + out.print("Subprime(q): "); + out.println(b64BigInt(p.getQ())); + out.print("Base(g): "); + out.println(b64BigInt(p.getG())); + out.print("Private_value(x): "); + out.println(b64BigInt(key.getX())); + out.print("Public_value(y): "); + out.println(b64BigInt(pub.getY())); + + return sw.toString(); + } +} diff --git a/src/com/verisignlabs/dnssec/security/DnsKeyPair.java b/src/com/verisignlabs/dnssec/security/DnsKeyPair.java new file mode 100644 index 0000000..d477cd9 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/DnsKeyPair.java @@ -0,0 +1,356 @@ +// $Id: DnsKeyPair.java,v 1.3 2004/01/16 21:07:09 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.verisignlabs.dnssec.security; + +import java.security.*; +import java.security.interfaces.*; +import org.xbill.DNS.*; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** This class forms the basis for representing public/private key + * pairs in a DNSSEC context. It is possible to get a JCA public and + * private key from this object, as well as a KEYRecord encoding of + * the public key. This class is implemented as a UNION of all the + * functionality needed for handing native java, BIND, and possibly + * other underlying KEY engines. + * + * JCA == Java Cryptography Architecture. + * + * @author David Blacka (orig) + * @author $Author: davidb $ (latest) + * @version $Revision: 1.3 $ + */ + +// FIXME: this class is not a generic DnsKeyPair at all. It is really +// a JCEDnsKeyPair. We probably need to reexamine the class design here. + +// NOTE: this class is designed to do "lazy" evaluation of it's +// various cached objects and format conversions, so methods should +// avoid direct access to the member variables. + +public class DnsKeyPair +{ + /** This is the real (base) encoding of the public key. */ + protected DNSKEYRecord mPublicKeyRecord; + + /** This is a precalcuated cache of the KEYRecord converted into a + * JCA public key. */ + private PublicKey mPublicKey; + + /** The private key in Base64 encoded format. This version is + * presumed to be opaque, so no attempts will be made to convert it + * to a JCA private key. */ + protected String mPrivateKeyString; + + /** The private key in JCA format. This is the base encoding for + * instances were JCA private keys are used. */ + protected PrivateKey mPrivateKey; + + /** The local key converter. */ + protected DnsKeyConverter mKeyConverter; + + /** a cached Signature used for signing (initialized with the + * private key) */ + protected Signature mSigner; + + /** a caches Signature used for verifying (intialized with the + * public key) */ + protected Signature mVerifier; + + + private Log log; + + public DnsKeyPair() + { + log = LogFactory.getLog(this.getClass()); + } + + public DnsKeyPair(DNSKEYRecord keyRecord, PrivateKey privateKey) + { + this(); + + setDNSKEYRecord(keyRecord); + setPrivate(privateKey); + } + + public DnsKeyPair(DNSKEYRecord keyRecord, String privateKeyString) + { + this(); + + setDNSKEYRecord(keyRecord); + setPrivateKeyString(privateKeyString); + } + + public DnsKeyPair(Name keyName, int algorithm, PublicKey publicKey, + PrivateKey privateKey) + { + this(); + + DnsKeyConverter conv = new DnsKeyConverter(); + DNSKEYRecord keyrec = conv.generateDNSKEYRecord(keyName, DClass.IN, 0, 0, + algorithm, publicKey); + setDNSKEYRecord(keyrec); + setPrivate(privateKey); + } + + public DnsKeyPair(DnsKeyPair pair) + { + this(); + + setDNSKEYRecord(pair.getDNSKEYRecord()); + setPrivate(pair.getPrivate()); + setPrivateKeyString(pair.getPrivateKeyString()); + } + + /** @return cached DnsKeyConverter object. */ + protected DnsKeyConverter getKeyConverter() + { + if (mKeyConverter == null) + { + mKeyConverter = new DnsKeyConverter(); + } + + return mKeyConverter; + } + + /** @return the appropriate Signature object for this keypair. */ + protected Signature getSignature() + { + Signature s = null; + + // First try and deduce the algorithm from the KEYRecord (which + // will be specific), then try and deduce it from the private key. + // We should have one or the other. + try + { + switch (getDNSKEYAlgorithm()) + { + case DNSSEC.RSAMD5: + s = Signature.getInstance("MD5withRSA"); + break; + case DNSSEC.DSA: + s = Signature.getInstance("SHA1withDSA"); + break; + case DNSSEC.RSASHA1: + s = Signature.getInstance("SHA1withRSA"); + break; + case -1: + s = null; + break; + } + } + catch (NoSuchAlgorithmException e) + { + log.error("error getting Signature object", e); + } + + return s; + } + + /** @return the public key, translated from the KEYRecord, if + * necessary. */ + public PublicKey getPublic() + { + if (mPublicKey == null && getDNSKEYRecord() != null) + { + DnsKeyConverter conv = getKeyConverter(); + setPublic(conv.parseDNSKEYRecord(getDNSKEYRecord())); + } + + return mPublicKey; + } + + + /** sets the public key. This method is generally not used + * directly. */ + protected void setPublic(PublicKey k) + { + mPublicKey = k; + } + + /** @return the private key. */ + public PrivateKey getPrivate() + { + // attempt to convert the private key string format into a JCA + // private key. + if (mPrivateKey == null && mPrivateKeyString != null) + { + mPrivateKey = BINDKeyUtils.convertPrivateKeyString(mPrivateKeyString); + } + + return mPrivateKey; + } + + /** sets the private key */ + public void setPrivate(PrivateKey k) + { + mPrivateKey = k; + } + + /** @return the opaque private key string, null if one doesn't + * exist. */ + public String getPrivateKeyString() + { + if (mPrivateKeyString == null && mPrivateKey != null) + { + PublicKey pub = getPublic(); + mPrivateKeyString = BINDKeyUtils.convertPrivateKey(mPrivateKey, pub, + getDNSKEYAlgorithm()); + } + + return mPrivateKeyString; + } + + /** sets the opaque private key string. */ + public void setPrivateKeyString(String p) + { + mPrivateKeyString = p; + } + + /** @return the private key in an encoded form (normally PKCS#8). */ + public byte[] getEncodedPrivate() + { + PrivateKey priv = getPrivate(); + if (priv != null) return priv.getEncoded(); + return null; + } + + /** Sets the private key from the encoded form (PKCS#8). This + * routine requires that the public key already be assigned. + * Currently it can only handle DSA and RSA keys. */ + public void setEncodedPrivate(byte[] encoded) + { + int alg = getDNSKEYAlgorithm(); + + if (alg >= 0) + { + DnsKeyConverter conv = getKeyConverter(); + setPrivate(conv.convertEncodedPrivateKey(encoded, alg)); + } + } + + /** @return the public DNSKEY record */ + public DNSKEYRecord getDNSKEYRecord() + { + return mPublicKeyRecord; + } + + /** @return a Signature object initialized for signing, or null if + * this key pair does not have a valid private key. */ + public Signature getSigner() + { + if (mSigner == null) + { + mSigner = getSignature(); + PrivateKey priv = getPrivate(); + if (mSigner != null && priv != null) + { + try + { + mSigner.initSign(priv); + } + catch (InvalidKeyException e) + { + log.error("Signature error", e); + } + } + else + { + // do not return an unitialized signer. + return null; + } + } + + return mSigner; + } + + /** @return a Signature object initialized for verifying, or null if + * this key pair does not have a valid public key. */ + 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 unitialized verifier + return null; + } + } + + return mVerifier; + } + + /** sets the public key record */ + public void setDNSKEYRecord(DNSKEYRecord r) + { + mPublicKeyRecord = r; + // force the conversion to PublicKey: + mPublicKey = null; + } + + + public Name getDNSKEYName() + { + DNSKEYRecord kr = getDNSKEYRecord(); + if (kr != null) return kr.getName(); + return null; + } + + public int getDNSKEYAlgorithm() + { + DNSKEYRecord kr = getDNSKEYRecord(); + if (kr != null) return kr.getAlgorithm(); + + PublicKey pk = getPublic(); + if (pk != null) + { + // currently, alg 5 is the default over alg 1 (RSASHA1). + if (pk instanceof RSAPublicKey) return DNSSEC.RSASHA1; + if (pk instanceof DSAPublicKey) return DNSSEC.DSA; + } + + PrivateKey priv = getPrivate(); + if (priv != null) + { + if (priv instanceof RSAPrivateKey) return DNSSEC.RSASHA1; + if (priv instanceof DSAPrivateKey) return DNSSEC.DSA; + } + + return -1; + } + + public int getDNSKEYFootprint() + { + DNSKEYRecord kr = getDNSKEYRecord(); + if (kr != null) return kr.getFootprint(); + return -1; + } +} diff --git a/src/com/verisignlabs/dnssec/security/DnsSecVerifier.java b/src/com/verisignlabs/dnssec/security/DnsSecVerifier.java new file mode 100644 index 0000000..c3012d5 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/DnsSecVerifier.java @@ -0,0 +1,321 @@ +// $Id: DnsSecVerifier.java,v 1.5 2004/02/25 20:46:14 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.security; + +import java.util.*; +import java.io.*; +import java.security.*; + +import org.xbill.DNS.*; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** A class for performing basic DNSSEC verification. The DNSJAVA + * package contains a similar class. This differs (for the moment, + * anyway) by allowing timing "fudge" factors and logging more + * specifically why an RRset did not validate. + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.5 $ + */ +public class DnsSecVerifier implements Verifier +{ + + private class TrustedKeyStore + { + // for now, this is implemented as a hashtable of lists of + // DnsKeyPair objects (obviously, all of them will not have + // private keys). + private HashMap mKeyMap; + + public TrustedKeyStore() + { + mKeyMap = new HashMap(); + } + + public void add(DnsKeyPair pair) + { + String n = pair.getDNSKEYName().toString().toLowerCase(); + List l = (List) mKeyMap.get(n); + if (l == null) + { + l = new ArrayList(); + mKeyMap.put(n, l); + } + + l.add(pair); + } + + public void add(DNSKEYRecord keyrec) + { + DnsKeyPair pair = new DnsKeyPair(keyrec, (PrivateKey) null); + add(pair); + } + + public void add(Name name, int algorithm, PublicKey key) + { + DnsKeyPair pair = new DnsKeyPair(name, algorithm, key, null); + add(pair); + } + + public DnsKeyPair find(Name name, int algorithm, int keyid) + { + String n = name.toString().toLowerCase(); + List l = (List) mKeyMap.get(n); + if (l == null) return null; + + // FIXME: this algorithm assumes that name+alg+footprint is + // unique, which isn't necessarily true. + for (Iterator i = l.iterator(); i.hasNext(); ) + { + DnsKeyPair p = (DnsKeyPair) i.next(); + if (p.getDNSKEYAlgorithm() == algorithm && + p.getDNSKEYFootprint() == keyid) + { + return p; + } + } + return null; + } + } + + private TrustedKeyStore mKeyStore; + private int mStartFudge = 0; + private int mExpireFudge = 0; + private boolean mVerifyAllSigs = false; + + private Log log; + + public DnsSecVerifier() + { + log = LogFactory.getLog(this.getClass()); + + mKeyStore = new TrustedKeyStore(); + } + + public void addTrustedKey(DNSKEYRecord keyrec) + { + mKeyStore.add(keyrec); + } + + public void addTrustedKey(DnsKeyPair pair) + { + mKeyStore.add(pair); + } + + public void addTrustedKey(Name name, int algorithm, PublicKey key) + { + mKeyStore.add(name, algorithm, key); + } + + public void addTrustedKey(Name name, PublicKey key) + { + mKeyStore.add(name, 0, key); + } + + public void setExpireFudge(int fudge) + { + mExpireFudge = fudge; + } + + public void setStartFudge(int fudge) + { + mStartFudge = fudge; + } + + public void setVerifyAllSigs(boolean v) + { + mVerifyAllSigs = v; + } + + private DnsKeyPair findCachedKey(Cache cache, Name name, int algorithm, + int footprint) + { + RRset[] keysets = cache.findAnyRecords(name, Type.KEY); + if (keysets == null) return null; + + // look for the particular key + // FIXME: this assumes that name+alg+footprint is unique. + for (Iterator i = keysets[0].rrs(); i.hasNext(); ) + { + Object o = i.next(); + if (! (o instanceof DNSKEYRecord)) continue; + DNSKEYRecord keyrec = (DNSKEYRecord) o; + if (keyrec.getAlgorithm() == algorithm && + keyrec.getFootprint() == footprint) + { + return new DnsKeyPair(keyrec, (PrivateKey) null); + } + } + + return null; + } + + private DnsKeyPair findKey(Cache cache, Name name, int algorithm, + int footprint) + { + DnsKeyPair pair = mKeyStore.find(name, algorithm, footprint); + if (pair == null && cache != null) + { + pair = findCachedKey(cache, name, algorithm, footprint); + } + + return pair; + } + + private byte validateSignature(RRset rrset, RRSIGRecord sigrec) + { + if (rrset == null || sigrec == null) return DNSSEC.Failed; + if (! rrset.getName().equals(sigrec.getName())) + { + log.info("Signature name does not match RRset name"); + return DNSSEC.Failed; + } + if (rrset.getType() != sigrec.getTypeCovered()) + { + log.info("Signature type does not match RRset type"); + } + + Date now = new Date(); + Date start = sigrec.getTimeSigned(); + Date expire = sigrec.getExpire(); + + if (mStartFudge >= 0) + { + if (mStartFudge > 0) + { + start = new Date(start.getTime() - ((long) mStartFudge * 1000)); + } + if (now.before(start)) + { + log.info("Signature is not yet valid"); + return DNSSEC.Failed; + } + } + + if (mExpireFudge >= 0) + { + if (mExpireFudge > 0) + { + expire = new Date(expire.getTime() + ((long) mExpireFudge * 1000)); + } + if (now.after(expire)) + { + log.info("Signature has expired (now = " + now + + ", sig expires = " + expire); + return DNSSEC.Failed; + } + } + + return DNSSEC.Secure; + } + + /** Verify an RRset against a particular signature. + * + * @return DNSSEC.Secure if the signature verfied, DNSSEC.Failed if + * it did not verify (for any reason), and DNSSEC.Insecure if + * verification could not be completed (usually because the public + * key was not available). */ + public byte verifySignature(RRset rrset, RRSIGRecord sigrec, Cache cache) + { + byte result = validateSignature(rrset, sigrec); + if (result != DNSSEC.Secure) return result; + + DnsKeyPair keypair = findKey(cache, sigrec.getSigner(), + sigrec.getAlgorithm(), + sigrec.getFootprint()); + + if (keypair == null) + { + log.info("could not find appropriate key"); + return DNSSEC.Insecure; + } + + try + { + byte[] data = SignUtils.generateSigData(rrset, sigrec); + + Signature signer = keypair.getVerifier(); + signer.update(data); + + byte[] sig = sigrec.getSignature(); + if (sigrec.getAlgorithm() == DNSSEC.DSA) + { + sig = SignUtils.convertDSASignature(sig); + } + if (!signer.verify(sig)) + { + log.info("Signature failed to verify cryptographically"); + return DNSSEC.Failed; + } + + return DNSSEC.Secure; + } + catch (IOException e) + { + log.error("I/O error", e); + } + catch (GeneralSecurityException e) + { + log.error("Security error", e); + } + + log.debug("Signature failed to verify due to exception"); + return DNSSEC.Insecure; + } + + /** Verifies an RRset. This routine does not modify the RRset. + * + * @return DNSSEC.Secure if the set verified, DNSSEC.Failed if it + * did not, and DNSSEC.Insecure if verification could not + * complete. */ + public int verify(RRset rrset, Cache cache) + { + int result = mVerifyAllSigs ? DNSSEC.Secure: DNSSEC.Insecure; + + Iterator i = rrset.sigs(); + + if ( ! i.hasNext()) + { + log.info("RRset failed to verify due to lack of signatures"); + return DNSSEC.Insecure; + } + + while (i.hasNext()) + { + RRSIGRecord sigrec = (RRSIGRecord) i.next(); + + byte res = verifySignature(rrset, sigrec, cache); + + if (!mVerifyAllSigs && res == DNSSEC.Secure) return res; + + if (!mVerifyAllSigs && res < result) result = res; + + if (mVerifyAllSigs && res != DNSSEC.Secure && res < result) + { + result = res; + } + } + + return result; + } +} diff --git a/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java b/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java new file mode 100644 index 0000000..84eab56 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java @@ -0,0 +1,373 @@ +// $Id: JCEDnsSecSigner.java,v 1.3 2004/02/25 20:46:14 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA +package com.verisignlabs.dnssec.security; + +import java.util.*; +import java.io.*; +import java.security.*; +import java.security.interfaces.*; + +import org.xbill.DNS.*; + +/** This class contains routines for signing DNS zones. + * + * In particular, it contains both an ability to sign an individual + * RRset and the ability to sign and entire zone. It primarily glues + * together the more basic primitives found in {@link SignUtils}. + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.3 $ + */ +public class JCEDnsSecSigner +{ + private DnsKeyConverter mKeyConverter; + + private KeyPairGenerator mRSAKeyGenerator; + private KeyPairGenerator mDSAKeyGenerator; + + + /** 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 (RSAMD5, RSASHA1, or DSA). + * @param flags any flags for the KEY RR. + * @param keysize the size of the key to generate. + * @return a DnsKeyPair with the public and private keys populated. + */ + public DnsKeyPair generateKey(Name owner, + long ttl, + int dclass, + int algorithm, + int flags, + int keysize) + throws IOException, NoSuchAlgorithmException + { + KeyPair pair; + + if (ttl < 0) ttl = 86400; // set to a reasonable default. + + switch (algorithm) + { + case DNSSEC.RSAMD5: + case DNSSEC.RSASHA1: + if (mRSAKeyGenerator == null) + { + mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA"); + } + mRSAKeyGenerator.initialize(keysize); + pair = mRSAKeyGenerator.generateKeyPair(); + break; + case DNSSEC.DSA: + if (mDSAKeyGenerator == null) + { + mDSAKeyGenerator = KeyPairGenerator.getInstance("DSA"); + } + mDSAKeyGenerator.initialize(keysize); + pair = mDSAKeyGenerator.generateKeyPair(); + break; + default: + throw new NoSuchAlgorithmException("Alg " + algorithm); + } + + if (mKeyConverter == null) + { + mKeyConverter = new DnsKeyConverter(); + } + + DNSKEYRecord keyrec = mKeyConverter.generateDNSKEYRecord(owner, + dclass, + ttl, + flags, + algorithm, + pair.getPublic()); + DnsKeyPair dnspair = new DnsKeyPair(); + dnspair.setDNSKEYRecord(keyrec); + dnspair.setPublic(pair.getPublic()); // keep from conv. the keyrec back. + dnspair.setPrivate(pair.getPrivate()); + + return dnspair; + } + + /** 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. + * @return a list of RRSIGRecord objects. + */ + public List signRRset(RRset rrset, List keypairs, Date start, Date expire) + throws IOException, GeneralSecurityException + { + if (rrset == null || keypairs == null) return null; + + // default start to now, expire to start + 1 second. + if (start == null) start = new Date(); + if (expire == null) expire = new Date(start.getTime() + 1000L); + if (keypairs.size() == 0) return null; + + // first, pre-calculate the rrset bytes. + byte[] rrset_data = SignUtils.generateCanonicalRRsetData(rrset); + + ArrayList sigs = new ArrayList(keypairs.size()); + + // for each keypair, sign the rrset. + for (Iterator i = keypairs.iterator(); i.hasNext(); ) + { + DnsKeyPair pair = (DnsKeyPair) i.next(); + DNSKEYRecord keyrec = pair.getDNSKEYRecord(); + if (keyrec == null) continue; + + RRSIGRecord presig = SignUtils.generatePreRRSIG(rrset, keyrec, start, + expire, rrset.getTTL()); + byte[] sign_data = SignUtils.generateSigData(rrset_data, presig); + + Signature signer = pair.getSigner(); + + if (signer == null) + { + // debug + System.out.println("missing private key that goes with:\n" + + pair.getDNSKEYRecord()); + throw new GeneralSecurityException + ("cannot sign without a valid Signer " + + "(probably missing private key)"); + } + + // sign the data. + signer.update(sign_data); + byte[] sig = signer.sign(); + + // Convert to RFC 2536 format, if necessary. + if (pair.getDNSKEYAlgorithm() == DNSSEC.DSA) + { + sig = SignUtils.convertDSASignature + (((DSAPublicKey)pair.getPublic()).getParams(), sig); + } + RRSIGRecord sigrec = SignUtils.generateRRSIG(sig, presig); + sigs.add(sigrec); + } + + return sigs; + } + + /** Create a completely self-signed KEY RRset. + * + * @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 keypairs, Date start, Date expire) + throws IOException, GeneralSecurityException + { + // Generate a KEY RR set to sign. + + RRset keyset = new RRset(); + + for (Iterator i = keypairs.iterator(); i.hasNext(); ) + { + DnsKeyPair pair = (DnsKeyPair) i.next(); + keyset.addRR(pair.getDNSKEYRecord()); + } + + List records = signRRset(keyset, keypairs, start, expire); + + for (Iterator i = records.iterator(); i.hasNext(); ) + { + keyset.addRR((Record) i.next()); + } + + return keyset; + } + + /** 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 keysigningkeypairs the List of KSKs.. + * @param zonekeypairs 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. + * @return the name of the new last_cut. + */ + private Name addRRset(List toList, Name zonename, RRset rrset, + List keysigningkeypairs, List zonekeypairs, + Date start, Date expire, boolean fullySignKeyset, + Name last_cut) + throws IOException, GeneralSecurityException + { + // add the records themselves + for (Iterator i = rrset.rrs(); i.hasNext(); ) + { + toList.add(i.next()); + } + + int type = SignUtils.recordSecType(zonename, rrset.getName(), + rrset.getType(), last_cut); + + // we don't sign non-normal sets (delegations, glue, invalid). + // we also don't sign the zone key set unless we've been asked. + if (type == SignUtils.RR_DELEGATION) + { + return rrset.getName(); + } + if (type == SignUtils.RR_GLUE || type == SignUtils.RR_INVALID) + { + return last_cut; + } + + // check for the zone apex keyset. + if (rrset.getName().equals(zonename) && rrset.getType() == Type.DNSKEY) + { + // if we have key signing keys, sign the keyset with them, + // otherwise we will just sign them with the zonesigning keys. + if (keysigningkeypairs != null && keysigningkeypairs.size() > 0) + { + List sigs = signRRset(rrset, keysigningkeypairs, start, expire); + toList.addAll(sigs); + + // 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 sigs = signRRset(rrset, zonekeypairs, start, expire); + toList.addAll(sigs); + + return last_cut; + } + + /** Given a zone, sign it. + * + * @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 keysigningkeypairs the key pairs that are designated as + * "key signing keys". + * @param zonekeypair this key pairs that are designated as "zone + * signing keys". + * @param start the RRSIG inception time. + * @param expire the RRSIG expiration time. + * @param useOptIn generate Opt-In style NXT records. It will + * consider any insecure delegation to be unsigned. To override + * this, include the name of the insecure delegation in the + * NXTIncludeNames list. + * @param useConservativeOptIn if true, Opt-In NXT records will + * only be generated if there are insecure, unsigned delegations in + * the span. Not effect if useOptIn is false. + * @param fullySignKeyset sign the zone apex keyset with all available keys. + * @param NXTIncludeNames names that are to be included in the NXT + * chain regardless. This may be null and is only used if useOptIn + * is true. + * + * @return an ordered list of {@link org.xbill.DNS.Record} objects, + * representing the signed zone. + */ + public List signZone(Name zonename, + List records, + List keysigningkeypairs, + List zonekeypairs, + Date start, + Date expire, + boolean useOptIn, + boolean useConservativeOptIn, + boolean fullySignKeyset, + List NSECIncludeNames) + throws IOException, GeneralSecurityException + { + + // Remove any existing DNSSEC records (NSEC, RRSIG) + SignUtils.removeGeneratedRecords(zonename, records); + // Sort the zone + Collections.sort(records, new RecordComparator()); + + // Remove any duplicate records. + SignUtils.removeDuplicateRecords(records); + + // Generate DS records + SignUtils.generateDSRecords(zonename, records); + + // Generate NXT records + if (useOptIn) + { + SignUtils.generateOptInNSECRecords(zonename, records, + NSECIncludeNames, + useConservativeOptIn); + } + else + { + SignUtils.generateNSECRecords(zonename, records); + } + + // Assemble into RRsets and sign. + RRset rrset = new RRset(); + ArrayList signed_records = new ArrayList(); + Name last_cut = null; + + for (ListIterator i = records.listIterator(); i.hasNext(); ) + { + Record r = (Record) i.next(); + Name r_name = r.getName(); + + // First record + if (rrset.getName() == null) + { + rrset.addRR(r); + continue; + } + + // Current record is part of the current RRset. + if (rrset.getName().equals(r.getName()) && + rrset.getDClass() == r.getDClass() && + rrset.getType() == r.getType()) + { + rrset.addRR(r); + continue; + } + + // Otherwise, we have completed the RRset + // Sign the records + + // add the RRset to the list of signed_records, regardless of + // whether or not we actually end up signing the set. + last_cut = addRRset(signed_records, zonename, rrset, keysigningkeypairs, + zonekeypairs, start, expire, fullySignKeyset, + last_cut); + + rrset.clear(); + rrset.addRR(r); + } + + // add the last RR set + addRRset(signed_records, zonename, rrset, keysigningkeypairs, zonekeypairs, + start, expire, fullySignKeyset, last_cut); + + return signed_records; + } +} diff --git a/src/com/verisignlabs/dnssec/security/RecordComparator.java b/src/com/verisignlabs/dnssec/security/RecordComparator.java new file mode 100644 index 0000000..7d398db --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/RecordComparator.java @@ -0,0 +1,91 @@ +// $Id: RecordComparator.java,v 1.2 2004/01/16 17:54:48 davidb Exp $ +// +// Copyright (C) 2000-2003 Network Solutions, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.security; + +import java.util.*; + +import org.xbill.DNS.*; + +/** This class implements a comparison operator for {@link + * org.xbill.DNS.Record} objects. It imposes a canonical order + * consistent with DNSSEC. It does not put records within a RRset + * into canonical order: see {@link ByteArrayComparator}. + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.2 $ */ + +public class RecordComparator implements Comparator +{ + public RecordComparator() + {} + + /** In general, types are compared numerically. However, SOA and NS + * are ordered before the rest. */ + private int compareTypes(int a, int b) + { + if (a == b) return 0; + if (a == Type.SOA) return -1; + if (b == Type.SOA) return 1; + + if (a == Type.NS) return -1; + if (b == Type.NS) return 1; + + if (a < b) return -1; + + return 1; + } + + public int compare(Object o1, Object o2) + throws ClassCastException + { + Record a = (Record) o1; + Record b = (Record) o2; + + if (a == null && b == null) return 0; + if (a == null) return 1; + if (b == null) return -1; + + int res = a.getName().compareTo(b.getName()); + if (res != 0) return res; + + int a_type = a.getType(); + int b_type = b.getType(); + int sig_type = 0; + + if (a_type == Type.RRSIG) + { + a_type = ((RRSIGRecord) a).getTypeCovered(); + sig_type = 1; + } + if (b_type == Type.RRSIG) + { + b_type = ((RRSIGRecord) b).getTypeCovered(); + sig_type = -1; + } + + res = compareTypes(a_type, b_type); + if (res != 0) return res; + + if (sig_type != 0) return sig_type; + + return 0; + } +} diff --git a/src/com/verisignlabs/dnssec/security/SignUtils.java b/src/com/verisignlabs/dnssec/security/SignUtils.java new file mode 100644 index 0000000..7e2eba7 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/SignUtils.java @@ -0,0 +1,927 @@ +// $Id: SignUtils.java,v 1.7 2004/03/23 17:53:57 davidb Exp $ +// +// Copyright (C) 2001-2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.security; + +import java.util.*; +import java.io.*; +import java.security.MessageDigest; +import java.security.interfaces.DSAParams; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; + +import org.xbill.DNS.*; +import org.xbill.DNS.utils.base64; // debug only + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** This class contains a bunch of utility methods that are generally + * useful in signing zones. + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.7 $ + */ + +public class SignUtils +{ + private static final int DSA_SIGNATURE_LENGTH = 20; + private static final int ASN1_INT = 0x02; + private static final int ASN1_SEQ = 0x30; + + public static final int RR_NORMAL = 0; + public static final int RR_DELEGATION = 1; + public static final int RR_GLUE = 2; + public static final int RR_INVALID = 3; + + private static Log log; + + static { + log = LogFactory.getLog(SignUtils.class); + } + + public static void setLog(Log v) + { + log = v; + } + /** Generate from some basic information a prototype SIG RR + * containing everything but the actual signature itself. + * + * @param rrset the RRset being signed. + * @param key the public KEY RR counterpart to the key being used + * to sign the RRset + * @param start the SIG inception time. + * @param expire the SIG expiration time. + * @param sig_ttl the TTL of the resulting SIG record. + * @return a prototype signature based on the RRset and key + * information. */ + public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key, + Date start, Date expire, + long sig_ttl) + { + return new RRSIGRecord(rrset.getName(), + rrset.getDClass(), + sig_ttl, + rrset.getType(), + key.getAlgorithm(), + (int) rrset.getTTL(), + expire, + start, + key.getFootprint(), + key.getName(), + null); + } + + /** Generate from some basic information a prototype SIG RR + * containing everything but the actual signature itself. + * + * @param rec the DNS record being signed (forming an entire RRset). + * @param key the public KEY RR counterpart to the key signing the + * record. + * @param start the SIG inception time. + * @param expire the SIG expiration time. + * @param sig_ttl the TTL of the result SIG record. + * @return a prototype signature based on the Record and key + * information. */ + public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key, + Date start, Date expire, + long sig_ttl) + { + return new RRSIGRecord(rec.getName(), + rec.getDClass(), + sig_ttl, + rec.getType(), + key.getAlgorithm(), + rec.getTTL(), + expire, + start, + key.getFootprint(), + key.getName(), + null); + } + + + /** Generate the binary image of the prototype SIG RR. + * + * @param presig the SIG RR prototype. + * @return the RDATA portion of the prototype SIG record. This + * forms the first part of the data to be signed. */ + private static byte[] generatePreSigRdata(RRSIGRecord presig) + throws IOException + { + // Generate the binary image; + DNSOutput image = new DNSOutput(); + + // precalc some things + int start_time = (int) (presig.getTimeSigned().getTime() / 1000); + int expire_time = (int) (presig.getExpire().getTime() / 1000); + Name signer = presig.getSigner(); + + // first write out the partial SIG record (this is the SIG RDATA + // minus the actual signature. + image.writeU16(presig.getTypeCovered()); + image.writeU8(presig.getAlgorithm()); + image.writeU8(presig.getLabels()); + image.writeU32((int) presig.getOrigTTL()); + image.writeU32(expire_time); + image.writeU32(start_time); + image.writeU16(presig.getFootprint()); + image.writeByteArray(signer.toWireCanonical()); + + return image.toByteArray(); + } + + /** Calculate the canonical wire line format of the RRset. + * + * @param rrset the RRset to convert. + * @return the canonical wire line format of the rrset. This is + * the second part of data to be signed.*/ + public static byte[] generateCanonicalRRsetData(RRset rrset) + throws IOException + { + DNSOutput image = new DNSOutput(); + + // now convert load the wire format records in the RRset into a + // list of byte arrays. + ArrayList canonical_rrs = new ArrayList(); + for (Iterator i = rrset.rrs(); i.hasNext(); ) + { + Record r = (Record) i.next(); + byte[] wire_fmt = r.toWireCanonical(); + canonical_rrs.add(wire_fmt); + } + + // put the records into the correct ordering. + // Caculate the offset where the RDATA begins (we have to skip + // past the length byte) + + // FIXME: currently, draft-ietf-dnsext-dnssec-records-06 has us + // sorting by length first, then bytes. This can be accomplished + // by not skipping past the RDLENGTH field, I think. + // FIXME update: I pointed this out as an error, and subsequent + // versions should correct this, setting the standard back to + // bytes, then length. + + int offset = rrset.getName().toWireCanonical().length + 10; + ByteArrayComparator bac = new ByteArrayComparator(offset, false); + + Collections.sort(canonical_rrs, bac); + + for (Iterator i = canonical_rrs.iterator(); i.hasNext(); ) + { + byte[] wire_fmt_rec = (byte[]) i.next(); + image.writeByteArray(wire_fmt_rec); + } + + return image.toByteArray(); + } + + /** Given an RRset and the prototype signature, generate the + * canonical data that is to be signed. + * + * @param rrset the RRset to be signed. + * @param presig a prototype SIG RR created using the same RRset. + * @return a block of data ready to be signed. + */ + public static byte[] generateSigData(RRset rrset, RRSIGRecord presig) + throws IOException + { + byte[] rrset_data = generateCanonicalRRsetData(rrset); + + return generateSigData(rrset_data, presig); + } + + /** Given an RRset and the prototype signature, generate the + * canonical data that is to be signed. + * + * @param rrset_data the RRset converted into canonical wire line + * format (as per the canonicalization rules in RFC 2535). + * @param presig the prototype signature based on the same RRset + * represented in rrset_data. + * @return a block of data ready to be signed. + */ + public static byte[] generateSigData(byte[] rrset_data, RRSIGRecord presig) + throws IOException + { + byte[] sig_rdata = generatePreSigRdata(presig); + + ByteArrayOutputStream image + = new ByteArrayOutputStream(sig_rdata.length + rrset_data.length); + + image.write(sig_rdata); + image.write(rrset_data); + + return image.toByteArray(); + } + + /** Given the acutal signature an the prototype signature, combine + * them and return the fully formed SIGRecord. + * + * @param signature the cryptographic signature, in DNSSEC format. + * @param presig the prototype SIG RR to add the signature to. + * @return the fully formed SIG RR. + */ + public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig) + { + return new RRSIGRecord(presig.getName(), + presig.getDClass(), + presig.getTTL(), + presig.getTypeCovered(), + presig.getAlgorithm(), + presig.getOrigTTL(), + presig.getExpire(), + presig.getTimeSigned(), + presig.getFootprint(), + presig.getSigner(), + signature); + } + + /** Converts from a RFC 2536 formatted DSA signature to a JCE + * (ASN.1) formatted signature. + * + *

ASN.1 format = ASN1_SEQ . seq_length . ASN1_INT . Rlength . R + * . ANS1_INT . Slength . S

+ * + * The integers R and S may have a leading null byte to force the + * integer positive. + * + * @param signature the RFC 2536 formatted DSA signature. + * @return The ASN.1 formatted DSA signature. + * @throws SignatureException if there was something wrong with the + * RFC 2536 formatted signature. + **/ + public static byte[] convertDSASignature(byte[] signature) + throws SignatureException + { + if (signature.length != 41) + throw new SignatureException("RFC 2536 signature not expected length."); + + byte r_pad = 0; + byte s_pad = 0; + + // handle initial null byte padding. + if (signature[1] < 0) r_pad++; + if (signature[21] < 0) s_pad++; + + // ASN.1 length = R length + S length + (2 + 2 + 2), where each 2 + // is for a ASN.1 type-length byte pair of which there are three + // (SEQ, INT, INT). + byte sig_length = (byte) (40 + r_pad + s_pad + 6); + + byte sig[] = new byte[sig_length]; + byte pos = 0; + + sig[pos++] = ASN1_SEQ; + sig[pos++] = (byte) (sig_length - 2); // all but the SEQ type+length. + sig[pos++] = ASN1_INT; + sig[pos++] = (byte) (20 + r_pad); + + // copy the value of R, leaving a null byte if necessary + if (r_pad == 1) sig[pos++] = 0; + + System.arraycopy(signature, 1, sig, pos, 20); + pos += 20; + + sig[pos++] = ASN1_INT; + sig[pos++] = (byte) (20 + s_pad); + + // copy the value of S, leaving a null byte if necessary + if (s_pad == 1) sig[pos++] = 0; + + System.arraycopy(signature, 21, sig, pos, 20); + + return sig; + } + + + /** Converts from a JCE (ASN.1) formatted DSA signature to a RFC + * 2536 compliant signature. + * + *

rfc2536 format = T . R . S

+ * + * where T is a number between 0 and 8, which is based on the DSA + * key length, and R & S are formatted to be exactly 20 bytes each + * (no leading null bytes). + * + * @param params the DSA parameters associated with the DSA key + * used to generate the signature. + * @param signature the ASN.1 formatted DSA signature. + * @return a RFC 2536 formatted DSA signature. + * @throws SignatureException if something is wrong with the ASN.1 + * format. + */ + public static byte[] convertDSASignature(DSAParams params, byte[] signature) + throws SignatureException + { + if (signature[0] != ASN1_SEQ || signature[2] != ASN1_INT) + { + throw new SignatureException + ("Invalid ASN.1 signature format: expected SEQ, INT"); + } + + byte r_pad = (byte) (signature[3] - 20); + + if (signature[24 + r_pad] != ASN1_INT) { + throw new SignatureException + ("Invalid ASN.1 signature format: expected SEQ, INT, INT"); + } + + log.trace("(start) ASN.1 DSA Sig:\n" + base64.toString(signature)); + + byte s_pad = (byte) (signature[25 + r_pad] - 20); + + byte[] sig = new byte[41]; // all rfc2536 signatures are 41 bytes. + + // Calculate T: + sig[0] = (byte) ((params.getP().bitLength() - 512) / 64); + + // copy R value + if (r_pad >= 0) + { + System.arraycopy(signature, 4 + r_pad, sig, 1, 20); + } + else + { + // R is shorter than 20 bytes, so right justify the number + // (r_pad is negative here, remember?). + Arrays.fill(sig, 1 , 1 - r_pad, (byte) 0); + System.arraycopy(signature, 4, sig, 1 - r_pad, 20 + r_pad); + } + + // copy S value + if (s_pad >= 0) + { + System.arraycopy(signature, 26 + r_pad + s_pad, sig, 21, 20); + } + else + { + // S is shorter than 20 bytes, so right justify the number + // (s_pad is negative here). + Arrays.fill(sig, 21, 21 - s_pad, (byte) 0); + System.arraycopy(signature, 26 + r_pad, sig, 21 - s_pad, 20 + s_pad); + } + + if (r_pad < 0 || s_pad < 0) + { + log.trace("(finish ***) RFC 2536 DSA Sig:\n" + base64.toString(sig)); + + } + else + { + log.trace("(finish) RFC 2536 DSA Sig:\n" + base64.toString(sig)); + } + + return sig; + } + + /** This is a convenience routine to help us classify records/RRsets. + * + * It charaterizes a record/RRset as one of the following classes:
+ *
+ + *
NORMAL
This record/set is properly within the zone + * an subject to all NXT and SIG processing.
+ + *
DELEGATION
This is a zone delegation point (or + * cut). It is used in NXT processing but is not signed.
+ + *
GLUE
This is a glue record and therefore not + * properly within the zone. It is not included in NXT or SIG + * processing. Normally glue records are A records, but this + * routine calls anything that is below a zone delegation + * glue.
+ + *
INVALID
This record doesn't even belong in the + * zone.
+ + *

+ + * This method must be called successively on records in the + * canonical name ordering, and the caller must maintain the + * last_cut parameter. + + * @param zonename the name of the zone that is being processed. + * @param name the name of the record/set under consideration. + * @param type the type of the record/set under consideration. + * @param last_cut the name of the last DELEGATION record/set that + * was encountered while iterating over the zone in canonical + * order. + */ + public static int recordSecType(Name zonename, Name name, int type, + Name last_cut) + { + // records not even in the zone itself are invalid. + if (!name.subdomain(zonename)) return RR_INVALID; + + // records that are at the zonename node are definitely normal. + if (name.equals(zonename)) return RR_NORMAL; + + // since we are not at zone level, any NS records are delegations + if (type == Type.NS) return RR_DELEGATION; + + if (last_cut != null) + { + // if we are at the same level as a delegation point, but not an + // NS record, then we either a DS record or glue. + if (name.equals(last_cut)) + { + if (type == Type.DS || type == Type.NXT || + type == Type.NSEC) return RR_NORMAL; + // actually, this is probably INVALID, but it could be glue. + return RR_GLUE; + } + // below the delegation, we are glue + if (name.subdomain(last_cut)) return RR_GLUE; + } + + return RR_NORMAL; + } + + /** Given a canonical ordered list of records from a single zone, + * order the raw records into a list of RRsets. + * + * @param records a list of {@link org.xbill.DNS.Record} objects, + * in DNSSEC canonical order. + * @return a List of {@link org.xbill.DNS.RRset} objects. + */ + public static List assembleIntoRRsets(List records) + { + RRset rrset = new RRset(); + ArrayList rrsets = new ArrayList(); + + for (Iterator i = records.iterator(); i.hasNext(); ) + { + Object o = i.next(); + + if (! (o instanceof Record)) + { + log.warn("assembleIntoRRsets: a non-record object was " + + "encountered and skipped: " + o + " (" + o.getClass() + ")"); + continue; + } + + Record r = (Record) o; + Name r_name = r.getName(); + + // First record + if (rrset.getName() == null) + { + rrset.addRR(r); + continue; + } + + // Current record is part of the current RRset. + if (rrset.getName().equals(r.getName()) && + rrset.getDClass() == r.getDClass() && + ((r.getType() == Type.RRSIG && + rrset.getType() == ((RRSIGRecord)r).getTypeCovered()) || + rrset.getType() == r.getType())) + { + rrset.addRR(r); + continue; + } + + // otherwise, we have completed the RRset + rrsets.add(rrset); + + // set up for the next set. + rrset = new RRset(); + rrset.addRR(r); + } + + // add the last rrset. + rrsets.add(rrset); + + return rrsets; + } + + + /** A little private class to hold information about a given + * node. */ + private static class NodeInfo + { + public Name name; + public int type; + public long ttl; + public int dclass; + public Set typemap; + public boolean isSecureNode; // opt-in support. + public boolean hasOptInSpan; // opt-in support. + public int nsecIndex; + + public NodeInfo(Record r) + { + this.name = r.getName(); + this.type = r.getType(); + this.ttl = r.getTTL(); + this.dclass = r.getDClass(); + this.typemap = new HashSet(); + this.isSecureNode = false; + this.hasOptInSpan = false; + addType(type); + } + public void addType(int type) + { + this.typemap.add(new Integer(type)); + + // Opt-In support. + if (type != Type.NS && type != Type.NSEC && type != Type.RRSIG) + { + isSecureNode = true; + } + } + public String toString() + { + StringBuffer sb = new StringBuffer(name.toString()); + if (isSecureNode) sb.append("(S)"); + if (hasOptInSpan) sb.append("(O)"); + return sb.toString(); + } + + public int[] getTypes() + { + Object[] a = typemap.toArray(); + int[] res = new int[a.length]; + + for (int i = 0; i < a.length; i++) + { + res[i] = ((Integer) a[i]).intValue(); + } + return res; + } + } + + /** Given a canonical (by name) ordered list of records in a zone, + * generate the NXT records in place. + * + * Note that the list that the records are stored in must support + * the listIterator.add() operation. + * + * @param zonename the name of the zone (used to distinguish + * between zone apex NS RRsets and delegations). + * @param records a list of {@link org.xbill.DNS.Record} objects in + * DNSSEC canonical order. + */ + public static void generateNSECRecords(Name zonename, List records) + { + // This works by iterating over a known sorted list of records. + + NodeInfo last_node = null; + NodeInfo current_node = null; + + Name last_cut = null; + int backup; + + for (ListIterator i = records.listIterator(); i.hasNext(); ) + { + Record r = (Record) i.next(); + Name r_name = r.getName(); + int r_type = r.getType(); + int r_sectype = recordSecType(zonename, r_name, r_type, last_cut); + + // skip irrelevant records + if (r_sectype == RR_INVALID || r_sectype == RR_GLUE) continue; + + // note our last delegation point so we can recognize glue. + if (r_sectype == RR_DELEGATION) last_cut = r_name; + + // first node -- initialize + if (current_node == null) + { + current_node = new NodeInfo(r); + current_node.addType(Type.RRSIG); + current_node.addType(Type.NSEC); + continue; + } + + // record name hasn't changed, so we are still on the same node. + if (r_name.equals(current_node.name)) + { + current_node.addType(r_type); + continue; + } + + if (last_node != null) + { + NSECRecord nsec = new NSECRecord(last_node.name, last_node.dclass, + last_node.ttl, current_node.name, + last_node.getTypes()); + // Note: we have to add this through the iterator, otherwise + // the next access via the iterator will generate a + // ConcurrencyModificationException. + backup = i.nextIndex() - last_node.nsecIndex; + for (int j = 0; j < backup; j++) i.previous(); + i.add(nsec); + for (int j = 0; j < backup; j++) i.next(); + + log.trace("Generated: " + nsec); + } + + last_node = current_node; + + current_node.nsecIndex = i.previousIndex(); + current_node = new NodeInfo(r); + current_node.addType(Type.RRSIG); + current_node.addType(Type.NSEC); + } + + // Generate next to last NSEC + if (last_node != null) + { + NSECRecord nsec = new NSECRecord(last_node.name, last_node.dclass, + last_node.ttl, current_node.name, + last_node.getTypes()); + records.add(last_node.nsecIndex - 1, nsec); + log.trace("Generated: " + nsec); + } + + // Generate last NSEC + NSECRecord nsec = new NSECRecord(current_node.name, current_node.dclass, + current_node.ttl, zonename, + current_node.getTypes()); + records.add(nsec); + + log.trace("Generated: " + nsec); + } + + + /** Given a canonical (by name) ordered list of records in a zone, + * generate the NXT records in place. + * + * Note thatthe list that the records are stored in must support + * the listIterator.add operation. + * + * @param zonename the name of the zone apex, used to distinguish + * between authoritative and delegation NS RRsets. + * @param records a list of {@link org.xbill.DNS.Record}s in DNSSEC + * canonical order. + * @param includeNames a list of names that should be in the NXT + * chain regardless. This may be null. + * @param beConservative if true, then Opt-In NXTs will only be + * generated where there is actually a span of insecure + * delegations. + */ + public static void generateOptInNSECRecords(Name zonename, + List records, + List includeNames, + boolean beConservative) + { + // This works by iterating over a known sorted list of records. + + NodeInfo last_node = null; + NodeInfo current_node = null; + + Name last_cut = null; + int backup; + HashSet includeSet = null; + + if (includeNames != null) + { + includeSet = new HashSet(includeNames); + } + + for (ListIterator i = records.listIterator(); i.hasNext(); ) + { + Record r = (Record) i.next(); + Name r_name = r.getName(); + int r_type = r.getType(); + int r_sectype = recordSecType(zonename, r_name, r_type, last_cut); + + // skip irrelevant records + if (r_sectype == RR_INVALID || r_sectype == RR_GLUE) continue; + + // note our last delegation point so we can recognize glue. + if (r_sectype == RR_DELEGATION) last_cut = r_name; + + // first node -- initialize + if (current_node == null) + { + current_node = new NodeInfo(r); + current_node.addType(Type.RRSIG); + continue; + } + + // record name hasn't changed, so we are still on the same node. + if (r_name.equals(current_node.name)) + { + current_node.addType(r_type); + continue; + } + + // If the name is in the set of included names, mark it as + // secure. + if (includeSet != null && includeSet.contains(current_node.name)) + { + current_node.isSecureNode = true; + } + + if (last_node != null && current_node.isSecureNode) + { + // generate a NSEC record. + if (beConservative && !last_node.hasOptInSpan) + { + last_node.addType(Type.NSEC); + } + NSECRecord nsec = new NSECRecord(last_node.name, last_node.dclass, + last_node.ttl, current_node.name, + last_node.getTypes()); + // Note: we have to add this through the iterator, otherwise + // the next access via the iterator will generate a + // ConcurrencyModificationException. + backup = i.nextIndex() - last_node.nsecIndex; + for (int j = 0; j < backup; j++) i.previous(); + i.add(nsec); + for (int j = 0; j < backup; j++) i.next(); + + log.trace("Generated: " + nsec); + } + + if (current_node.isSecureNode) + { + last_node = current_node; + } + else if (last_node != null) + { + // last_node does not change -- last_node is essentially the + // last *secure* node, and current_node is not secure. + // However, we need to note the passing of the insecure node. + last_node.hasOptInSpan = true; + } + + current_node.nsecIndex = i.previousIndex(); + current_node = new NodeInfo(r); + current_node.addType(Type.RRSIG); + } + + // Generate next to last NSEC + if (last_node != null && current_node.isSecureNode) + { + // generate a NSEC record. + if (beConservative && !last_node.hasOptInSpan) + { + last_node.addType(Type.NSEC); + } + NSECRecord nsec = new NSECRecord(last_node.name, last_node.dclass, + last_node.ttl, current_node.name, + last_node.getTypes()); + records.add(last_node.nsecIndex - 1, nsec); + log.trace("Generated: " + nsec); + } + + // Generate last NSEC + NSECRecord nsec; + if (current_node.isSecureNode) + { + if (beConservative) + { + current_node.addType(Type.NSEC); + } + nsec = new NSECRecord(current_node.name, current_node.dclass, + current_node.ttl, zonename, + current_node.getTypes()); + // we can just tack this on the end as we are working on the + // last node. + records.add(nsec); + } + else + { + nsec = new NSECRecord(last_node.name, last_node.dclass, last_node.ttl, + zonename, last_node.getTypes()); + // We need to tack this on after the last secure node, not the + // end of the whole list. + records.add(last_node.nsecIndex, nsec); + } + + log.trace("Generated: " + nsec); + } + + + /** Given a zone with DNSKEY records at delegation points, convert + * those KEY records into their corresponding DS records in place. + * + * @param zonename the name of the zone, used to reliably + * distinguish the zone apex from other records. + * @param records a list of {@link org.xbill.DNS.Record} objects. + */ + public static void generateDSRecords(Name zonename, List records) + throws IOException + { + + for (ListIterator i = records.listIterator(); i.hasNext(); ) + { + Record r = (Record) i.next(); + if (r == null) continue; // this should never be true. + + Name r_name = r.getName(); + if (r_name == null) continue; // this should never be true. + + // Convert non-zone level KEY records into DS records. + if (r.getType() == Type.DNSKEY && !r_name.equals(zonename)) + { + DSRecord ds = calculateDSRecord((DNSKEYRecord) r, r.getTTL()); + + i.set(ds); + } + } + } + + /** Given a zone, remove all records that are generated. + * + * @param zonename the name of the zone. + * @param records a list of {@link org.xbill.DNS.Record} objects. + */ + public static void removeGeneratedRecords(Name zonename, List records) + { + for (Iterator i = records.iterator(); i.hasNext(); ) + { + Record r = (Record) i.next(); + + if (r.getType() == Type.RRSIG || + r.getType() == Type.NSEC) + { + i.remove(); + } + } + } + + /** Remove duplicate records from a list of records. This routine + * presumes the list of records is in a canonical sorted order, at + * least on name and RR type. + * + * @param records a list of {@link org.xbill.DNS.Record} object, in + * sorted order. + */ + public static void removeDuplicateRecords(List records) + { + Record lastrec = null; + for (Iterator i = records.iterator(); i.hasNext(); ) + { + Record r = (Record) i.next(); + if (lastrec == null) + { + lastrec = r; + continue; + } + if (lastrec.equals(r)) + { + i.remove(); + continue; + } + lastrec = r; + } + } + + /** Given a DNSKEY record, generate the DS record from it. + * + * @param keyrec the KEY record in question. + * @param ttl the desired TTL for the generated DS record. If + * zero, or negative, the original KEY RR's TTL will be used. + * @return the corresponding {@link org.xbill.DNS.DSRecord} + */ + public static DSRecord calculateDSRecord(DNSKEYRecord keyrec, long ttl) + throws IOException + { + 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 + { + MessageDigest md = MessageDigest.getInstance("SHA"); + + byte[] digest = md.digest(os.toByteArray()); + + return new DSRecord(keyrec.getName(), keyrec.getDClass(), ttl, + keyrec.getFootprint(), keyrec.getAlgorithm(), + DSRecord.SHA1_DIGEST_ID, digest); + + } + catch (NoSuchAlgorithmException e) + { + log.error("", e); + return null; + } + } +} diff --git a/src/com/verisignlabs/dnssec/security/TypeMap.java b/src/com/verisignlabs/dnssec/security/TypeMap.java new file mode 100644 index 0000000..756a66f --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/TypeMap.java @@ -0,0 +1,182 @@ +// $Id: TypeMap.java,v 1.5 2004/03/23 17:53:57 davidb Exp $ +// +// Copyright (C) 2004 Verisign, Inc. + +package com.verisignlabs.dnssec.security; + +import java.util.*; + +import org.xbill.DNS.Type; +import org.xbill.DNS.DNSOutput; + +/** This class represents the multiple type maps of the NSEC + * record. Currently it is just used to convert the wire format type + * map to the int array that org.xbill.DNS.NSECRecord uses. */ + +public class TypeMap +{ + private static final Integer[] integerArray = new Integer[0]; + + private Set typeSet; + + public TypeMap() + { + this.typeSet = new HashSet(); + } + + /** Add the given type to the typemap. */ + public void set(int type) + { + typeSet.add(new Integer(type)); + } + + /** Remove the given type from the type map. */ + public void clear(int type) + { + typeSet.remove(new Integer(type)); + } + + /** @return true if the given type is present in the type map. */ + public boolean get(int type) + { + return typeSet.contains(new Integer(type)); + } + + + public static TypeMap fromTypes(int[] types) + { + TypeMap m = new TypeMap(); + for (int i = 0; i < types.length; i++) + { + m.set(types[i]); + } + + return m; + } + + /** Given an array of bytes representing a wire-format type map, + * construct the TypeMap object. */ + public static TypeMap fromBytes(byte[] map) + { + int m = 0; + TypeMap typemap = new TypeMap(); + + int map_number; + int byte_length; + + while (m < map.length) + { + map_number = map[m++]; + byte_length = map[m++]; + + for (int i = 0; i < byte_length; i++) + { + for (int j = 0; j < 8; j++) + { + if ( (map[m + i] & (1 << (7 - j))) != 0 ) + { + typemap.set(map_number * 8 + j); + } + } + } + m += byte_length; + } + + return typemap; + } + + /** @return the normal string representation of the typemap. */ + public String toString() + { + int[] types = getTypes(); + Arrays.sort(types); + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < types.length; i++) + { + sb.append(" "); + sb.append(Type.string(types[i])); + } + + return sb.toString(); + } + + 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; + + // write the map "header" -- the base and the length of the map. + out.writeU8(base & 0xFF); + out.writeU8(map_length & 0xFF); + + // allocate a temporary scratch space for caculating the actual + // bitmap. + byte[] map = new byte[map_length]; + + // for each type in our sub-array, set its corresponding bit in the map. + for (int i = start; i < end; i++) + { + map[ (types[i] & 0xFF) / 8 ] |= ( 1 << (7 - types[i] % 8) ); + } + // write out the resulting binary bitmap. + for (int i = 0; i < map.length; i++) + { + out.writeU8(map[i]); + } + } + + public byte[] toWire() + { + int[] types = getTypes(); + + Arrays.sort(types); + + int mapbase = -1; + int mapstart = -1; + + DNSOutput out = new DNSOutput(); + + for (int i = 0; i < types.length; i++) + { + int base = types[i] >> 8; + if (base == mapbase) continue; + if (mapstart >= 0) + { + mapToWire(out, types, mapbase, mapstart, i); + } + mapbase = base; + mapstart = i; + } + mapToWire(out, types, mapbase, mapstart, types.length); + + return out.toByteArray(); + } + + public int[] getTypes() + { + Integer[] a = (Integer[]) typeSet.toArray(integerArray); + + int[] res = new int[a.length]; + for (int i = 0; i < res.length; i++) { + res[i] = a[i].intValue(); + } + + return res; + } + + public static int[] fromWireToTypes(byte[] wire_fmt) + { + return TypeMap.fromBytes(wire_fmt).getTypes(); + } + + public static byte[] fromTypesToWire(int[] types) + { + return TypeMap.fromTypes(types).toWire(); + } + +} diff --git a/src/com/verisignlabs/dnssec/security/ZoneUtils.java b/src/com/verisignlabs/dnssec/security/ZoneUtils.java new file mode 100644 index 0000000..12338a6 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/ZoneUtils.java @@ -0,0 +1,138 @@ +// $Id: ZoneUtils.java,v 1.3 2004/01/15 17:32:18 davidb Exp $ +// +// Copyright (C) 2003 VeriSign, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA + +package com.verisignlabs.dnssec.security; + +import java.util.*; +import java.io.*; + +import org.xbill.DNS.*; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** This class contains a bunch of utility methods that are generally + * useful in manipulating zones. + * + * @author David Blacka (original) + * @author $Author: davidb $ + * @version $Revision: 1.3 $ + */ + +public class ZoneUtils +{ + + private static Log log; + + static { + log = LogFactory.getLog(ZoneUtils.class); + } + + /** Load a zone file. + * + * @param zonefile the filename/path of the zonefile to read. + * @param origin the origin to use for the zonefile (may be null if + * the origin is specified in the zone file itself). + * @return a {@link java.util.List} of {@link org.xbill.DNS.Record} + * objects. + * @throws IOException if something goes wrong reading the zone + * file. + */ + public static List readZoneFile(String zonefile, Name origin) + throws IOException + { + ArrayList records = new ArrayList(); + Master m = new Master(zonefile, origin); + + Record r = null; + + while ( (r = m.nextRecord()) != null ) + { + records.add(r); + } + + return records; + } + + /** Write the records out into a zone file. + * + * @param records a {@link java.util.List} of {@link + * org.xbill.DNS.Record} objects forming a zone. + * @param zonefile the file to write to. If null or equal to "-", + * System.out is used. + */ + public static void writeZoneFile(List records, String zonefile) + throws IOException + { + PrintWriter out = null; + + if (zonefile == null || zonefile.equals("-")) + { + out = new PrintWriter(System.out); + } + else + { + out = new PrintWriter(new BufferedWriter(new FileWriter(zonefile))); + } + + + for (Iterator i = records.iterator(); i.hasNext(); ) + { + out.println(i.next()); + } + + out.close(); + } + + /** Given just the list of records, determine the zone name + * (origin). + * + * @param records a list of {@link org.xbill.DNS.Record} or {@link + * org.xbill.DNS.RRset} objects. + * @return the zone name, if found. null if one couldn't be found.q + */ + public static Name findZoneName(List records) + { + for (Iterator i = records.iterator(); i.hasNext(); ) + { + int type = 0; + Name n = null; + + Object o = i.next(); + + if (o instanceof Record) + { + Record r = (Record) o; + type = r.getType(); + n = r.getName(); + } + else if (o instanceof RRset) + { + RRset r = (RRset) o; + type = r.getType(); + n = r.getName(); + } + + if (type == Type.SOA) return n; + } + + return null; + } +} diff --git a/src/com/verisignlabs/dnssec/security/package.html b/src/com/verisignlabs/dnssec/security/package.html new file mode 100644 index 0000000..74cf0f4 --- /dev/null +++ b/src/com/verisignlabs/dnssec/security/package.html @@ -0,0 +1,35 @@ + + +This package contains a number of utility classes that can be used to +implement DNSSEC tool (key generation, zone signing, etc.) +functionality. + +

+ +

+ + +
+ +Copyright © 2003 Verisign, Inc. by +David Blacka

+ +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version.

+ +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details.

+ +You should have received a copy of the +GNU Library General Public License +along with this library; if not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +

+

+ + diff --git a/svn-commit.tmp b/svn-commit.tmp new file mode 100644 index 0000000..a37ab7c --- /dev/null +++ b/svn-commit.tmp @@ -0,0 +1,4 @@ +signing tools project +--This line, and those below, will be ignored-- + +A https://svn.verisignlabs.com/main/dnssec/sectools/trunk