Home | History | Annotate | Download | only in DNS
      1 // Copyright (c) 1999-2010 Brian Wellington (bwelling (at) xbill.org)
      2 
      3 package org.xbill.DNS;
      4 
      5 import java.io.*;
      6 import java.math.*;
      7 import java.security.*;
      8 import java.security.interfaces.*;
      9 import java.security.spec.*;
     10 import java.util.*;
     11 
     12 /**
     13  * Constants and methods relating to DNSSEC.
     14  *
     15  * DNSSEC provides authentication for DNS information.
     16  * @see RRSIGRecord
     17  * @see DNSKEYRecord
     18  * @see RRset
     19  *
     20  * @author Brian Wellington
     21  */
     22 
     23 public class DNSSEC {
     24 
     25 public static class Algorithm {
     26 	private Algorithm() {}
     27 
     28 	/** RSA/MD5 public key (deprecated) */
     29 	public static final int RSAMD5 = 1;
     30 
     31 	/** Diffie Hellman key */
     32 	public static final int DH = 2;
     33 
     34 	/** DSA public key */
     35 	public static final int DSA = 3;
     36 
     37 	/** RSA/SHA1 public key */
     38 	public static final int RSASHA1 = 5;
     39 
     40 	/** DSA/SHA1, NSEC3-aware public key */
     41 	public static final int DSA_NSEC3_SHA1 = 6;
     42 
     43 	/** RSA/SHA1, NSEC3-aware public key */
     44 	public static final int RSA_NSEC3_SHA1 = 7;
     45 
     46 	/** RSA/SHA256 public key */
     47 	public static final int RSASHA256 = 8;
     48 
     49 	/** RSA/SHA512 public key */
     50 	public static final int RSASHA512 = 10;
     51 
     52 	/** ECDSA Curve P-256 with SHA-256 public key **/
     53 	public static final int ECDSAP256SHA256 = 13;
     54 
     55 	/** ECDSA Curve P-384 with SHA-384 public key **/
     56 	public static final int ECDSAP384SHA384 = 14;
     57 
     58 	/** Indirect keys; the actual key is elsewhere. */
     59 	public static final int INDIRECT = 252;
     60 
     61 	/** Private algorithm, specified by domain name */
     62 	public static final int PRIVATEDNS = 253;
     63 
     64 	/** Private algorithm, specified by OID */
     65 	public static final int PRIVATEOID = 254;
     66 
     67 	private static Mnemonic algs = new Mnemonic("DNSSEC algorithm",
     68 						    Mnemonic.CASE_UPPER);
     69 
     70 	static {
     71 		algs.setMaximum(0xFF);
     72 		algs.setNumericAllowed(true);
     73 
     74 		algs.add(RSAMD5, "RSAMD5");
     75 		algs.add(DH, "DH");
     76 		algs.add(DSA, "DSA");
     77 		algs.add(RSASHA1, "RSASHA1");
     78 		algs.add(DSA_NSEC3_SHA1, "DSA-NSEC3-SHA1");
     79 		algs.add(RSA_NSEC3_SHA1, "RSA-NSEC3-SHA1");
     80 		algs.add(RSASHA256, "RSASHA256");
     81 		algs.add(RSASHA512, "RSASHA512");
     82 		algs.add(ECDSAP256SHA256, "ECDSAP256SHA256");
     83 		algs.add(ECDSAP384SHA384, "ECDSAP384SHA384");
     84 		algs.add(INDIRECT, "INDIRECT");
     85 		algs.add(PRIVATEDNS, "PRIVATEDNS");
     86 		algs.add(PRIVATEOID, "PRIVATEOID");
     87 	}
     88 
     89 	/**
     90 	 * Converts an algorithm into its textual representation
     91 	 */
     92 	public static String
     93 	string(int alg) {
     94 		return algs.getText(alg);
     95 	}
     96 
     97 	/**
     98 	 * Converts a textual representation of an algorithm into its numeric
     99 	 * code.  Integers in the range 0..255 are also accepted.
    100 	 * @param s The textual representation of the algorithm
    101 	 * @return The algorithm code, or -1 on error.
    102 	 */
    103 	public static int
    104 	value(String s) {
    105 		return algs.getValue(s);
    106 	}
    107 }
    108 
    109 private
    110 DNSSEC() { }
    111 
    112 private static void
    113 digestSIG(DNSOutput out, SIGBase sig) {
    114 	out.writeU16(sig.getTypeCovered());
    115 	out.writeU8(sig.getAlgorithm());
    116 	out.writeU8(sig.getLabels());
    117 	out.writeU32(sig.getOrigTTL());
    118 	out.writeU32(sig.getExpire().getTime() / 1000);
    119 	out.writeU32(sig.getTimeSigned().getTime() / 1000);
    120 	out.writeU16(sig.getFootprint());
    121 	sig.getSigner().toWireCanonical(out);
    122 }
    123 
    124 /**
    125  * Creates a byte array containing the concatenation of the fields of the
    126  * SIG record and the RRsets to be signed/verified.  This does not perform
    127  * a cryptographic digest.
    128  * @param rrsig The RRSIG record used to sign/verify the rrset.
    129  * @param rrset The data to be signed/verified.
    130  * @return The data to be cryptographically signed or verified.
    131  */
    132 public static byte []
    133 digestRRset(RRSIGRecord rrsig, RRset rrset) {
    134 	DNSOutput out = new DNSOutput();
    135 	digestSIG(out, rrsig);
    136 
    137 	int size = rrset.size();
    138 	Record [] records = new Record[size];
    139 
    140 	Iterator it = rrset.rrs();
    141 	Name name = rrset.getName();
    142 	Name wild = null;
    143 	int sigLabels = rrsig.getLabels() + 1; // Add the root label back.
    144 	if (name.labels() > sigLabels)
    145 		wild = name.wild(name.labels() - sigLabels);
    146 	while (it.hasNext())
    147 		records[--size] = (Record) it.next();
    148 	Arrays.sort(records);
    149 
    150 	DNSOutput header = new DNSOutput();
    151 	if (wild != null)
    152 		wild.toWireCanonical(header);
    153 	else
    154 		name.toWireCanonical(header);
    155 	header.writeU16(rrset.getType());
    156 	header.writeU16(rrset.getDClass());
    157 	header.writeU32(rrsig.getOrigTTL());
    158 	for (int i = 0; i < records.length; i++) {
    159 		out.writeByteArray(header.toByteArray());
    160 		int lengthPosition = out.current();
    161 		out.writeU16(0);
    162 		out.writeByteArray(records[i].rdataToWireCanonical());
    163 		int rrlength = out.current() - lengthPosition - 2;
    164 		out.save();
    165 		out.jump(lengthPosition);
    166 		out.writeU16(rrlength);
    167 		out.restore();
    168 	}
    169 	return out.toByteArray();
    170 }
    171 
    172 /**
    173  * Creates a byte array containing the concatenation of the fields of the
    174  * SIG(0) record and the message to be signed.  This does not perform
    175  * a cryptographic digest.
    176  * @param sig The SIG record used to sign the rrset.
    177  * @param msg The message to be signed.
    178  * @param previous If this is a response, the signature from the query.
    179  * @return The data to be cryptographically signed.
    180  */
    181 public static byte []
    182 digestMessage(SIGRecord sig, Message msg, byte [] previous) {
    183 	DNSOutput out = new DNSOutput();
    184 	digestSIG(out, sig);
    185 
    186 	if (previous != null)
    187 		out.writeByteArray(previous);
    188 
    189 	msg.toWire(out);
    190 	return out.toByteArray();
    191 }
    192 
    193 /**
    194  * A DNSSEC exception.
    195  */
    196 public static class DNSSECException extends Exception {
    197 	DNSSECException(String s) {
    198 		super(s);
    199 	}
    200 }
    201 
    202 /**
    203  * An algorithm is unsupported by this DNSSEC implementation.
    204  */
    205 public static class UnsupportedAlgorithmException extends DNSSECException {
    206 	UnsupportedAlgorithmException(int alg) {
    207 		super("Unsupported algorithm: " + alg);
    208 	}
    209 }
    210 
    211 /**
    212  * The cryptographic data in a DNSSEC key is malformed.
    213  */
    214 public static class MalformedKeyException extends DNSSECException {
    215 	MalformedKeyException(KEYBase rec) {
    216 		super("Invalid key data: " + rec.rdataToString());
    217 	}
    218 }
    219 
    220 /**
    221  * A DNSSEC verification failed because fields in the DNSKEY and RRSIG records
    222  * do not match.
    223  */
    224 public static class KeyMismatchException extends DNSSECException {
    225 	private KEYBase key;
    226 	private SIGBase sig;
    227 
    228 	KeyMismatchException(KEYBase key, SIGBase sig) {
    229 		super("key " +
    230 		      key.getName() + "/" +
    231 		      DNSSEC.Algorithm.string(key.getAlgorithm()) + "/" +
    232 		      key.getFootprint() + " " +
    233 		      "does not match signature " +
    234 		      sig.getSigner() + "/" +
    235 		      DNSSEC.Algorithm.string(sig.getAlgorithm()) + "/" +
    236 		      sig.getFootprint());
    237 	}
    238 }
    239 
    240 /**
    241  * A DNSSEC verification failed because the signature has expired.
    242  */
    243 public static class SignatureExpiredException extends DNSSECException {
    244 	private Date when, now;
    245 
    246 	SignatureExpiredException(Date when, Date now) {
    247 		super("signature expired");
    248 		this.when = when;
    249 		this.now = now;
    250 	}
    251 
    252 	/**
    253 	 * @return When the signature expired
    254 	 */
    255 	public Date
    256 	getExpiration() {
    257 		return when;
    258 	}
    259 
    260 	/**
    261 	 * @return When the verification was attempted
    262 	 */
    263 	public Date
    264 	getVerifyTime() {
    265 		return now;
    266 	}
    267 }
    268 
    269 /**
    270  * A DNSSEC verification failed because the signature has not yet become valid.
    271  */
    272 public static class SignatureNotYetValidException extends DNSSECException {
    273 	private Date when, now;
    274 
    275 	SignatureNotYetValidException(Date when, Date now) {
    276 		super("signature is not yet valid");
    277 		this.when = when;
    278 		this.now = now;
    279 	}
    280 
    281 	/**
    282 	 * @return When the signature will become valid
    283 	 */
    284 	public Date
    285 	getExpiration() {
    286 		return when;
    287 	}
    288 
    289 	/**
    290 	 * @return When the verification was attempted
    291 	 */
    292 	public Date
    293 	getVerifyTime() {
    294 		return now;
    295 	}
    296 }
    297 
    298 /**
    299  * A DNSSEC verification failed because the cryptographic signature
    300  * verification failed.
    301  */
    302 public static class SignatureVerificationException extends DNSSECException {
    303 	SignatureVerificationException() {
    304 		super("signature verification failed");
    305 	}
    306 }
    307 
    308 /**
    309  * The key data provided is inconsistent.
    310  */
    311 public static class IncompatibleKeyException extends IllegalArgumentException {
    312 	IncompatibleKeyException() {
    313 		super("incompatible keys");
    314 	}
    315 }
    316 
    317 private static int
    318 BigIntegerLength(BigInteger i) {
    319 	return (i.bitLength() + 7) / 8;
    320 }
    321 
    322 private static BigInteger
    323 readBigInteger(DNSInput in, int len) throws IOException {
    324 	byte [] b = in.readByteArray(len);
    325 	return new BigInteger(1, b);
    326 }
    327 
    328 private static BigInteger
    329 readBigInteger(DNSInput in) {
    330 	byte [] b = in.readByteArray();
    331 	return new BigInteger(1, b);
    332 }
    333 
    334 private static void
    335 writeBigInteger(DNSOutput out, BigInteger val) {
    336 	byte [] b = val.toByteArray();
    337 	if (b[0] == 0)
    338 		out.writeByteArray(b, 1, b.length - 1);
    339 	else
    340 		out.writeByteArray(b);
    341 }
    342 
    343 private static PublicKey
    344 toRSAPublicKey(KEYBase r) throws IOException, GeneralSecurityException {
    345 	DNSInput in = new DNSInput(r.getKey());
    346 	int exponentLength = in.readU8();
    347 	if (exponentLength == 0)
    348 		exponentLength = in.readU16();
    349 	BigInteger exponent = readBigInteger(in, exponentLength);
    350 	BigInteger modulus = readBigInteger(in);
    351 
    352 	KeyFactory factory = KeyFactory.getInstance("RSA");
    353 	return factory.generatePublic(new RSAPublicKeySpec(modulus, exponent));
    354 }
    355 
    356 private static PublicKey
    357 toDSAPublicKey(KEYBase r) throws IOException, GeneralSecurityException,
    358 	MalformedKeyException
    359 {
    360 	DNSInput in = new DNSInput(r.getKey());
    361 
    362 	int t = in.readU8();
    363 	if (t > 8)
    364 		throw new MalformedKeyException(r);
    365 
    366 	BigInteger q = readBigInteger(in, 20);
    367 	BigInteger p = readBigInteger(in, 64 + t*8);
    368 	BigInteger g = readBigInteger(in, 64 + t*8);
    369 	BigInteger y = readBigInteger(in, 64 + t*8);
    370 
    371 	KeyFactory factory = KeyFactory.getInstance("DSA");
    372 	return factory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
    373 }
    374 
    375 private static class ECKeyInfo {
    376 	int length;
    377 	public BigInteger p, a, b, gx, gy, n;
    378 	EllipticCurve curve;
    379 	ECParameterSpec spec;
    380 
    381 	ECKeyInfo(int length, String p_str, String a_str, String b_str,
    382 		  String gx_str, String gy_str, String n_str)
    383 	{
    384 		this.length = length;
    385 		p = new BigInteger(p_str, 16);
    386 		a = new BigInteger(a_str, 16);
    387 		b = new BigInteger(b_str, 16);
    388 		gx = new BigInteger(gx_str, 16);
    389 		gy = new BigInteger(gy_str, 16);
    390 		n = new BigInteger(n_str, 16);
    391 		curve = new EllipticCurve(new ECFieldFp(p), a, b);
    392 		spec = new ECParameterSpec(curve, new ECPoint(gx, gy), n, 1);
    393 	}
    394 }
    395 
    396 // RFC 5114 Section 2.6
    397 private static final ECKeyInfo ECDSA_P256 = new ECKeyInfo(32,
    398 	"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
    399 	"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
    400 	"5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
    401 	"6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
    402 	"4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
    403 	"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
    404 
    405 // RFC 5114 Section 2.7
    406 private static final ECKeyInfo ECDSA_P384 = new ECKeyInfo(48,
    407 	"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
    408 	"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
    409 	"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
    410 	"AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
    411 	"3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
    412 	"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
    413 
    414 private static PublicKey
    415 toECDSAPublicKey(KEYBase r, ECKeyInfo keyinfo) throws IOException,
    416 	GeneralSecurityException, MalformedKeyException
    417 {
    418 	DNSInput in = new DNSInput(r.getKey());
    419 
    420 	// RFC 6605 Section 4
    421 	BigInteger x = readBigInteger(in, keyinfo.length);
    422 	BigInteger y = readBigInteger(in, keyinfo.length);
    423 	ECPoint q = new ECPoint(x, y);
    424 
    425 	KeyFactory factory = KeyFactory.getInstance("EC");
    426 	return factory.generatePublic(new ECPublicKeySpec(q, keyinfo.spec));
    427 }
    428 
    429 /** Converts a KEY/DNSKEY record into a PublicKey */
    430 static PublicKey
    431 toPublicKey(KEYBase r) throws DNSSECException {
    432 	int alg = r.getAlgorithm();
    433 	try {
    434 		switch (alg) {
    435 		case Algorithm.RSAMD5:
    436 		case Algorithm.RSASHA1:
    437 		case Algorithm.RSA_NSEC3_SHA1:
    438 		case Algorithm.RSASHA256:
    439 		case Algorithm.RSASHA512:
    440 			return toRSAPublicKey(r);
    441 		case Algorithm.DSA:
    442 		case Algorithm.DSA_NSEC3_SHA1:
    443 			return toDSAPublicKey(r);
    444 		case Algorithm.ECDSAP256SHA256:
    445 			return toECDSAPublicKey(r, ECDSA_P256);
    446 		case Algorithm.ECDSAP384SHA384:
    447 			return toECDSAPublicKey(r, ECDSA_P384);
    448 		default:
    449 			throw new UnsupportedAlgorithmException(alg);
    450 		}
    451 	}
    452 	catch (IOException e) {
    453 		throw new MalformedKeyException(r);
    454 	}
    455 	catch (GeneralSecurityException e) {
    456 		throw new DNSSECException(e.toString());
    457 	}
    458 }
    459 
    460 private static byte []
    461 fromRSAPublicKey(RSAPublicKey key) {
    462 	DNSOutput out = new DNSOutput();
    463 	BigInteger exponent = key.getPublicExponent();
    464 	BigInteger modulus = key.getModulus();
    465 	int exponentLength = BigIntegerLength(exponent);
    466 
    467 	if (exponentLength < 256)
    468 		out.writeU8(exponentLength);
    469 	else {
    470 		out.writeU8(0);
    471 		out.writeU16(exponentLength);
    472 	}
    473 	writeBigInteger(out, exponent);
    474 	writeBigInteger(out, modulus);
    475 
    476 	return out.toByteArray();
    477 }
    478 
    479 private static byte []
    480 fromDSAPublicKey(DSAPublicKey key) {
    481 	DNSOutput out = new DNSOutput();
    482 	BigInteger q = key.getParams().getQ();
    483 	BigInteger p = key.getParams().getP();
    484 	BigInteger g = key.getParams().getG();
    485 	BigInteger y = key.getY();
    486 	int t = (p.toByteArray().length - 64) / 8;
    487 
    488 	out.writeU8(t);
    489 	writeBigInteger(out, q);
    490 	writeBigInteger(out, p);
    491 	writeBigInteger(out, g);
    492 	writeBigInteger(out, y);
    493 
    494 	return out.toByteArray();
    495 }
    496 
    497 private static byte []
    498 fromECDSAPublicKey(ECPublicKey key) {
    499 	DNSOutput out = new DNSOutput();
    500 
    501 	BigInteger x = key.getW().getAffineX();
    502 	BigInteger y = key.getW().getAffineY();
    503 
    504 	writeBigInteger(out, x);
    505 	writeBigInteger(out, y);
    506 
    507 	return out.toByteArray();
    508 }
    509 
    510 /** Builds a DNSKEY record from a PublicKey */
    511 static byte []
    512 fromPublicKey(PublicKey key, int alg) throws DNSSECException
    513 {
    514 
    515 	switch (alg) {
    516 	case Algorithm.RSAMD5:
    517 	case Algorithm.RSASHA1:
    518 	case Algorithm.RSA_NSEC3_SHA1:
    519 	case Algorithm.RSASHA256:
    520 	case Algorithm.RSASHA512:
    521 		if (! (key instanceof RSAPublicKey))
    522 			throw new IncompatibleKeyException();
    523 		return fromRSAPublicKey((RSAPublicKey) key);
    524 	case Algorithm.DSA:
    525 	case Algorithm.DSA_NSEC3_SHA1:
    526 		if (! (key instanceof DSAPublicKey))
    527 			throw new IncompatibleKeyException();
    528 		return fromDSAPublicKey((DSAPublicKey) key);
    529 	case Algorithm.ECDSAP256SHA256:
    530 	case Algorithm.ECDSAP384SHA384:
    531 		if (! (key instanceof ECPublicKey))
    532 			throw new IncompatibleKeyException();
    533 		return fromECDSAPublicKey((ECPublicKey) key);
    534 	default:
    535 		throw new UnsupportedAlgorithmException(alg);
    536 	}
    537 }
    538 
    539 /**
    540  * Convert an algorithm number to the corresponding JCA string.
    541  * @param alg The algorithm number.
    542  * @throws UnsupportedAlgorithmException The algorithm is unknown.
    543  */
    544 public static String
    545 algString(int alg) throws UnsupportedAlgorithmException {
    546 	switch (alg) {
    547 	case Algorithm.RSAMD5:
    548 		return "MD5withRSA";
    549 	case Algorithm.DSA:
    550 	case Algorithm.DSA_NSEC3_SHA1:
    551 		return "SHA1withDSA";
    552 	case Algorithm.RSASHA1:
    553 	case Algorithm.RSA_NSEC3_SHA1:
    554 		return "SHA1withRSA";
    555 	case Algorithm.RSASHA256:
    556 		return "SHA256withRSA";
    557 	case Algorithm.RSASHA512:
    558 		return "SHA512withRSA";
    559 	case Algorithm.ECDSAP256SHA256:
    560 		return "SHA256withECDSA";
    561 	case Algorithm.ECDSAP384SHA384:
    562 		return "SHA384withECDSA";
    563 	default:
    564 		throw new UnsupportedAlgorithmException(alg);
    565 	}
    566 }
    567 
    568 private static final int ASN1_SEQ = 0x30;
    569 private static final int ASN1_INT = 0x2;
    570 
    571 private static final int DSA_LEN = 20;
    572 
    573 private static byte []
    574 DSASignaturefromDNS(byte [] dns) throws DNSSECException, IOException {
    575 	if (dns.length != 1 + DSA_LEN * 2)
    576 		throw new SignatureVerificationException();
    577 
    578 	DNSInput in = new DNSInput(dns);
    579 	DNSOutput out = new DNSOutput();
    580 
    581 	int t = in.readU8();
    582 
    583 	byte [] r = in.readByteArray(DSA_LEN);
    584 	int rlen = DSA_LEN;
    585 	if (r[0] < 0)
    586 		rlen++;
    587 
    588 	byte [] s = in.readByteArray(DSA_LEN);
    589         int slen = DSA_LEN;
    590         if (s[0] < 0)
    591                 slen++;
    592 
    593 	out.writeU8(ASN1_SEQ);
    594 	out.writeU8(rlen + slen + 4);
    595 
    596 	out.writeU8(ASN1_INT);
    597 	out.writeU8(rlen);
    598 	if (rlen > DSA_LEN)
    599 		out.writeU8(0);
    600 	out.writeByteArray(r);
    601 
    602 	out.writeU8(ASN1_INT);
    603 	out.writeU8(slen);
    604 	if (slen > DSA_LEN)
    605 		out.writeU8(0);
    606 	out.writeByteArray(s);
    607 
    608 	return out.toByteArray();
    609 }
    610 
    611 private static byte []
    612 DSASignaturetoDNS(byte [] signature, int t) throws IOException {
    613 	DNSInput in = new DNSInput(signature);
    614 	DNSOutput out = new DNSOutput();
    615 
    616 	out.writeU8(t);
    617 
    618 	int tmp = in.readU8();
    619 	if (tmp != ASN1_SEQ)
    620 		throw new IOException();
    621 	int seqlen = in.readU8();
    622 
    623 	tmp = in.readU8();
    624 	if (tmp != ASN1_INT)
    625 		throw new IOException();
    626 	int rlen = in.readU8();
    627 	if (rlen == DSA_LEN + 1) {
    628 		if (in.readU8() != 0)
    629 			throw new IOException();
    630 	} else if (rlen != DSA_LEN)
    631 		throw new IOException();
    632 	byte [] bytes = in.readByteArray(DSA_LEN);
    633 	out.writeByteArray(bytes);
    634 
    635 	tmp = in.readU8();
    636 	if (tmp != ASN1_INT)
    637 		throw new IOException();
    638 	int slen = in.readU8();
    639 	if (slen == DSA_LEN + 1) {
    640 		if (in.readU8() != 0)
    641 			throw new IOException();
    642 	} else if (slen != DSA_LEN)
    643 		throw new IOException();
    644 	bytes = in.readByteArray(DSA_LEN);
    645 	out.writeByteArray(bytes);
    646 
    647 	return out.toByteArray();
    648 }
    649 
    650 private static byte []
    651 ECDSASignaturefromDNS(byte [] signature, ECKeyInfo keyinfo)
    652 	throws DNSSECException, IOException
    653 {
    654 	if (signature.length != keyinfo.length * 2)
    655 		throw new SignatureVerificationException();
    656 
    657 	DNSInput in = new DNSInput(signature);
    658 	DNSOutput out = new DNSOutput();
    659 
    660 	byte [] r = in.readByteArray(keyinfo.length);
    661 	int rlen = keyinfo.length;
    662 	if (r[0] < 0)
    663 		rlen++;
    664 
    665 	byte [] s = in.readByteArray(keyinfo.length);
    666 	int slen = keyinfo.length;
    667 	if (s[0] < 0)
    668 		slen++;
    669 
    670 	out.writeU8(ASN1_SEQ);
    671 	out.writeU8(rlen + slen + 4);
    672 
    673 	out.writeU8(ASN1_INT);
    674 	out.writeU8(rlen);
    675 	if (rlen > keyinfo.length)
    676 		out.writeU8(0);
    677 	out.writeByteArray(r);
    678 
    679 	out.writeU8(ASN1_INT);
    680 	out.writeU8(slen);
    681 	if (slen > keyinfo.length)
    682 		out.writeU8(0);
    683 	out.writeByteArray(s);
    684 
    685 	return out.toByteArray();
    686 }
    687 
    688 private static byte []
    689 ECDSASignaturetoDNS(byte [] signature, ECKeyInfo keyinfo) throws IOException {
    690 	DNSInput in = new DNSInput(signature);
    691 	DNSOutput out = new DNSOutput();
    692 
    693 	int tmp = in.readU8();
    694 	if (tmp != ASN1_SEQ)
    695 		throw new IOException();
    696 	int seqlen = in.readU8();
    697 
    698 	tmp = in.readU8();
    699 	if (tmp != ASN1_INT)
    700 		throw new IOException();
    701 	int rlen = in.readU8();
    702 	if (rlen == keyinfo.length + 1) {
    703 		if (in.readU8() != 0)
    704 			throw new IOException();
    705 	} else if (rlen != keyinfo.length)
    706 		throw new IOException();
    707 	byte[] bytes = in.readByteArray(keyinfo.length);
    708 	out.writeByteArray(bytes);
    709 
    710 	tmp = in.readU8();
    711 	if (tmp != ASN1_INT)
    712 		throw new IOException();
    713 	int slen = in.readU8();
    714 	if (slen == keyinfo.length + 1) {
    715 		if (in.readU8() != 0)
    716 			throw new IOException();
    717 	} else if (slen != keyinfo.length)
    718 		throw new IOException();
    719 	bytes = in.readByteArray(keyinfo.length);
    720 	out.writeByteArray(bytes);
    721 
    722 	return out.toByteArray();
    723 }
    724 
    725 private static void
    726 verify(PublicKey key, int alg, byte [] data, byte [] signature)
    727 throws DNSSECException
    728 {
    729 	if (key instanceof DSAPublicKey) {
    730 		try {
    731 			signature = DSASignaturefromDNS(signature);
    732 		}
    733 		catch (IOException e) {
    734 			throw new IllegalStateException();
    735 		}
    736 	} else if (key instanceof ECPublicKey) {
    737 		try {
    738 			switch (alg) {
    739 			case Algorithm.ECDSAP256SHA256:
    740 				signature = ECDSASignaturefromDNS(signature,
    741 								  ECDSA_P256);
    742 				break;
    743 			case Algorithm.ECDSAP384SHA384:
    744 				signature = ECDSASignaturefromDNS(signature,
    745 								  ECDSA_P384);
    746 				break;
    747 			default:
    748 				throw new UnsupportedAlgorithmException(alg);
    749 			}
    750 		}
    751 		catch (IOException e) {
    752 			throw new IllegalStateException();
    753 		}
    754 	}
    755 
    756 	try {
    757 		Signature s = Signature.getInstance(algString(alg));
    758 		s.initVerify(key);
    759 		s.update(data);
    760 		if (!s.verify(signature))
    761 			throw new SignatureVerificationException();
    762 	}
    763 	catch (GeneralSecurityException e) {
    764 		throw new DNSSECException(e.toString());
    765 	}
    766 }
    767 
    768 private static boolean
    769 matches(SIGBase sig, KEYBase key)
    770 {
    771 	return (key.getAlgorithm() == sig.getAlgorithm() &&
    772 		key.getFootprint() == sig.getFootprint() &&
    773 		key.getName().equals(sig.getSigner()));
    774 }
    775 
    776 /**
    777  * Verify a DNSSEC signature.
    778  * @param rrset The data to be verified.
    779  * @param rrsig The RRSIG record containing the signature.
    780  * @param key The DNSKEY record to verify the signature with.
    781  * @throws UnsupportedAlgorithmException The algorithm is unknown
    782  * @throws MalformedKeyException The key is malformed
    783  * @throws KeyMismatchException The key and signature do not match
    784  * @throws SignatureExpiredException The signature has expired
    785  * @throws SignatureNotYetValidException The signature is not yet valid
    786  * @throws SignatureVerificationException The signature does not verify.
    787  * @throws DNSSECException Some other error occurred.
    788  */
    789 public static void
    790 verify(RRset rrset, RRSIGRecord rrsig, DNSKEYRecord key) throws DNSSECException
    791 {
    792 	if (!matches(rrsig, key))
    793 		throw new KeyMismatchException(key, rrsig);
    794 
    795 	Date now = new Date();
    796 	if (now.compareTo(rrsig.getExpire()) > 0)
    797 		throw new SignatureExpiredException(rrsig.getExpire(), now);
    798 	if (now.compareTo(rrsig.getTimeSigned()) < 0)
    799 		throw new SignatureNotYetValidException(rrsig.getTimeSigned(),
    800 							now);
    801 
    802 	verify(key.getPublicKey(), rrsig.getAlgorithm(),
    803 	       digestRRset(rrsig, rrset), rrsig.getSignature());
    804 }
    805 
    806 private static byte []
    807 sign(PrivateKey privkey, PublicKey pubkey, int alg, byte [] data,
    808      String provider) throws DNSSECException
    809 {
    810 	byte [] signature;
    811 	try {
    812 		Signature s;
    813 		if (provider != null)
    814 			s = Signature.getInstance(algString(alg), provider);
    815 		else
    816 			s = Signature.getInstance(algString(alg));
    817 		s.initSign(privkey);
    818 		s.update(data);
    819 		signature = s.sign();
    820 	}
    821 	catch (GeneralSecurityException e) {
    822 		throw new DNSSECException(e.toString());
    823 	}
    824 
    825 	if (pubkey instanceof DSAPublicKey) {
    826 		try {
    827 			DSAPublicKey dsa = (DSAPublicKey) pubkey;
    828 			BigInteger P = dsa.getParams().getP();
    829 			int t = (BigIntegerLength(P) - 64) / 8;
    830 			signature = DSASignaturetoDNS(signature, t);
    831 		}
    832 		catch (IOException e) {
    833 			throw new IllegalStateException();
    834 		}
    835 	} else if (pubkey instanceof ECPublicKey) {
    836 		try {
    837 			switch (alg) {
    838 			case Algorithm.ECDSAP256SHA256:
    839 				signature = ECDSASignaturetoDNS(signature,
    840 								ECDSA_P256);
    841 				break;
    842 			case Algorithm.ECDSAP384SHA384:
    843 				signature = ECDSASignaturetoDNS(signature,
    844 								ECDSA_P384);
    845 				break;
    846 			default:
    847 				throw new UnsupportedAlgorithmException(alg);
    848 			}
    849 		}
    850 		catch (IOException e) {
    851 			throw new IllegalStateException();
    852 		}
    853 	}
    854 
    855 	return signature;
    856 }
    857 static void
    858 checkAlgorithm(PrivateKey key, int alg) throws UnsupportedAlgorithmException
    859 {
    860 	switch (alg) {
    861 	case Algorithm.RSAMD5:
    862 	case Algorithm.RSASHA1:
    863 	case Algorithm.RSA_NSEC3_SHA1:
    864 	case Algorithm.RSASHA256:
    865 	case Algorithm.RSASHA512:
    866 		if (! (key instanceof RSAPrivateKey))
    867 			throw new IncompatibleKeyException();
    868 		break;
    869 	case Algorithm.DSA:
    870 	case Algorithm.DSA_NSEC3_SHA1:
    871 		if (! (key instanceof DSAPrivateKey))
    872 			throw new IncompatibleKeyException();
    873 		break;
    874 	case Algorithm.ECDSAP256SHA256:
    875 	case Algorithm.ECDSAP384SHA384:
    876 		if (! (key instanceof ECPrivateKey))
    877 			throw new IncompatibleKeyException();
    878 		break;
    879 	default:
    880 		throw new UnsupportedAlgorithmException(alg);
    881 	}
    882 }
    883 
    884 /**
    885  * Generate a DNSSEC signature.  key and privateKey must refer to the
    886  * same underlying cryptographic key.
    887  * @param rrset The data to be signed
    888  * @param key The DNSKEY record to use as part of signing
    889  * @param privkey The PrivateKey to use when signing
    890  * @param inception The time at which the signatures should become valid
    891  * @param expiration The time at which the signatures should expire
    892  * @throws UnsupportedAlgorithmException The algorithm is unknown
    893  * @throws MalformedKeyException The key is malformed
    894  * @throws DNSSECException Some other error occurred.
    895  * @return The generated signature
    896  */
    897 public static RRSIGRecord
    898 sign(RRset rrset, DNSKEYRecord key, PrivateKey privkey,
    899      Date inception, Date expiration) throws DNSSECException
    900 {
    901 	return sign(rrset, key, privkey, inception, expiration, null);
    902 }
    903 
    904 /**
    905  * Generate a DNSSEC signature.  key and privateKey must refer to the
    906  * same underlying cryptographic key.
    907  * @param rrset The data to be signed
    908  * @param key The DNSKEY record to use as part of signing
    909  * @param privkey The PrivateKey to use when signing
    910  * @param inception The time at which the signatures should become valid
    911  * @param expiration The time at which the signatures should expire
    912  * @param provider The name of the JCA provider.  If non-null, it will be
    913  * passed to JCA getInstance() methods.
    914  * @throws UnsupportedAlgorithmException The algorithm is unknown
    915  * @throws MalformedKeyException The key is malformed
    916  * @throws DNSSECException Some other error occurred.
    917  * @return The generated signature
    918  */
    919 public static RRSIGRecord
    920 sign(RRset rrset, DNSKEYRecord key, PrivateKey privkey,
    921      Date inception, Date expiration, String provider) throws DNSSECException
    922 {
    923 	int alg = key.getAlgorithm();
    924 	checkAlgorithm(privkey, alg);
    925 
    926 	RRSIGRecord rrsig = new RRSIGRecord(rrset.getName(), rrset.getDClass(),
    927 					    rrset.getTTL(), rrset.getType(),
    928 					    alg, rrset.getTTL(),
    929 					    expiration, inception,
    930 					    key.getFootprint(),
    931 					    key.getName(), null);
    932 
    933 	rrsig.setSignature(sign(privkey, key.getPublicKey(), alg,
    934 				digestRRset(rrsig, rrset), provider));
    935 	return rrsig;
    936 }
    937 
    938 static SIGRecord
    939 signMessage(Message message, SIGRecord previous, KEYRecord key,
    940 	    PrivateKey privkey, Date inception, Date expiration)
    941 	throws DNSSECException
    942 {
    943 	int alg = key.getAlgorithm();
    944 	checkAlgorithm(privkey, alg);
    945 
    946 	SIGRecord sig = new SIGRecord(Name.root, DClass.ANY, 0, 0,
    947 					    alg, 0, expiration, inception,
    948 					    key.getFootprint(),
    949 					    key.getName(), null);
    950 	DNSOutput out = new DNSOutput();
    951 	digestSIG(out, sig);
    952 	if (previous != null)
    953 		out.writeByteArray(previous.getSignature());
    954 	message.toWire(out);
    955 
    956 	sig.setSignature(sign(privkey, key.getPublicKey(),
    957 			      alg, out.toByteArray(), null));
    958 	return sig;
    959 }
    960 
    961 static void
    962 verifyMessage(Message message, byte [] bytes, SIGRecord sig, SIGRecord previous,
    963 	      KEYRecord key) throws DNSSECException
    964 {
    965 	if (!matches(sig, key))
    966 		throw new KeyMismatchException(key, sig);
    967 
    968 	Date now = new Date();
    969 
    970 	if (now.compareTo(sig.getExpire()) > 0)
    971 		throw new SignatureExpiredException(sig.getExpire(), now);
    972 	if (now.compareTo(sig.getTimeSigned()) < 0)
    973 		throw new SignatureNotYetValidException(sig.getTimeSigned(),
    974 							now);
    975 
    976 	DNSOutput out = new DNSOutput();
    977 	digestSIG(out, sig);
    978 	if (previous != null)
    979 		out.writeByteArray(previous.getSignature());
    980 
    981 	Header header = (Header) message.getHeader().clone();
    982 	header.decCount(Section.ADDITIONAL);
    983 	out.writeByteArray(header.toWire());
    984 
    985 	out.writeByteArray(bytes, Header.LENGTH,
    986 			   message.sig0start - Header.LENGTH);
    987 
    988 	verify(key.getPublicKey(), sig.getAlgorithm(),
    989 	       out.toByteArray(), sig.getSignature());
    990 }
    991 
    992 /**
    993  * Generate the digest value for a DS key
    994  * @param key Which is covered by the DS record
    995  * @param digestid The type of digest
    996  * @return The digest value as an array of bytes
    997  */
    998 static byte []
    999 generateDSDigest(DNSKEYRecord key, int digestid)
   1000 {
   1001 	MessageDigest digest;
   1002 	try {
   1003 		switch (digestid) {
   1004 		case DSRecord.Digest.SHA1:
   1005 			digest = MessageDigest.getInstance("sha-1");
   1006 			break;
   1007 		case DSRecord.Digest.SHA256:
   1008 			digest = MessageDigest.getInstance("sha-256");
   1009 			break;
   1010 		case DSRecord.Digest.SHA384:
   1011 			digest = MessageDigest.getInstance("sha-384");
   1012 			break;
   1013 		default:
   1014 			throw new IllegalArgumentException(
   1015 					"unknown DS digest type " + digestid);
   1016 		}
   1017 	}
   1018 	catch (NoSuchAlgorithmException e) {
   1019 		throw new IllegalStateException("no message digest support");
   1020 	}
   1021 	digest.update(key.getName().toWire());
   1022 	digest.update(key.rdataToWireCanonical());
   1023 	return digest.digest();
   1024 }
   1025 
   1026 }
   1027