001/* 002 * Copyright 2015-2024 the original author or authors 003 * 004 * This software is licensed under the Apache License, Version 2.0, 005 * the GNU Lesser General Public License version 2 or later ("LGPL") 006 * and the WTFPL. 007 * You may choose either license to govern your use of this software only 008 * upon the condition that you accept all of the terms of either 009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL. 010 */ 011package org.minidns.dnssec.algorithms; 012 013import org.minidns.dnssec.DnssecValidationFailedException; 014import org.minidns.dnssec.DnssecValidationFailedException.DataMalformedException; 015import org.minidns.dnssec.DnssecValidationFailedException.DnssecInvalidKeySpecException; 016import org.minidns.dnssec.SignatureVerifier; 017import org.minidns.record.DNSKEY; 018import org.minidns.record.RRSIG; 019 020import java.io.ByteArrayOutputStream; 021import java.math.BigInteger; 022import java.security.InvalidKeyException; 023import java.security.KeyFactory; 024import java.security.NoSuchAlgorithmException; 025import java.security.PublicKey; 026import java.security.Signature; 027import java.security.SignatureException; 028 029public abstract class JavaSecSignatureVerifier implements SignatureVerifier { 030 private final KeyFactory keyFactory; 031 private final String signatureAlgorithm; 032 033 public JavaSecSignatureVerifier(String keyAlgorithm, String signatureAlgorithm) throws NoSuchAlgorithmException { 034 keyFactory = KeyFactory.getInstance(keyAlgorithm); 035 this.signatureAlgorithm = signatureAlgorithm; 036 037 // Verify signature algorithm to be valid 038 Signature.getInstance(signatureAlgorithm); 039 } 040 041 public KeyFactory getKeyFactory() { 042 return keyFactory; 043 } 044 045 @Override 046 public boolean verify(byte[] content, RRSIG rrsig, DNSKEY key) throws DnssecValidationFailedException { 047 try { 048 PublicKey publicKey = getPublicKey(key); 049 Signature signature = Signature.getInstance(signatureAlgorithm); 050 signature.initVerify(publicKey); 051 signature.update(content); 052 return signature.verify(getSignature(rrsig)); 053 } catch (NoSuchAlgorithmException e) { 054 // We checked against this before, it should never happen! 055 throw new AssertionError(e); 056 } catch (InvalidKeyException | SignatureException | ArithmeticException e) { 057 throw new DnssecValidationFailedException("Validating signature failed", e); 058 } 059 } 060 061 /** 062 * Encode the two unsigned integers r and s of a DSA or ECDSA signature as ASN.1 DER 063 * {@code SEQUENCE { INTEGER r, INTEGER s }}, as expected by {@link Signature#verify(byte[])}. 064 * <p> 065 * DER requires the minimal two's complement encoding of each INTEGER: leading zero bytes have to be removed, 066 * unless they are required because the following byte has its high bit set. Strict DER parsers, like the one of 067 * the SunEC provider, reject non-minimal encodings. 068 * </p> 069 * 070 * @param r the unsigned big-endian r value. 071 * @param s the unsigned big-endian s value. 072 * @return the DER encoded signature. 073 */ 074 static byte[] toDerSignature(byte[] r, byte[] s) { 075 byte[] rDer = new BigInteger(1, r).toByteArray(); 076 byte[] sDer = new BigInteger(1, s).toByteArray(); 077 078 int sequenceLength = 2 + rDer.length + 2 + sDer.length; 079 // Only the DER short form length encoding is implemented, which is sufficient for DSA (20 byte r and s) and 080 // ECDSA P-256 (32 byte r and s) and P-384 (48 byte r and s). 081 if (sequenceLength > 127) { 082 throw new IllegalArgumentException("The DER encoding of r (" + r.length + " bytes) and s (" + s.length 083 + " bytes) requires a SEQUENCE length of " + sequenceLength 084 + " bytes, but only up to 127 bytes (DER short form length) are supported"); 085 } 086 087 ByteArrayOutputStream bos = new ByteArrayOutputStream(2 + sequenceLength); 088 bos.write(0x30); 089 bos.write(sequenceLength); 090 bos.write(0x02); 091 bos.write(rDer.length); 092 bos.write(rDer, 0, rDer.length); 093 bos.write(0x02); 094 bos.write(sDer.length); 095 bos.write(sDer, 0, sDer.length); 096 return bos.toByteArray(); 097 } 098 099 protected abstract byte[] getSignature(RRSIG rrsig) throws DataMalformedException; 100 101 protected abstract PublicKey getPublicKey(DNSKEY key) throws DataMalformedException, DnssecInvalidKeySpecException; 102}