From a9353b3af3a841920cffcbaa8d146af4c0cd3e2e Mon Sep 17 00:00:00 2001 From: David Blacka Date: Sun, 15 Jul 2018 00:54:10 +0000 Subject: [PATCH] Now able to generated alg 15 keypairs They _look_ correct, but may not be. --- .../dnssec/security/DnsKeyAlgorithm.java | 23 ++- .../dnssec/security/DnsKeyConverter.java | 133 +++++++++++++++++- .../dnssec/security/JCEDnsSecSigner.java | 28 ++-- 3 files changed, 152 insertions(+), 32 deletions(-) diff --git a/src/com/verisignlabs/dnssec/security/DnsKeyAlgorithm.java b/src/com/verisignlabs/dnssec/security/DnsKeyAlgorithm.java index 1ce5a47..429f63f 100644 --- a/src/com/verisignlabs/dnssec/security/DnsKeyAlgorithm.java +++ b/src/com/verisignlabs/dnssec/security/DnsKeyAlgorithm.java @@ -1,11 +1,11 @@ /* * $Id$ - * + * * Copyright (c) 2006 VeriSign. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. 2. Redistributions in * binary form must reproduce the above copyright notice, this list of @@ -13,7 +13,7 @@ * materials provided with the distribution. 3. The name of the author may not * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN @@ -24,7 +24,7 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * */ package com.verisignlabs.dnssec.security; @@ -47,12 +47,12 @@ import net.i2p.crypto.eddsa.spec.*; /** * This class handles translating DNS signing algorithm identifiers into various * usable java implementations. - * + * * Besides centralizing the logic surrounding matching a DNSKEY algorithm * identifier with various crypto implementations, it also handles algorithm * aliasing -- that is, defining a new algorithm identifier to be equivalent to * an existing identifier. - * + * * @author David Blacka (orig) * @author $Author: davidb $ (latest) * @version $Revision: 2098 $ @@ -265,7 +265,6 @@ public class DnsKeyAlgorithm } EdAlgEntry entry = new EdAlgEntry(algorithm, sigName, baseType, ed_spec); mAlgorithmMap.put(algorithm, entry); - } @@ -451,7 +450,7 @@ public class DnsKeyAlgorithm /** * Translate a possible algorithm alias back to the original DNSSEC algorithm * number - * + * * @param algorithm * a DNSSEC algorithm that may be an alias. * @return -1 if the algorithm isn't recognised, the orignal algorithm number @@ -466,7 +465,7 @@ public class DnsKeyAlgorithm /** * Test if a given algorithm is supported. - * + * * @param algorithm The DNSSEC algorithm number. * @return true if the algorithm is a recognized and supported algorithm or alias. */ @@ -479,7 +478,7 @@ public class DnsKeyAlgorithm /** * Given an algorithm mnemonic, convert the mnemonic to a DNSSEC algorithm * number. - * + * * @param s * The mnemonic string. This is case-insensitive. * @return -1 if the mnemonic isn't recognized or supported, the algorithm @@ -494,7 +493,7 @@ public class DnsKeyAlgorithm /** * Given a DNSSEC algorithm number, return the "preferred" mnemonic. - * + * * @param algorithm * A DNSSEC algorithm number. * @return The preferred mnemonic string, or null if not supported or diff --git a/src/com/verisignlabs/dnssec/security/DnsKeyConverter.java b/src/com/verisignlabs/dnssec/security/DnsKeyConverter.java index 5a00fd3..13e1ade 100644 --- a/src/com/verisignlabs/dnssec/security/DnsKeyConverter.java +++ b/src/com/verisignlabs/dnssec/security/DnsKeyConverter.java @@ -36,6 +36,11 @@ import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; import javax.crypto.spec.DHPrivateKeySpec; +// For now, just import the native EdDSA classes +import net.i2p.crypto.eddsa.EdDSAPublicKey; +import net.i2p.crypto.eddsa.EdDSAPrivateKey; +import net.i2p.crypto.eddsa.spec.*; + import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSSEC; import org.xbill.DNS.DNSSEC.DNSSECException; @@ -45,7 +50,7 @@ import org.xbill.DNS.utils.base64; /** * This class handles conversions between JCA key formats and DNSSEC and BIND9 * key formats. - * + * * @author David Blacka (original) * @author $Author$ (latest) * @version $Revision$ @@ -56,16 +61,17 @@ public class DnsKeyConverter private KeyFactory mDSAKeyFactory; private KeyFactory mDHKeyFactory; private KeyFactory mECKeyFactory; + private KeyFactory mEdKeyFactory; private DnsKeyAlgorithm mAlgorithms; - public DnsKeyConverter() + public DnsKeyConverter() { mAlgorithms = DnsKeyAlgorithm.getInstance(); } /** * Given a DNS KEY record, return the JCA public key - * + * * @throws NoSuchAlgorithmException */ public PublicKey parseDNSKEYRecord(DNSKEYRecord pKeyRecord) @@ -89,8 +95,20 @@ public class DnsKeyConverter pKeyRecord.getKey()); } + // do not rely on DNSJava's method for EdDSA for now. + if (mAlgorithms.baseType(originalAlgorithm) == DnsKeyAlgorithm.EDDSA) + { + try { + return parseEdDSADNSKEYRecord(pKeyRecord); + } catch (InvalidKeySpecException e) { + // just to be expedient, recast this as a NoSuchAlgorithmException. + throw new NoSuchAlgorithmException(e.getMessage()); + } + } + try { + // This uses DNSJava's DNSSEC.toPublicKey() method. return pKeyRecord.getPublicKey(); } catch (DNSSECException e) @@ -99,6 +117,19 @@ public class DnsKeyConverter } } + /** Since we don't (yet) have support in DNSJava for parsing the + newer EdDSA algorithms, here is a local version. */ + private PublicKey parseEdDSADNSKEYRecord(DNSKEYRecord pKeyRecord) + throws IllegalArgumentException, NoSuchAlgorithmException, InvalidKeySpecException + { + + EdDSAPublicKeySpec spec = new EdDSAPublicKeySpec + (pKeyRecord.getKey(), mAlgorithms.getEdwardsCurveParams(pKeyRecord.getAlgorithm())); + + KeyFactory factory = KeyFactory.getInstance("EdDSA"); + return factory.generatePublic(spec); + } + /** * Given a JCA public key and the ancillary data, generate a DNSKEY record. */ @@ -107,6 +138,9 @@ public class DnsKeyConverter { try { + if (mAlgorithms.baseType(alg) == DnsKeyAlgorithm.EDDSA) { + return generateEdDSADNSKEYRecord(name, dclass, ttl, flags, alg, key); + } return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg, key); } @@ -117,6 +151,14 @@ public class DnsKeyConverter } } + + private DNSKEYRecord generateEdDSADNSKEYRecord(Name name, int dclass, long ttl, + int flags, int alg, PublicKey key) + { + EdDSAPublicKey ed_key = (EdDSAPublicKey) key; + return new DNSKEYRecord(name, dclass, ttl, flags, DNSKEYRecord.Protocol.DNSSEC, alg, + ed_key.getEncoded()); + } // Private Key Specific Parsing routines /** @@ -204,6 +246,8 @@ public class DnsKeyConverter return parsePrivateECDSA(lines, alg); case DnsKeyAlgorithm.ECDSA: return parsePrivateECDSA(lines, alg); + case DnsKeyAlgorithm.EDDSA: + return parsePrivateEdDSA(lines, alg); default: throw new IOException("unsupported private key algorithm: " + val); } @@ -230,7 +274,7 @@ public class DnsKeyConverter /** * Given the rest of the RSA BIND9 string format private key, parse and * translate into a JCA private key - * + * * @throws NoSuchAlgorithmException * if the RSA algorithm is not available. */ @@ -319,7 +363,7 @@ public class DnsKeyConverter /** * Given the remaining lines in a BIND9 style DH private key, parse the key * info and translate it into a JCA private key. - * + * * @throws NoSuchAlgorithmException * if the DH algorithm is not available. */ @@ -375,7 +419,7 @@ public class DnsKeyConverter /** * Given the remaining lines in a BIND9 style DSA private key, parse the key * info and translate it into a JCA private key. - * + * * @throws NoSuchAlgorithmException * if the DSA algorithm is not available. */ @@ -487,6 +531,60 @@ public class DnsKeyConverter } } + /** + * Given the remaining lines in a BIND9-style ECDSA private key, parse the key + * info and translate it into a JCA private key object. + * @param lines The remaining lines in a private key file (after + * @throws NoSuchAlgorithmException + * If elliptic curve is not available. + */ + private PrivateKey parsePrivateEdDSA(StringTokenizer lines, int algorithm) + throws NoSuchAlgorithmException + { + BigInteger s = null; + + while (lines.hasMoreTokens()) + { + String line = lines.nextToken(); + if (line == null) continue; + + if (line.startsWith("#")) continue; + + String val = value(line); + if (val == null) continue; + + byte[] data = base64.fromString(val); + + if (line.startsWith("PrivateKey: ")) + { + s = new BigInteger(1, data); + } + } + + if (mEdKeyFactory == null) + { + mEdKeyFactory = KeyFactory.getInstance("EdDSA"); + } + EdDSAParameterSpec ed_spec = mAlgorithms.getEdwardsCurveParams(algorithm); + if (ed_spec == null) + { + throw new NoSuchAlgorithmException("DNSSEC algorithm " + algorithm + + " is not a recognized Edwards Curve algorithm"); + } + + KeySpec spec = new EdDSAPrivateKeySpec(s.toByteArray(), ed_spec); + + try + { + return mEdKeyFactory.generatePrivate(spec); + } + catch (InvalidKeySpecException e) + { + e.printStackTrace(); + return null; + } + } + /** * Given a private key and public key, generate the BIND9 style private key * format. @@ -509,6 +607,11 @@ public class DnsKeyConverter { return generatePrivateEC((ECPrivateKey) priv, (ECPublicKey) pub, alg); } + else if (priv instanceof EdDSAPrivateKey && pub instanceof EdDSAPublicKey) + { + return generatePrivateED((EdDSAPrivateKey) priv, (EdDSAPublicKey) pub, alg); + } + return null; } @@ -630,4 +733,22 @@ public class DnsKeyConverter return sw.toString(); } + /** + * Given an edwards curve key pair, and the actual algorithm (which will + * describe the curve used), return the BIND9-style text encoding. + */ + private String generatePrivateED(EdDSAPrivateKey priv, EdDSAPublicKey pub, int alg) + { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + + out.println("Private-key-format: v1.2"); + out.println("Algorithm: " + alg + " (" + mAlgorithms.algToString(alg) + + ")"); + out.print("PrivateKey: "); + out.println(base64.toString(priv.geta())); + + return sw.toString(); + } + } diff --git a/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java b/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java index 80067ae..ebf5dc6 100644 --- a/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java +++ b/src/com/verisignlabs/dnssec/security/JCEDnsSecSigner.java @@ -38,11 +38,11 @@ import org.xbill.DNS.utils.hexdump; /** * 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 an entire zone. It primarily glues together the more * basic primitives found in {@link SignUtils}. - * + * * @author David Blacka (original) * @author $Author$ * @version $Revision$ @@ -69,7 +69,7 @@ public class JCEDnsSecSigner /** * Cryptographically generate a new DNSSEC key. - * + * * @param owner * the KEY RR's owner name. * @param ttl @@ -114,7 +114,7 @@ public class JCEDnsSecSigner /** * Sign an RRset. - * + * * @param rrset * the RRset to sign -- any existing signatures are ignored. * @param keypars @@ -211,7 +211,7 @@ public class JCEDnsSecSigner /** * Create a completely self-signed DNSKEY RRset. - * + * * @param keypairs * the public & private keypairs to use in the keyset. * @param start @@ -244,7 +244,7 @@ public class JCEDnsSecSigner /** * Conditionally sign an RRset and add it to the toList. - * + * * @param toList * the list to which we are adding the processed RRsets. * @param zonename @@ -263,7 +263,7 @@ public class JCEDnsSecSigner * 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. */ @SuppressWarnings("unchecked") @@ -324,7 +324,7 @@ public class JCEDnsSecSigner * signing variants (NSEC with or without Opt-In, NSEC3 with or without * Opt-Out, etc.) External users of this class are expected to use the * appropriate public signZone* methods instead of this. - * + * * @param zonename * The name of the zone * @param records @@ -363,7 +363,7 @@ public class JCEDnsSecSigner * values will use the SOA TTL. * @return an ordered list of {@link org.xbill.DNS.Record} objects, * representing the signed zone. - * + * * @throws IOException * @throws GeneralSecurityException */ @@ -459,7 +459,7 @@ public class JCEDnsSecSigner /** * Given a zone, sign it using standard NSEC records. - * + * * @param zonename * The name of the zone. * @param records @@ -478,7 +478,7 @@ public class JCEDnsSecSigner * the key signing keys). * @param ds_digest_alg * The digest algorithm to use when generating DS records. - * + * * @return an ordered list of {@link org.xbill.DNS.Record} objects, * representing the signed zone. */ @@ -494,7 +494,7 @@ public class JCEDnsSecSigner /** * Given a zone, sign it using NSEC3 records. - * + * * @param signer * A signer (utility) object used to actually sign stuff. * @param zonename @@ -529,7 +529,7 @@ public class JCEDnsSecSigner * values will use the SOA TTL. * @return an ordered list of {@link org.xbill.DNS.Record} objects, * representing the signed zone. - * + * * @throws IOException * @throws GeneralSecurityException */ @@ -558,7 +558,7 @@ public class JCEDnsSecSigner /** * Given a zone, sign it using experimental Opt-In NSEC records (see RFC * 4956). - * + * * @param zonename * the name of the zone. * @param records