aboutsummaryrefslogtreecommitdiff
path: root/src/org/xbill/DNS/DNSSEC.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/xbill/DNS/DNSSEC.java')
-rw-r--r--src/org/xbill/DNS/DNSSEC.java1026
1 files changed, 1026 insertions, 0 deletions
diff --git a/src/org/xbill/DNS/DNSSEC.java b/src/org/xbill/DNS/DNSSEC.java
new file mode 100644
index 0000000..2707947
--- /dev/null
+++ b/src/org/xbill/DNS/DNSSEC.java
@@ -0,0 +1,1026 @@
+// Copyright (c) 1999-2010 Brian Wellington (bwelling@xbill.org)
+
+package org.xbill.DNS;
+
+import java.io.*;
+import java.math.*;
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+import java.util.*;
+
+/**
+ * Constants and methods relating to DNSSEC.
+ *
+ * DNSSEC provides authentication for DNS information.
+ * @see RRSIGRecord
+ * @see DNSKEYRecord
+ * @see RRset
+ *
+ * @author Brian Wellington
+ */
+
+public class DNSSEC {
+
+public static class Algorithm {
+ private Algorithm() {}
+
+ /** RSA/MD5 public key (deprecated) */
+ public static final int RSAMD5 = 1;
+
+ /** Diffie Hellman key */
+ public static final int DH = 2;
+
+ /** DSA public key */
+ public static final int DSA = 3;
+
+ /** RSA/SHA1 public key */
+ public static final int RSASHA1 = 5;
+
+ /** DSA/SHA1, NSEC3-aware public key */
+ public static final int DSA_NSEC3_SHA1 = 6;
+
+ /** RSA/SHA1, NSEC3-aware public key */
+ public static final int RSA_NSEC3_SHA1 = 7;
+
+ /** RSA/SHA256 public key */
+ public static final int RSASHA256 = 8;
+
+ /** RSA/SHA512 public key */
+ public static final int RSASHA512 = 10;
+
+ /** ECDSA Curve P-256 with SHA-256 public key **/
+ public static final int ECDSAP256SHA256 = 13;
+
+ /** ECDSA Curve P-384 with SHA-384 public key **/
+ public static final int ECDSAP384SHA384 = 14;
+
+ /** Indirect keys; the actual key is elsewhere. */
+ public static final int INDIRECT = 252;
+
+ /** Private algorithm, specified by domain name */
+ public static final int PRIVATEDNS = 253;
+
+ /** Private algorithm, specified by OID */
+ public static final int PRIVATEOID = 254;
+
+ private static Mnemonic algs = new Mnemonic("DNSSEC algorithm",
+ Mnemonic.CASE_UPPER);
+
+ static {
+ algs.setMaximum(0xFF);
+ algs.setNumericAllowed(true);
+
+ algs.add(RSAMD5, "RSAMD5");
+ algs.add(DH, "DH");
+ algs.add(DSA, "DSA");
+ algs.add(RSASHA1, "RSASHA1");
+ algs.add(DSA_NSEC3_SHA1, "DSA-NSEC3-SHA1");
+ algs.add(RSA_NSEC3_SHA1, "RSA-NSEC3-SHA1");
+ algs.add(RSASHA256, "RSASHA256");
+ algs.add(RSASHA512, "RSASHA512");
+ algs.add(ECDSAP256SHA256, "ECDSAP256SHA256");
+ algs.add(ECDSAP384SHA384, "ECDSAP384SHA384");
+ algs.add(INDIRECT, "INDIRECT");
+ algs.add(PRIVATEDNS, "PRIVATEDNS");
+ algs.add(PRIVATEOID, "PRIVATEOID");
+ }
+
+ /**
+ * Converts an algorithm into its textual representation
+ */
+ public static String
+ string(int alg) {
+ return algs.getText(alg);
+ }
+
+ /**
+ * Converts a textual representation of an algorithm into its numeric
+ * code. Integers in the range 0..255 are also accepted.
+ * @param s The textual representation of the algorithm
+ * @return The algorithm code, or -1 on error.
+ */
+ public static int
+ value(String s) {
+ return algs.getValue(s);
+ }
+}
+
+private
+DNSSEC() { }
+
+private static void
+digestSIG(DNSOutput out, SIGBase sig) {
+ out.writeU16(sig.getTypeCovered());
+ out.writeU8(sig.getAlgorithm());
+ out.writeU8(sig.getLabels());
+ out.writeU32(sig.getOrigTTL());
+ out.writeU32(sig.getExpire().getTime() / 1000);
+ out.writeU32(sig.getTimeSigned().getTime() / 1000);
+ out.writeU16(sig.getFootprint());
+ sig.getSigner().toWireCanonical(out);
+}
+
+/**
+ * Creates a byte array containing the concatenation of the fields of the
+ * SIG record and the RRsets to be signed/verified. This does not perform
+ * a cryptographic digest.
+ * @param rrsig The RRSIG record used to sign/verify the rrset.
+ * @param rrset The data to be signed/verified.
+ * @return The data to be cryptographically signed or verified.
+ */
+public static byte []
+digestRRset(RRSIGRecord rrsig, RRset rrset) {
+ DNSOutput out = new DNSOutput();
+ digestSIG(out, rrsig);
+
+ int size = rrset.size();
+ Record [] records = new Record[size];
+
+ Iterator it = rrset.rrs();
+ Name name = rrset.getName();
+ Name wild = null;
+ int sigLabels = rrsig.getLabels() + 1; // Add the root label back.
+ if (name.labels() > sigLabels)
+ wild = name.wild(name.labels() - sigLabels);
+ while (it.hasNext())
+ records[--size] = (Record) it.next();
+ Arrays.sort(records);
+
+ DNSOutput header = new DNSOutput();
+ if (wild != null)
+ wild.toWireCanonical(header);
+ else
+ name.toWireCanonical(header);
+ header.writeU16(rrset.getType());
+ header.writeU16(rrset.getDClass());
+ header.writeU32(rrsig.getOrigTTL());
+ for (int i = 0; i < records.length; i++) {
+ out.writeByteArray(header.toByteArray());
+ int lengthPosition = out.current();
+ out.writeU16(0);
+ out.writeByteArray(records[i].rdataToWireCanonical());
+ int rrlength = out.current() - lengthPosition - 2;
+ out.save();
+ out.jump(lengthPosition);
+ out.writeU16(rrlength);
+ out.restore();
+ }
+ return out.toByteArray();
+}
+
+/**
+ * Creates a byte array containing the concatenation of the fields of the
+ * SIG(0) record and the message to be signed. This does not perform
+ * a cryptographic digest.
+ * @param sig The SIG record used to sign the rrset.
+ * @param msg The message to be signed.
+ * @param previous If this is a response, the signature from the query.
+ * @return The data to be cryptographically signed.
+ */
+public static byte []
+digestMessage(SIGRecord sig, Message msg, byte [] previous) {
+ DNSOutput out = new DNSOutput();
+ digestSIG(out, sig);
+
+ if (previous != null)
+ out.writeByteArray(previous);
+
+ msg.toWire(out);
+ return out.toByteArray();
+}
+
+/**
+ * A DNSSEC exception.
+ */
+public static class DNSSECException extends Exception {
+ DNSSECException(String s) {
+ super(s);
+ }
+}
+
+/**
+ * An algorithm is unsupported by this DNSSEC implementation.
+ */
+public static class UnsupportedAlgorithmException extends DNSSECException {
+ UnsupportedAlgorithmException(int alg) {
+ super("Unsupported algorithm: " + alg);
+ }
+}
+
+/**
+ * The cryptographic data in a DNSSEC key is malformed.
+ */
+public static class MalformedKeyException extends DNSSECException {
+ MalformedKeyException(KEYBase rec) {
+ super("Invalid key data: " + rec.rdataToString());
+ }
+}
+
+/**
+ * A DNSSEC verification failed because fields in the DNSKEY and RRSIG records
+ * do not match.
+ */
+public static class KeyMismatchException extends DNSSECException {
+ private KEYBase key;
+ private SIGBase sig;
+
+ KeyMismatchException(KEYBase key, SIGBase sig) {
+ super("key " +
+ key.getName() + "/" +
+ DNSSEC.Algorithm.string(key.getAlgorithm()) + "/" +
+ key.getFootprint() + " " +
+ "does not match signature " +
+ sig.getSigner() + "/" +
+ DNSSEC.Algorithm.string(sig.getAlgorithm()) + "/" +
+ sig.getFootprint());
+ }
+}
+
+/**
+ * A DNSSEC verification failed because the signature has expired.
+ */
+public static class SignatureExpiredException extends DNSSECException {
+ private Date when, now;
+
+ SignatureExpiredException(Date when, Date now) {
+ super("signature expired");
+ this.when = when;
+ this.now = now;
+ }
+
+ /**
+ * @return When the signature expired
+ */
+ public Date
+ getExpiration() {
+ return when;
+ }
+
+ /**
+ * @return When the verification was attempted
+ */
+ public Date
+ getVerifyTime() {
+ return now;
+ }
+}
+
+/**
+ * A DNSSEC verification failed because the signature has not yet become valid.
+ */
+public static class SignatureNotYetValidException extends DNSSECException {
+ private Date when, now;
+
+ SignatureNotYetValidException(Date when, Date now) {
+ super("signature is not yet valid");
+ this.when = when;
+ this.now = now;
+ }
+
+ /**
+ * @return When the signature will become valid
+ */
+ public Date
+ getExpiration() {
+ return when;
+ }
+
+ /**
+ * @return When the verification was attempted
+ */
+ public Date
+ getVerifyTime() {
+ return now;
+ }
+}
+
+/**
+ * A DNSSEC verification failed because the cryptographic signature
+ * verification failed.
+ */
+public static class SignatureVerificationException extends DNSSECException {
+ SignatureVerificationException() {
+ super("signature verification failed");
+ }
+}
+
+/**
+ * The key data provided is inconsistent.
+ */
+public static class IncompatibleKeyException extends IllegalArgumentException {
+ IncompatibleKeyException() {
+ super("incompatible keys");
+ }
+}
+
+private static int
+BigIntegerLength(BigInteger i) {
+ return (i.bitLength() + 7) / 8;
+}
+
+private static BigInteger
+readBigInteger(DNSInput in, int len) throws IOException {
+ byte [] b = in.readByteArray(len);
+ return new BigInteger(1, b);
+}
+
+private static BigInteger
+readBigInteger(DNSInput in) {
+ byte [] b = in.readByteArray();
+ return new BigInteger(1, b);
+}
+
+private static void
+writeBigInteger(DNSOutput out, BigInteger val) {
+ byte [] b = val.toByteArray();
+ if (b[0] == 0)
+ out.writeByteArray(b, 1, b.length - 1);
+ else
+ out.writeByteArray(b);
+}
+
+private static PublicKey
+toRSAPublicKey(KEYBase r) throws IOException, GeneralSecurityException {
+ DNSInput in = new DNSInput(r.getKey());
+ int exponentLength = in.readU8();
+ if (exponentLength == 0)
+ exponentLength = in.readU16();
+ BigInteger exponent = readBigInteger(in, exponentLength);
+ BigInteger modulus = readBigInteger(in);
+
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+ return factory.generatePublic(new RSAPublicKeySpec(modulus, exponent));
+}
+
+private static PublicKey
+toDSAPublicKey(KEYBase r) throws IOException, GeneralSecurityException,
+ MalformedKeyException
+{
+ DNSInput in = new DNSInput(r.getKey());
+
+ int t = in.readU8();
+ if (t > 8)
+ throw new MalformedKeyException(r);
+
+ BigInteger q = readBigInteger(in, 20);
+ BigInteger p = readBigInteger(in, 64 + t*8);
+ BigInteger g = readBigInteger(in, 64 + t*8);
+ BigInteger y = readBigInteger(in, 64 + t*8);
+
+ KeyFactory factory = KeyFactory.getInstance("DSA");
+ return factory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+}
+
+private static class ECKeyInfo {
+ int length;
+ public BigInteger p, a, b, gx, gy, n;
+ EllipticCurve curve;
+ ECParameterSpec spec;
+
+ ECKeyInfo(int length, String p_str, String a_str, String b_str,
+ String gx_str, String gy_str, String n_str)
+ {
+ this.length = length;
+ p = new BigInteger(p_str, 16);
+ a = new BigInteger(a_str, 16);
+ b = new BigInteger(b_str, 16);
+ gx = new BigInteger(gx_str, 16);
+ gy = new BigInteger(gy_str, 16);
+ n = new BigInteger(n_str, 16);
+ curve = new EllipticCurve(new ECFieldFp(p), a, b);
+ spec = new ECParameterSpec(curve, new ECPoint(gx, gy), n, 1);
+ }
+}
+
+// RFC 5114 Section 2.6
+private static final ECKeyInfo ECDSA_P256 = new ECKeyInfo(32,
+ "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+ "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
+ "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+ "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+ "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
+
+// RFC 5114 Section 2.7
+private static final ECKeyInfo ECDSA_P384 = new ECKeyInfo(48,
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
+ "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
+ "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
+
+private static PublicKey
+toECDSAPublicKey(KEYBase r, ECKeyInfo keyinfo) throws IOException,
+ GeneralSecurityException, MalformedKeyException
+{
+ DNSInput in = new DNSInput(r.getKey());
+
+ // RFC 6605 Section 4
+ BigInteger x = readBigInteger(in, keyinfo.length);
+ BigInteger y = readBigInteger(in, keyinfo.length);
+ ECPoint q = new ECPoint(x, y);
+
+ KeyFactory factory = KeyFactory.getInstance("EC");
+ return factory.generatePublic(new ECPublicKeySpec(q, keyinfo.spec));
+}
+
+/** Converts a KEY/DNSKEY record into a PublicKey */
+static PublicKey
+toPublicKey(KEYBase r) throws DNSSECException {
+ int alg = r.getAlgorithm();
+ try {
+ switch (alg) {
+ case Algorithm.RSAMD5:
+ case Algorithm.RSASHA1:
+ case Algorithm.RSA_NSEC3_SHA1:
+ case Algorithm.RSASHA256:
+ case Algorithm.RSASHA512:
+ return toRSAPublicKey(r);
+ case Algorithm.DSA:
+ case Algorithm.DSA_NSEC3_SHA1:
+ return toDSAPublicKey(r);
+ case Algorithm.ECDSAP256SHA256:
+ return toECDSAPublicKey(r, ECDSA_P256);
+ case Algorithm.ECDSAP384SHA384:
+ return toECDSAPublicKey(r, ECDSA_P384);
+ default:
+ throw new UnsupportedAlgorithmException(alg);
+ }
+ }
+ catch (IOException e) {
+ throw new MalformedKeyException(r);
+ }
+ catch (GeneralSecurityException e) {
+ throw new DNSSECException(e.toString());
+ }
+}
+
+private static byte []
+fromRSAPublicKey(RSAPublicKey key) {
+ DNSOutput out = new DNSOutput();
+ BigInteger exponent = key.getPublicExponent();
+ BigInteger modulus = key.getModulus();
+ int exponentLength = BigIntegerLength(exponent);
+
+ if (exponentLength < 256)
+ out.writeU8(exponentLength);
+ else {
+ out.writeU8(0);
+ out.writeU16(exponentLength);
+ }
+ writeBigInteger(out, exponent);
+ writeBigInteger(out, modulus);
+
+ return out.toByteArray();
+}
+
+private static byte []
+fromDSAPublicKey(DSAPublicKey key) {
+ DNSOutput out = new DNSOutput();
+ BigInteger q = key.getParams().getQ();
+ BigInteger p = key.getParams().getP();
+ BigInteger g = key.getParams().getG();
+ BigInteger y = key.getY();
+ int t = (p.toByteArray().length - 64) / 8;
+
+ out.writeU8(t);
+ writeBigInteger(out, q);
+ writeBigInteger(out, p);
+ writeBigInteger(out, g);
+ writeBigInteger(out, y);
+
+ return out.toByteArray();
+}
+
+private static byte []
+fromECDSAPublicKey(ECPublicKey key) {
+ DNSOutput out = new DNSOutput();
+
+ BigInteger x = key.getW().getAffineX();
+ BigInteger y = key.getW().getAffineY();
+
+ writeBigInteger(out, x);
+ writeBigInteger(out, y);
+
+ return out.toByteArray();
+}
+
+/** Builds a DNSKEY record from a PublicKey */
+static byte []
+fromPublicKey(PublicKey key, int alg) throws DNSSECException
+{
+
+ switch (alg) {
+ case Algorithm.RSAMD5:
+ case Algorithm.RSASHA1:
+ case Algorithm.RSA_NSEC3_SHA1:
+ case Algorithm.RSASHA256:
+ case Algorithm.RSASHA512:
+ if (! (key instanceof RSAPublicKey))
+ throw new IncompatibleKeyException();
+ return fromRSAPublicKey((RSAPublicKey) key);
+ case Algorithm.DSA:
+ case Algorithm.DSA_NSEC3_SHA1:
+ if (! (key instanceof DSAPublicKey))
+ throw new IncompatibleKeyException();
+ return fromDSAPublicKey((DSAPublicKey) key);
+ case Algorithm.ECDSAP256SHA256:
+ case Algorithm.ECDSAP384SHA384:
+ if (! (key instanceof ECPublicKey))
+ throw new IncompatibleKeyException();
+ return fromECDSAPublicKey((ECPublicKey) key);
+ default:
+ throw new UnsupportedAlgorithmException(alg);
+ }
+}
+
+/**
+ * Convert an algorithm number to the corresponding JCA string.
+ * @param alg The algorithm number.
+ * @throws UnsupportedAlgorithmException The algorithm is unknown.
+ */
+public static String
+algString(int alg) throws UnsupportedAlgorithmException {
+ switch (alg) {
+ case Algorithm.RSAMD5:
+ return "MD5withRSA";
+ case Algorithm.DSA:
+ case Algorithm.DSA_NSEC3_SHA1:
+ return "SHA1withDSA";
+ case Algorithm.RSASHA1:
+ case Algorithm.RSA_NSEC3_SHA1:
+ return "SHA1withRSA";
+ case Algorithm.RSASHA256:
+ return "SHA256withRSA";
+ case Algorithm.RSASHA512:
+ return "SHA512withRSA";
+ case Algorithm.ECDSAP256SHA256:
+ return "SHA256withECDSA";
+ case Algorithm.ECDSAP384SHA384:
+ return "SHA384withECDSA";
+ default:
+ throw new UnsupportedAlgorithmException(alg);
+ }
+}
+
+private static final int ASN1_SEQ = 0x30;
+private static final int ASN1_INT = 0x2;
+
+private static final int DSA_LEN = 20;
+
+private static byte []
+DSASignaturefromDNS(byte [] dns) throws DNSSECException, IOException {
+ if (dns.length != 1 + DSA_LEN * 2)
+ throw new SignatureVerificationException();
+
+ DNSInput in = new DNSInput(dns);
+ DNSOutput out = new DNSOutput();
+
+ int t = in.readU8();
+
+ byte [] r = in.readByteArray(DSA_LEN);
+ int rlen = DSA_LEN;
+ if (r[0] < 0)
+ rlen++;
+
+ byte [] s = in.readByteArray(DSA_LEN);
+ int slen = DSA_LEN;
+ if (s[0] < 0)
+ slen++;
+
+ out.writeU8(ASN1_SEQ);
+ out.writeU8(rlen + slen + 4);
+
+ out.writeU8(ASN1_INT);
+ out.writeU8(rlen);
+ if (rlen > DSA_LEN)
+ out.writeU8(0);
+ out.writeByteArray(r);
+
+ out.writeU8(ASN1_INT);
+ out.writeU8(slen);
+ if (slen > DSA_LEN)
+ out.writeU8(0);
+ out.writeByteArray(s);
+
+ return out.toByteArray();
+}
+
+private static byte []
+DSASignaturetoDNS(byte [] signature, int t) throws IOException {
+ DNSInput in = new DNSInput(signature);
+ DNSOutput out = new DNSOutput();
+
+ out.writeU8(t);
+
+ int tmp = in.readU8();
+ if (tmp != ASN1_SEQ)
+ throw new IOException();
+ int seqlen = in.readU8();
+
+ tmp = in.readU8();
+ if (tmp != ASN1_INT)
+ throw new IOException();
+ int rlen = in.readU8();
+ if (rlen == DSA_LEN + 1) {
+ if (in.readU8() != 0)
+ throw new IOException();
+ } else if (rlen != DSA_LEN)
+ throw new IOException();
+ byte [] bytes = in.readByteArray(DSA_LEN);
+ out.writeByteArray(bytes);
+
+ tmp = in.readU8();
+ if (tmp != ASN1_INT)
+ throw new IOException();
+ int slen = in.readU8();
+ if (slen == DSA_LEN + 1) {
+ if (in.readU8() != 0)
+ throw new IOException();
+ } else if (slen != DSA_LEN)
+ throw new IOException();
+ bytes = in.readByteArray(DSA_LEN);
+ out.writeByteArray(bytes);
+
+ return out.toByteArray();
+}
+
+private static byte []
+ECDSASignaturefromDNS(byte [] signature, ECKeyInfo keyinfo)
+ throws DNSSECException, IOException
+{
+ if (signature.length != keyinfo.length * 2)
+ throw new SignatureVerificationException();
+
+ DNSInput in = new DNSInput(signature);
+ DNSOutput out = new DNSOutput();
+
+ byte [] r = in.readByteArray(keyinfo.length);
+ int rlen = keyinfo.length;
+ if (r[0] < 0)
+ rlen++;
+
+ byte [] s = in.readByteArray(keyinfo.length);
+ int slen = keyinfo.length;
+ if (s[0] < 0)
+ slen++;
+
+ out.writeU8(ASN1_SEQ);
+ out.writeU8(rlen + slen + 4);
+
+ out.writeU8(ASN1_INT);
+ out.writeU8(rlen);
+ if (rlen > keyinfo.length)
+ out.writeU8(0);
+ out.writeByteArray(r);
+
+ out.writeU8(ASN1_INT);
+ out.writeU8(slen);
+ if (slen > keyinfo.length)
+ out.writeU8(0);
+ out.writeByteArray(s);
+
+ return out.toByteArray();
+}
+
+private static byte []
+ECDSASignaturetoDNS(byte [] signature, ECKeyInfo keyinfo) throws IOException {
+ DNSInput in = new DNSInput(signature);
+ DNSOutput out = new DNSOutput();
+
+ int tmp = in.readU8();
+ if (tmp != ASN1_SEQ)
+ throw new IOException();
+ int seqlen = in.readU8();
+
+ tmp = in.readU8();
+ if (tmp != ASN1_INT)
+ throw new IOException();
+ int rlen = in.readU8();
+ if (rlen == keyinfo.length + 1) {
+ if (in.readU8() != 0)
+ throw new IOException();
+ } else if (rlen != keyinfo.length)
+ throw new IOException();
+ byte[] bytes = in.readByteArray(keyinfo.length);
+ out.writeByteArray(bytes);
+
+ tmp = in.readU8();
+ if (tmp != ASN1_INT)
+ throw new IOException();
+ int slen = in.readU8();
+ if (slen == keyinfo.length + 1) {
+ if (in.readU8() != 0)
+ throw new IOException();
+ } else if (slen != keyinfo.length)
+ throw new IOException();
+ bytes = in.readByteArray(keyinfo.length);
+ out.writeByteArray(bytes);
+
+ return out.toByteArray();
+}
+
+private static void
+verify(PublicKey key, int alg, byte [] data, byte [] signature)
+throws DNSSECException
+{
+ if (key instanceof DSAPublicKey) {
+ try {
+ signature = DSASignaturefromDNS(signature);
+ }
+ catch (IOException e) {
+ throw new IllegalStateException();
+ }
+ } else if (key instanceof ECPublicKey) {
+ try {
+ switch (alg) {
+ case Algorithm.ECDSAP256SHA256:
+ signature = ECDSASignaturefromDNS(signature,
+ ECDSA_P256);
+ break;
+ case Algorithm.ECDSAP384SHA384:
+ signature = ECDSASignaturefromDNS(signature,
+ ECDSA_P384);
+ break;
+ default:
+ throw new UnsupportedAlgorithmException(alg);
+ }
+ }
+ catch (IOException e) {
+ throw new IllegalStateException();
+ }
+ }
+
+ try {
+ Signature s = Signature.getInstance(algString(alg));
+ s.initVerify(key);
+ s.update(data);
+ if (!s.verify(signature))
+ throw new SignatureVerificationException();
+ }
+ catch (GeneralSecurityException e) {
+ throw new DNSSECException(e.toString());
+ }
+}
+
+private static boolean
+matches(SIGBase sig, KEYBase key)
+{
+ return (key.getAlgorithm() == sig.getAlgorithm() &&
+ key.getFootprint() == sig.getFootprint() &&
+ key.getName().equals(sig.getSigner()));
+}
+
+/**
+ * Verify a DNSSEC signature.
+ * @param rrset The data to be verified.
+ * @param rrsig The RRSIG record containing the signature.
+ * @param key The DNSKEY record to verify the signature with.
+ * @throws UnsupportedAlgorithmException The algorithm is unknown
+ * @throws MalformedKeyException The key is malformed
+ * @throws KeyMismatchException The key and signature do not match
+ * @throws SignatureExpiredException The signature has expired
+ * @throws SignatureNotYetValidException The signature is not yet valid
+ * @throws SignatureVerificationException The signature does not verify.
+ * @throws DNSSECException Some other error occurred.
+ */
+public static void
+verify(RRset rrset, RRSIGRecord rrsig, DNSKEYRecord key) throws DNSSECException
+{
+ if (!matches(rrsig, key))
+ throw new KeyMismatchException(key, rrsig);
+
+ Date now = new Date();
+ if (now.compareTo(rrsig.getExpire()) > 0)
+ throw new SignatureExpiredException(rrsig.getExpire(), now);
+ if (now.compareTo(rrsig.getTimeSigned()) < 0)
+ throw new SignatureNotYetValidException(rrsig.getTimeSigned(),
+ now);
+
+ verify(key.getPublicKey(), rrsig.getAlgorithm(),
+ digestRRset(rrsig, rrset), rrsig.getSignature());
+}
+
+private static byte []
+sign(PrivateKey privkey, PublicKey pubkey, int alg, byte [] data,
+ String provider) throws DNSSECException
+{
+ byte [] signature;
+ try {
+ Signature s;
+ if (provider != null)
+ s = Signature.getInstance(algString(alg), provider);
+ else
+ s = Signature.getInstance(algString(alg));
+ s.initSign(privkey);
+ s.update(data);
+ signature = s.sign();
+ }
+ catch (GeneralSecurityException e) {
+ throw new DNSSECException(e.toString());
+ }
+
+ if (pubkey instanceof DSAPublicKey) {
+ try {
+ DSAPublicKey dsa = (DSAPublicKey) pubkey;
+ BigInteger P = dsa.getParams().getP();
+ int t = (BigIntegerLength(P) - 64) / 8;
+ signature = DSASignaturetoDNS(signature, t);
+ }
+ catch (IOException e) {
+ throw new IllegalStateException();
+ }
+ } else if (pubkey instanceof ECPublicKey) {
+ try {
+ switch (alg) {
+ case Algorithm.ECDSAP256SHA256:
+ signature = ECDSASignaturetoDNS(signature,
+ ECDSA_P256);
+ break;
+ case Algorithm.ECDSAP384SHA384:
+ signature = ECDSASignaturetoDNS(signature,
+ ECDSA_P384);
+ break;
+ default:
+ throw new UnsupportedAlgorithmException(alg);
+ }
+ }
+ catch (IOException e) {
+ throw new IllegalStateException();
+ }
+ }
+
+ return signature;
+}
+static void
+checkAlgorithm(PrivateKey key, int alg) throws UnsupportedAlgorithmException
+{
+ switch (alg) {
+ case Algorithm.RSAMD5:
+ case Algorithm.RSASHA1:
+ case Algorithm.RSA_NSEC3_SHA1:
+ case Algorithm.RSASHA256:
+ case Algorithm.RSASHA512:
+ if (! (key instanceof RSAPrivateKey))
+ throw new IncompatibleKeyException();
+ break;
+ case Algorithm.DSA:
+ case Algorithm.DSA_NSEC3_SHA1:
+ if (! (key instanceof DSAPrivateKey))
+ throw new IncompatibleKeyException();
+ break;
+ case Algorithm.ECDSAP256SHA256:
+ case Algorithm.ECDSAP384SHA384:
+ if (! (key instanceof ECPrivateKey))
+ throw new IncompatibleKeyException();
+ break;
+ default:
+ throw new UnsupportedAlgorithmException(alg);
+ }
+}
+
+/**
+ * Generate a DNSSEC signature. key and privateKey must refer to the
+ * same underlying cryptographic key.
+ * @param rrset The data to be signed
+ * @param key The DNSKEY record to use as part of signing
+ * @param privkey The PrivateKey to use when signing
+ * @param inception The time at which the signatures should become valid
+ * @param expiration The time at which the signatures should expire
+ * @throws UnsupportedAlgorithmException The algorithm is unknown
+ * @throws MalformedKeyException The key is malformed
+ * @throws DNSSECException Some other error occurred.
+ * @return The generated signature
+ */
+public static RRSIGRecord
+sign(RRset rrset, DNSKEYRecord key, PrivateKey privkey,
+ Date inception, Date expiration) throws DNSSECException
+{
+ return sign(rrset, key, privkey, inception, expiration, null);
+}
+
+/**
+ * Generate a DNSSEC signature. key and privateKey must refer to the
+ * same underlying cryptographic key.
+ * @param rrset The data to be signed
+ * @param key The DNSKEY record to use as part of signing
+ * @param privkey The PrivateKey to use when signing
+ * @param inception The time at which the signatures should become valid
+ * @param expiration The time at which the signatures should expire
+ * @param provider The name of the JCA provider. If non-null, it will be
+ * passed to JCA getInstance() methods.
+ * @throws UnsupportedAlgorithmException The algorithm is unknown
+ * @throws MalformedKeyException The key is malformed
+ * @throws DNSSECException Some other error occurred.
+ * @return The generated signature
+ */
+public static RRSIGRecord
+sign(RRset rrset, DNSKEYRecord key, PrivateKey privkey,
+ Date inception, Date expiration, String provider) throws DNSSECException
+{
+ int alg = key.getAlgorithm();
+ checkAlgorithm(privkey, alg);
+
+ RRSIGRecord rrsig = new RRSIGRecord(rrset.getName(), rrset.getDClass(),
+ rrset.getTTL(), rrset.getType(),
+ alg, rrset.getTTL(),
+ expiration, inception,
+ key.getFootprint(),
+ key.getName(), null);
+
+ rrsig.setSignature(sign(privkey, key.getPublicKey(), alg,
+ digestRRset(rrsig, rrset), provider));
+ return rrsig;
+}
+
+static SIGRecord
+signMessage(Message message, SIGRecord previous, KEYRecord key,
+ PrivateKey privkey, Date inception, Date expiration)
+ throws DNSSECException
+{
+ int alg = key.getAlgorithm();
+ checkAlgorithm(privkey, alg);
+
+ SIGRecord sig = new SIGRecord(Name.root, DClass.ANY, 0, 0,
+ alg, 0, expiration, inception,
+ key.getFootprint(),
+ key.getName(), null);
+ DNSOutput out = new DNSOutput();
+ digestSIG(out, sig);
+ if (previous != null)
+ out.writeByteArray(previous.getSignature());
+ message.toWire(out);
+
+ sig.setSignature(sign(privkey, key.getPublicKey(),
+ alg, out.toByteArray(), null));
+ return sig;
+}
+
+static void
+verifyMessage(Message message, byte [] bytes, SIGRecord sig, SIGRecord previous,
+ KEYRecord key) throws DNSSECException
+{
+ if (!matches(sig, key))
+ throw new KeyMismatchException(key, sig);
+
+ Date now = new Date();
+
+ if (now.compareTo(sig.getExpire()) > 0)
+ throw new SignatureExpiredException(sig.getExpire(), now);
+ if (now.compareTo(sig.getTimeSigned()) < 0)
+ throw new SignatureNotYetValidException(sig.getTimeSigned(),
+ now);
+
+ DNSOutput out = new DNSOutput();
+ digestSIG(out, sig);
+ if (previous != null)
+ out.writeByteArray(previous.getSignature());
+
+ Header header = (Header) message.getHeader().clone();
+ header.decCount(Section.ADDITIONAL);
+ out.writeByteArray(header.toWire());
+
+ out.writeByteArray(bytes, Header.LENGTH,
+ message.sig0start - Header.LENGTH);
+
+ verify(key.getPublicKey(), sig.getAlgorithm(),
+ out.toByteArray(), sig.getSignature());
+}
+
+/**
+ * Generate the digest value for a DS key
+ * @param key Which is covered by the DS record
+ * @param digestid The type of digest
+ * @return The digest value as an array of bytes
+ */
+static byte []
+generateDSDigest(DNSKEYRecord key, int digestid)
+{
+ MessageDigest digest;
+ try {
+ switch (digestid) {
+ case DSRecord.Digest.SHA1:
+ digest = MessageDigest.getInstance("sha-1");
+ break;
+ case DSRecord.Digest.SHA256:
+ digest = MessageDigest.getInstance("sha-256");
+ break;
+ case DSRecord.Digest.SHA384:
+ digest = MessageDigest.getInstance("sha-384");
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "unknown DS digest type " + digestid);
+ }
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("no message digest support");
+ }
+ digest.update(key.getName().toWire());
+ digest.update(key.rdataToWireCanonical());
+ return digest.digest();
+}
+
+}