001: // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
002:
003: package org.xbill.DNS.security;
004:
005: import java.util.*;
006: import java.security.*;
007: import org.xbill.DNS.*;
008: import org.xbill.DNS.utils.*;
009:
010: /**
011: * A class that verifies DNS data using digital signatures contained in DNSSEC
012: * SIG records. DNSSECVerifier stores a set of trusted keys. Each specific
013: * verification references a cache where additional secure keys may be found.
014: * @see Verifier
015: * @see DNSSEC
016: *
017: * @author Brian Wellington
018: */
019:
020: public class DNSSECVerifier implements Verifier {
021:
022: private Map trustedKeys;
023:
024: /** Creates a new DNSSECVerifier */
025: public DNSSECVerifier() {
026: trustedKeys = new HashMap();
027: }
028:
029: /** Adds the specified key to the set of trusted keys */
030: public synchronized void addTrustedKey(DNSKEYRecord key) {
031: Name name = key.getName();
032: List list = (List) trustedKeys.get(name);
033: if (list == null)
034: trustedKeys.put(name, list = new LinkedList());
035: list.add(key);
036: }
037:
038: /** Adds the specified key to the set of trusted keys */
039: public void addTrustedKey(Name name, int alg, PublicKey key) {
040: Record rec;
041: rec = KEYConverter.buildRecord(name, Type.DNSKEY, DClass.IN, 0,
042: 0, DNSKEYRecord.Protocol.DNSSEC, alg, key);
043: if (rec != null)
044: addTrustedKey((DNSKEYRecord) rec);
045: }
046:
047: private PublicKey findMatchingKey(Iterator it, int algorithm,
048: int footprint) {
049: while (it.hasNext()) {
050: DNSKEYRecord keyrec = (DNSKEYRecord) it.next();
051: if (keyrec.getAlgorithm() == algorithm
052: && keyrec.getFootprint() == footprint)
053: return KEYConverter.parseRecord(keyrec);
054: }
055: return null;
056: }
057:
058: private synchronized PublicKey findTrustedKey(Name name,
059: int algorithm, int footprint) {
060: List list = (List) trustedKeys.get(name);
061: if (list == null)
062: return null;
063: return findMatchingKey(list.iterator(), algorithm, footprint);
064: }
065:
066: private PublicKey findCachedKey(Cache cache, Name name,
067: int algorithm, int footprint) {
068: RRset[] keysets = cache.findAnyRecords(name, Type.DNSKEY);
069: if (keysets == null)
070: return null;
071: RRset keys = keysets[0];
072: return findMatchingKey(keys.rrs(), algorithm, footprint);
073: }
074:
075: private PublicKey findKey(Cache cache, Name name, int algorithm,
076: int footprint) {
077: PublicKey key = findTrustedKey(name, algorithm, footprint);
078: if (key == null && cache != null)
079: return findCachedKey(cache, name, algorithm, footprint);
080: return key;
081: }
082:
083: private int verifySIG(RRset set, RRSIGRecord sigrec, Cache cache) {
084: PublicKey key = findKey(cache, sigrec.getSigner(), sigrec
085: .getAlgorithm(), sigrec.getFootprint());
086: if (key == null)
087: return DNSSEC.Insecure;
088:
089: Date now = new Date();
090: if (now.compareTo(sigrec.getExpire()) > 0
091: || now.compareTo(sigrec.getTimeSigned()) < 0) {
092: System.err.println("Outside of validity period");
093: return DNSSEC.Failed;
094: }
095: byte[] data = DNSSEC.digestRRset(sigrec, set);
096:
097: byte[] sig;
098: String algString;
099:
100: switch (sigrec.getAlgorithm()) {
101: case DNSSEC.RSAMD5:
102: sig = sigrec.getSignature();
103: algString = "MD5withRSA";
104: break;
105: case DNSSEC.DSA:
106: sig = DSASignature.fromDNS(sigrec.getSignature());
107: algString = "SHA1withDSA";
108: break;
109: case DNSSEC.RSASHA1:
110: sig = sigrec.getSignature();
111: algString = "SHA1withRSA";
112: break;
113: default:
114: return DNSSEC.Failed;
115: }
116:
117: try {
118: Signature s = Signature.getInstance(algString);
119: s.initVerify(key);
120: s.update(data);
121: return s.verify(sig) ? DNSSEC.Secure : DNSSEC.Failed;
122: } catch (GeneralSecurityException e) {
123: if (Options.check("verboseexceptions"))
124: System.err.println("Signing data: " + e);
125: return DNSSEC.Failed;
126: }
127: }
128:
129: /**
130: * Attempts to verify an RRset. This does not modify the set.
131: * @param set The RRset to verify
132: * @param cache The Cache where obtained secure keys are found (may be null)
133: * @return The new security status of the set
134: * @see RRset
135: */
136: public int verify(RRset set, Cache cache) {
137: Iterator sigs = set.sigs();
138: if (Options.check("verbosesec"))
139: System.out.print("Verifying " + set.getName() + "/"
140: + Type.string(set.getType()) + ": ");
141: if (!sigs.hasNext()) {
142: if (Options.check("verbosesec"))
143: System.out.println("Insecure");
144: return DNSSEC.Insecure;
145: }
146: while (sigs.hasNext()) {
147: RRSIGRecord sigrec = (RRSIGRecord) sigs.next();
148: if (verifySIG(set, sigrec, cache) == DNSSEC.Secure) {
149: if (Options.check("verbosesec"))
150: System.out.println("Secure");
151: return DNSSEC.Secure;
152: }
153: }
154: if (Options.check("verbosesec"))
155: System.out.println("Failed");
156: return DNSSEC.Failed;
157: }
158:
159: }
|