374 lines
12 KiB
Java
374 lines
12 KiB
Java
|
// $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;
|
||
|
}
|
||
|
}
|