add support for algorithm aliases, fix SignZone so you can specify more than one KSK

git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@64 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e
This commit is contained in:
David Blacka
2006-05-23 21:24:00 +00:00
parent 5ba24d35b1
commit 435acff6d0
9 changed files with 631 additions and 308 deletions

View File

@@ -0,0 +1,263 @@
/*
* $Id$
*
* Copyright (c) 2006 VeriSign. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. The name of the author may not
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package com.verisignlabs.dnssec.security;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.util.HashMap;
import java.util.logging.Logger;
import org.xbill.DNS.DNSSEC;
/**
* This class handles translated DNS signing algorithm identifiers into
* various usable java implementations.
*
* Besides centralizing the logic surrounding matching a DNSKEY algorithm
* identifier with various crypto implementations, it also handles algorithm
* aliasing -- that is, defining a new algorithm identifier to be equivalent
* to an existing identifier.
*
* @author David Blacka (orig)
* @author $Author: davidb $ (latest)
* @version $Revision: 2098 $
*/
public class DnsKeyAlgorithm
{
public static final int UNKNOWN = -1;
public static final int RSA = 1;
public static final int DH = 2;
public static final int DSA = 3;
private static class Entry
{
public String sigName;
public int baseType;
public Entry(String sigName, int baseType)
{
this.sigName = sigName;
this.baseType = baseType;
}
}
/**
* This is a mapping of algorithm identifier to Entry. The Entry contains
* the data needed to map the algorithm to the various crypto
* implementations.
*/
private HashMap mAlgorithmMap;
/**
* This is a mapping of algorithm mnemonics to algorithm identifiers.
*/
private HashMap mMnemonicToIdMap;
/**
* This is a mapping of identifiers to preferred mnemonic -- the preferred
* one is the first defined one
*/
private HashMap mIdToMnemonicMap;
/** This is a cached key pair generator for RSA keys. */
private KeyPairGenerator mRSAKeyGenerator;
/** This is a cache key pair generator for DSA keys. */
private KeyPairGenerator mDSAKeyGenerator;
private Logger log = Logger.getLogger(this.getClass()
.toString());
/** This is the global instance for this class. */
private static DnsKeyAlgorithm mInstance = null;
public DnsKeyAlgorithm()
{
mAlgorithmMap = new HashMap();
mMnemonicToIdMap = new HashMap();
mIdToMnemonicMap = new HashMap();
// Load the standard DNSSEC algorithms.
addAlgorithm(DNSSEC.RSAMD5, new Entry("MD5withRSA", RSA));
addMnemonic("RSAMD5", DNSSEC.RSAMD5);
addAlgorithm(DNSSEC.DH, new Entry("", DH));
addMnemonic("DH", DNSSEC.DH);
addAlgorithm(DNSSEC.DSA, new Entry("SHA1withDSA", DSA));
addMnemonic("DSA", DNSSEC.DSA);
addAlgorithm(DNSSEC.RSASHA1, new Entry("SHA1withRSA", RSA));
addMnemonic("RSASHA1", DNSSEC.RSASHA1);
addMnemonic("RSA", DNSSEC.RSASHA1);
}
private void addAlgorithm(int algorithm, Entry entry)
{
Integer a = new Integer(algorithm);
mAlgorithmMap.put(a, entry);
}
private void addMnemonic(String m, int alg)
{
Integer a = new Integer(alg);
mMnemonicToIdMap.put(m.toUpperCase(), a);
if (! mIdToMnemonicMap.containsKey(a))
{
mIdToMnemonicMap.put(a, m);
}
}
public void addAlias(int alias, String mnemonic, int original_algorithm)
{
Integer a = new Integer(alias);
Integer o = new Integer(original_algorithm);
if (mAlgorithmMap.containsKey(a))
{
log.warning("Unable to alias algorithm " + alias
+ " because it already exists.");
return;
}
if (!mAlgorithmMap.containsKey(o))
{
log.warning("Unable to alias algorith " + alias
+ " to unknown algorithm identifier " + original_algorithm);
return;
}
mAlgorithmMap.put(a, mAlgorithmMap.get(o));
if (mnemonic != null)
{
addMnemonic(mnemonic, alias);
}
}
private Entry getEntry(int alg)
{
return (Entry) mAlgorithmMap.get(new Integer(alg));
}
public Signature getSignature(int algorithm)
{
Entry entry = getEntry(algorithm);
if (entry == null) return null;
Signature s = null;
try
{
s = Signature.getInstance(entry.sigName);
}
catch (NoSuchAlgorithmException e)
{
log.severe("Unable to get signature implementation for algorithm "
+ algorithm + ": " + e);
}
return s;
}
public int stringToAlgorithm(String s)
{
Integer alg = (Integer) mMnemonicToIdMap.get(s.toUpperCase());
if (alg != null) return alg.intValue();
return -1;
}
public String algToString(int algorithm)
{
return (String) mIdToMnemonicMap.get(new Integer(algorithm));
}
public int baseType(int algorithm)
{
Entry entry = getEntry(algorithm);
if (entry != null) return entry.baseType;
return UNKNOWN;
}
public int standardAlgorithm(int algorithm)
{
switch (baseType(algorithm))
{
case RSA :
return DNSSEC.RSASHA1;
case DSA :
return DNSSEC.DSA;
case DH :
return DNSSEC.DH;
default :
return UNKNOWN;
}
}
public boolean isDSA(int algorithm)
{
return (baseType(algorithm) == DSA);
}
public KeyPair generateKeyPair(int algorithm, int keysize)
throws NoSuchAlgorithmException
{
KeyPair pair = null;
switch (baseType(algorithm))
{
case RSA :
if (mRSAKeyGenerator == null)
{
mRSAKeyGenerator = KeyPairGenerator.getInstance("RSA");
}
mRSAKeyGenerator.initialize(keysize);
pair = mRSAKeyGenerator.generateKeyPair();
break;
case DSA :
if (mDSAKeyGenerator == null)
{
mDSAKeyGenerator = KeyPairGenerator.getInstance("DSA");
}
mDSAKeyGenerator.initialize(keysize);
pair = mDSAKeyGenerator.generateKeyPair();
break;
default :
throw new NoSuchAlgorithmException("Alg " + algorithm);
}
return pair;
}
public static DnsKeyAlgorithm getInstance()
{
if (mInstance == null) mInstance = new DnsKeyAlgorithm();
return mInstance;
}
}

View File

@@ -44,7 +44,6 @@ import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPrivateKeySpec;
import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.KEYRecord;
import org.xbill.DNS.Name;
import org.xbill.DNS.security.KEYConverter;
@@ -68,11 +67,35 @@ public class DnsKeyConverter
{
}
/** Given a DNS KEY record, return the JCA public key */
/**
* Given a DNS KEY record, return the JCA public key
*
* @throws NoSuchAlgorithmException
*/
public PublicKey parseDNSKEYRecord(DNSKEYRecord pKeyRecord)
throws NoSuchAlgorithmException
{
if (pKeyRecord.getKey() == null) return null;
// FIXME: this won't work at all with alg aliases.
// For now, instead of re-implementing parseRecord (or adding this stuff
// to DNSjava), we will just translate the algorithm back to a standard
// algorithm. Note that this will unnecessarily convert RSAMD5 to RSASHA1.
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
int standard_alg = algs.standardAlgorithm(pKeyRecord.getAlgorithm());
if (standard_alg <= 0)
throw new NoSuchAlgorithmException("DNSKEY algorithm "
+ pKeyRecord.getAlgorithm() + " is unrecognized");
if (pKeyRecord.getAlgorithm() != standard_alg)
{
pKeyRecord = new DNSKEYRecord(pKeyRecord.getName(), pKeyRecord
.getDClass(), pKeyRecord.getTTL(), pKeyRecord.getFlags(),
pKeyRecord.getProtocol(), standard_alg, pKeyRecord.getKey());
}
return KEYConverter.parseRecord(pKeyRecord);
}
@@ -104,15 +127,15 @@ public class DnsKeyConverter
public PrivateKey convertEncodedPrivateKey(byte[] key, int algorithm)
{
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
try
{
switch (algorithm)
switch (algs.baseType(algorithm))
{
case DNSSEC.RSAMD5 :
case DNSSEC.RSASHA1 :
case DnsKeyAlgorithm.RSA :
return mRSAKeyFactory.generatePrivate(spec);
case DNSSEC.DSA :
case DnsKeyAlgorithm.DSA :
return mDSAKeyFactory.generatePrivate(spec);
}
}
@@ -122,11 +145,23 @@ public class DnsKeyConverter
return null;
}
private int parseInt(String s, int def)
{
try
{
return Integer.parseInt(s);
}
catch (NumberFormatException e)
{
return def;
}
}
/**
* @return a JCA private key, given a BIND9-style textual encoding
*/
public PrivateKey parsePrivateKeyString(String key) throws IOException,
NoSuchAlgorithmException
public PrivateKey parsePrivateKeyString(String key)
throws IOException, NoSuchAlgorithmException
{
StringTokenizer lines = new StringTokenizer(key, "\n");
@@ -149,11 +184,24 @@ public class DnsKeyConverter
}
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);
// here we assume that the value looks like # (MNEM) or just the
// number.
String[] toks = val.split("\\s", 2);
val = toks[0];
int alg = parseInt(val, -1);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
switch (algs.baseType(alg))
{
case DnsKeyAlgorithm.RSA :
return parsePrivateRSA(lines);
case DnsKeyAlgorithm.DSA :
return parsePrivateDSA(lines);
case DnsKeyAlgorithm.DH :
return parsePrivateDH(lines);
default :
throw new IOException("unsupported private key algorithm: " + val);
}
}
}
return null;
@@ -390,11 +438,11 @@ public class DnsKeyConverter
}
else if (priv instanceof DSAPrivateKey && pub instanceof DSAPublicKey)
{
return generatePrivateDSA((DSAPrivateKey) priv, (DSAPublicKey) pub);
return generatePrivateDSA((DSAPrivateKey) priv, (DSAPublicKey) pub, alg);
}
else if (priv instanceof DHPrivateKey && pub instanceof DHPublicKey)
{
return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub);
return generatePrivateDH((DHPrivateKey) priv, (DHPublicKey) pub, alg);
}
return null;
@@ -426,16 +474,11 @@ public class DnsKeyConverter
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
out.println("Private-key-format: v1.2");
if (algorithm == DNSSEC.RSAMD5)
{
out.println("Algorithm: 1 (RSAMD5)");
}
else
{
out.println("Algorithm: 5 (RSASHA1)");
}
out.println("Algorithm: " + algorithm + " ("
+ algs.algToString(algorithm) + ")");
out.print("Modulus: ");
out.println(b64BigInt(key.getModulus()));
out.print("PublicExponent: ");
@@ -457,15 +500,18 @@ public class DnsKeyConverter
}
/** Given a DH key pair, return the BIND9-style text encoding */
private String generatePrivateDH(DHPrivateKey key, DHPublicKey pub)
private String generatePrivateDH(DHPrivateKey key, DHPublicKey pub,
int algorithm)
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
DHParameterSpec p = key.getParams();
out.println("Private-key-format: v1.2");
out.println("Algorithm: 2 (DH)");
out.println("Algorithm: " + algorithm + " ("
+ algs.algToString(algorithm) + ")");
out.print("Prime(p): ");
out.println(b64BigInt(p.getP()));
out.print("Generator(g): ");
@@ -479,15 +525,18 @@ public class DnsKeyConverter
}
/** Given a DSA key pair, return the BIND9-style text encoding */
private String generatePrivateDSA(DSAPrivateKey key, DSAPublicKey pub)
private String generatePrivateDSA(DSAPrivateKey key, DSAPublicKey pub,
int algorithm)
{
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
DSAParams p = key.getParams();
out.println("Private-key-format: v1.2");
out.println("Algorithm: 3 (DSA)");
out.println("Algorithm: " + algorithm + " ("
+ algs.algToString(algorithm) + ")");
out.print("Prime(p): ");
out.println(b64BigInt(p.getP()));
out.print("Subprime(q): ");

View File

@@ -106,7 +106,7 @@ public class DnsKeyPair
setDNSKEYRecord(keyRecord);
setPrivateKeyString(null);
}
public DnsKeyPair(Name keyName, int algorithm, PublicKey publicKey,
PrivateKey privateKey)
{
@@ -146,35 +146,8 @@ public class DnsKeyPair
/** @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.severe("error getting Signature object: " + e);
}
return s;
DnsKeyAlgorithm algorithms = DnsKeyAlgorithm.getInstance();
return algorithms.getSignature(getDNSKEYAlgorithm());
}
/**
@@ -184,8 +157,16 @@ public class DnsKeyPair
{
if (mPublicKey == null && getDNSKEYRecord() != null)
{
DnsKeyConverter conv = getKeyConverter();
setPublic(conv.parseDNSKEYRecord(getDNSKEYRecord()));
try
{
DnsKeyConverter conv = getKeyConverter();
setPublic(conv.parseDNSKEYRecord(getDNSKEYRecord()));
}
catch (NoSuchAlgorithmException e)
{
log.severe(e.toString());
return null;
}
}
return mPublicKey;
@@ -220,6 +201,7 @@ public class DnsKeyPair
/**
* @return the opaque private key string, null if one doesn't exist.
* @throws NoSuchAlgorithmException
*/
public String getPrivateKeyString()
{
@@ -304,6 +286,7 @@ public class DnsKeyPair
/**
* @return a Signature object initialized for verifying, or null if this key
* pair does not have a valid public key.
* @throws NoSuchAlgorithmException
*/
public Signature getVerifier()
{

View File

@@ -263,14 +263,18 @@ public class DnsSecVerifier implements Verifier
{
byte[] data = SignUtils.generateSigData(rrset, sigrec);
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
Signature signer = keypair.getVerifier();
signer.update(data);
byte[] sig = sigrec.getSignature();
if (sigrec.getAlgorithm() == DNSSEC.DSA)
if (algs.baseType(sigrec.getAlgorithm()) == DnsKeyAlgorithm.DSA)
{
sig = SignUtils.convertDSASignature(sig);
}
if (!signer.verify(sig))
{
log.info("Signature failed to verify cryptographically");

View File

@@ -22,7 +22,6 @@ package com.verisignlabs.dnssec.security;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.interfaces.DSAPublicKey;
@@ -34,7 +33,6 @@ import java.util.List;
import java.util.ListIterator;
import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.Name;
import org.xbill.DNS.RRSIGRecord;
import org.xbill.DNS.RRset;
@@ -54,10 +52,7 @@ import org.xbill.DNS.Type;
*/
public class JCEDnsSecSigner
{
private DnsKeyConverter mKeyConverter;
private KeyPairGenerator mRSAKeyGenerator;
private KeyPairGenerator mDSAKeyGenerator;
private DnsKeyConverter mKeyConverter;
/**
* Cryptographically generate a new DNSSEC key.
@@ -73,32 +68,11 @@ public class JCEDnsSecSigner
public DnsKeyPair generateKey(Name owner, long ttl, int dclass,
int algorithm, int flags, int keysize) throws NoSuchAlgorithmException
{
KeyPair pair;
DnsKeyAlgorithm algorithms = DnsKeyAlgorithm.getInstance();
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);
}
KeyPair pair = algorithms.generateKeyPair(algorithm, keysize);
if (mKeyConverter == null)
{
@@ -111,6 +85,7 @@ public class JCEDnsSecSigner
flags,
algorithm,
pair.getPublic());
DnsKeyPair dnspair = new DnsKeyPair();
dnspair.setDNSKEYRecord(keyrec);
dnspair.setPublic(pair.getPublic()); // keep from conv. the keyrec back.
@@ -173,11 +148,12 @@ public class JCEDnsSecSigner
signer.update(sign_data);
byte[] sig = signer.sign();
DnsKeyAlgorithm algs = DnsKeyAlgorithm.getInstance();
// Convert to RFC 2536 format, if necessary.
if (pair.getDNSKEYAlgorithm() == DNSSEC.DSA)
if (algs.baseType(pair.getDNSKEYAlgorithm()) == DnsKeyAlgorithm.DSA)
{
sig = SignUtils.convertDSASignature(((DSAPublicKey) pair.getPublic()).getParams(),
sig);
sig = SignUtils.convertDSASignature(((DSAPublicKey) pair.getPublic())
.getParams(), sig);
}
RRSIGRecord sigrec = SignUtils.generateRRSIG(sig, presig);
sigs.add(sigrec);
@@ -234,8 +210,8 @@ public class JCEDnsSecSigner
*/
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
boolean fullySignKeyset, Name last_cut)
throws IOException, GeneralSecurityException
{
// add the records themselves
for (Iterator i = rrset.rrs(); i.hasNext();)
@@ -243,10 +219,8 @@ public class JCEDnsSecSigner
toList.add(i.next());
}
int type = SignUtils.recordSecType(zonename,
rrset.getName(),
rrset.getType(),
last_cut);
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.

View File

@@ -157,13 +157,6 @@ public class SignUtils
// 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);