diff options
author | Hakan Olsson <ho@cvs.openbsd.org> | 2001-08-15 12:31:41 +0000 |
---|---|---|
committer | Hakan Olsson <ho@cvs.openbsd.org> | 2001-08-15 12:31:41 +0000 |
commit | 15f376e0ffd6cd5bed12a1222d04f52774910dd4 (patch) | |
tree | daa76b5d17cf9ddaf568771872b3bc1d4ec1dcfd /sbin/isakmpd/apps | |
parent | cf2c55efac4edd0ddee07ecc3ea939a000d05fdb (diff) |
A small utility to convert between OpenSSL(1) and DNSSEC key formats.
Diffstat (limited to 'sbin/isakmpd/apps')
-rw-r--r-- | sbin/isakmpd/apps/keyconv/Makefile | 51 | ||||
-rw-r--r-- | sbin/isakmpd/apps/keyconv/base64.c | 314 | ||||
-rw-r--r-- | sbin/isakmpd/apps/keyconv/keyconv.c | 762 |
3 files changed, 1127 insertions, 0 deletions
diff --git a/sbin/isakmpd/apps/keyconv/Makefile b/sbin/isakmpd/apps/keyconv/Makefile new file mode 100644 index 00000000000..70e7880a19d --- /dev/null +++ b/sbin/isakmpd/apps/keyconv/Makefile @@ -0,0 +1,51 @@ +# $Id: Makefile,v 1.1 2001/08/15 12:31:40 ho Exp $ + +# +# Copyright (c) 2000 Håkan Olsson. 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 conditions and the following disclaimer in the +# documentation and/or other 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 NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF 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. +# + +PROG= keyconv +SRCS= keyconv.c base64.c +BINDIR= /usr/sbin + +TOPSRC= ${.CURDIR}/../.. +TOPOBJ!= cd ${TOPSRC}; printf "all:\n\t@pwd\n" |${MAKE} -f- +OS!= awk '/^OS=/ { print $$2 }' ${.CURDIR}/../../Makefile +.PATH: ${TOPSRC} ${TOPSRC}/sysdep/${OS} ${TOPOBJ} +CFLAGS+= -I${TOPSRC} -I${TOPSRC}/sysdep/${OS} -I${TOPOBJ} -Wall +CFLAGS+= -I/usr/local/include +LDADD+= -lcrypto ${LIBLWRES} +DPADD+= ${LIBCRYPTO} +NOMAN= yes + +.include "${TOPSRC}/sysdep/${OS}/Makefile.sysdep" +# Override LIBSYSDEPDIR definition from Makefile.sysdep +LIBSYSDEPDIR= ${TOPSRC}/sysdep/common/libsysdep + +.include <bsd.prog.mk> + +# Mainly during devel +realclean: clean + rm -f ${.CURDIR}/*.{pem,private,key} diff --git a/sbin/isakmpd/apps/keyconv/base64.c b/sbin/isakmpd/apps/keyconv/base64.c new file mode 100644 index 00000000000..0449fa899b3 --- /dev/null +++ b/sbin/isakmpd/apps/keyconv/base64.c @@ -0,0 +1,314 @@ +/* $Id: base64.c,v 1.1 2001/08/15 12:31:40 ho Exp $ */ +/* $OpenBSD: base64.c,v 1.1 2001/08/15 12:31:40 ho Exp $ */ + +/* + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$ISC: base64.c,v 8.6 1999/01/08 19:25:18 vixie Exp $"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(src, target, targsize) + char const *src; + u_char *target; + size_t targsize; +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/sbin/isakmpd/apps/keyconv/keyconv.c b/sbin/isakmpd/apps/keyconv/keyconv.c new file mode 100644 index 00000000000..4dab4190595 --- /dev/null +++ b/sbin/isakmpd/apps/keyconv/keyconv.c @@ -0,0 +1,762 @@ +/* $Id: keyconv.c,v 1.1 2001/08/15 12:31:40 ho Exp $ */ + +/* + * Copyright (c) 2000 Hakan Olsson. 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 conditions and the following disclaimer in the + * documentation and/or other 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 NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> + +#include <dns/keyvalues.h> +#include <lwres/lwres.h> +#include <lwres/netdb.h> + +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <openssl/dh.h> +#include <openssl/pem.h> + +/* base64.c prototypes */ +int b64_pton (const char *, u_char *, size_t); +int b64_ntop (const u_char *, size_t, char *, size_t); + +#define OPENSSL_DEFAULT_EXT ".pem" +#define DNSSEC_DEFAULT_EXT ".private" + +/* DNSSEC private key format version */ +#define MAJOR_VERSION 1 +#define MINOR_VERSION 2 + +char *progname; + +/* For DH calc. */ +BIGNUM bn_b2, bn_oakley1, bn_oakley2, bn_oakley5; + +void +usage_exit (void) +{ + fprintf (stderr, "Usage: %s infile outfile\n\n", progname); + fprintf (stderr, "Extension of infile / outfile gives mode of operation.\n"); + fprintf (stderr, "Recognized extensions = %s %s\n\n", OPENSSL_DEFAULT_EXT, + DNSSEC_DEFAULT_EXT); + fprintf (stderr, "Example:\n"); + fprintf (stderr, " %s test%s test%s\n", progname, DNSSEC_DEFAULT_EXT, + OPENSSL_DEFAULT_EXT); + fprintf (stderr, " - converts from DNSSEC to OPENSSL format.\n"); + exit (1); +} + +/* + * Ok, I dont have the patience to do everything "by the book" here, so + * I'll use some shortcuts... + */ + +static const char *dns_tags[] = +{ + "Private-key-format:", "Algorithm:", "Modulus:", "PublicExponent:", + "PrivateExponent:", "Prime1:", "Prime2:", "Exponent1:", "Exponent2:", + "Coefficient:", "Generator(g):", "Prime(p):", "Private_value(x):", + "Public_value(y):", "Subprime(q):", "Base(g):", "Key:", + NULL +}; + +/* Must match the above */ +enum DNS_TAGS +{ TAG_VERSION, TAG_ALGORITHM, TAG_MODULUS, TAG_PUBEXP, TAG_PRIVEXP, + TAG_PRIME1, TAG_PRIME2, TAG_EXP1, TAG_EXP2, TAG_COEFF, TAG_GENERATOR, + TAG_PRIME, TAG_PRIVVAL, TAG_PUBVAL, TAG_SUBPRIME, TAG_BASE, TAG_KEY }; + +#define MAX_LINE 4096 + +char * +hex2bin (char *h, int *len) +{ + static char b[MAX_LINE]; + const char hex[] = "0123456789abcdef0123456789ABCDEF"; + u_int8_t c; + char *n; + int p; + + for (p = 0; p < strlen (h); p += 2) + { + n = strchr (hex, *(h + p)); + if (!n) + { + printf ("D1: %d (%c)\n", *(h+p), *(h+p)); + fprintf (stderr, "hex2bin: bad input data!\n"); + exit (1); + } + c = ((char)(n - hex) & 0xF) << 4; + n = strchr (hex, *(h + p + 1)); + if (!n) + { + printf ("D2: %d (%c)\n", *(h+p+1), *(h+p+1)); + fprintf (stderr, "hex2bin: bad input data!\n"); + exit (1); + } + c |= ((char)(n - hex)) & 0xF; + *(b + (p/2)) = (char)c; + } + *(b + (p/2)) = (char)0; + *len = p/2; + return b; +} + +void +init_DH_constants (void) +{ +#define MODP_768 \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"\ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"\ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"\ + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" + +#define MODP_1024 \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ + "FFFFFFFFFFFFFFFF" + +#define MODP_1536 \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" + + int len; + char *t; + + BN_init (&bn_b2); + BN_init (&bn_oakley1); + BN_init (&bn_oakley2); + BN_init (&bn_oakley5); + BN_set_word (&bn_b2, 2); + t = hex2bin (MODP_768, &len); BN_bin2bn (t, len, &bn_oakley1); + t = hex2bin (MODP_1024, &len); BN_bin2bn (t, len, &bn_oakley2); + t = hex2bin (MODP_1536, &len); BN_bin2bn (t, len, &bn_oakley5); +} + +int +convert_dns_to_openssl (char *file_in, char *file_out) +{ + FILE *ii, *oo; + char line[MAX_LINE], data[MAX_LINE], *valuestr; + DH *dh; + RSA *rsa; + DSA *dsa; + BIGNUM *bn; + int lineno = 0, algorithm = 0, m, n, dh_code; + enum DNS_TAGS found; + u_int32_t bitfield = 0; + + /* Try to open input file */ + ii = fopen (file_in, "r"); + if (!ii) + { + fprintf (stderr, "Cannot open input file %.100s for reading\n", file_in); + return 1; + } + + /* Avoid compiler noises. */ + dh = NULL; rsa = NULL; dsa = NULL; bn = NULL; + + while (fgets (line, MAX_LINE, ii) != NULL) + { + lineno ++; + + /* Try to find a matching field in the *.private file (dnssec-keygen) */ + for (m = 0, found = -1; found == -1 && dns_tags[m]; m++) + if (strncasecmp (line, dns_tags[m], strlen (dns_tags[m])) == 0) + found = m; + + if (found == -1) + { + fprintf (stderr, "Unrecognized input line %d\n", lineno); + fprintf (stderr, " --> %s", line); + continue; + } + + valuestr = line + strlen (dns_tags[found]) + 1; + + if (found == TAG_VERSION) + { + if (sscanf (valuestr, "v%d.%d", &m, &n) != 2) + { + fprintf (stderr, "Invalid/unknown version string (%.100s).\n", + valuestr); + return 1; + } + if (m > MAJOR_VERSION || (m == MAJOR_VERSION && n > MINOR_VERSION)) + { + fprintf (stderr, "Cannot parse file formats above v%d.%d. " + "This is v%d.%d.\n", MAJOR_VERSION, MINOR_VERSION, m, + n); + return 1; + } + continue; + } + + if (found == TAG_ALGORITHM) + { + algorithm = strtol (valuestr, NULL, 10); + switch (algorithm) + { + case DNS_KEYALG_RSAMD5: + rsa = RSA_new (); + rsa->flags &= ~(RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE); + break; + case DNS_KEYALG_DH: + dh = DH_new(); + dh->flags &= ~DH_FLAG_CACHE_MONT_P; + break; + case DNS_KEYALG_DSA: + dsa = DSA_new(); + dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; + break; + default: + fprintf (stderr, "Invalid algorithm %d\n", algorithm); + return 1; + } + continue; + } + + switch (algorithm) + { + case DNS_KEYALG_RSAMD5: + if (found > TAG_COEFF) + { + fprintf (stderr, "Invalid keyword for RSA, line %d\n", lineno); + continue; + } + break; + case DNS_KEYALG_DH: + if (found < TAG_GENERATOR || found > TAG_PUBVAL) + { + fprintf (stderr, "Invalid keyword for DH, line %d\n", lineno); + continue; + } + break; + case DNS_KEYALG_DSA: + if (found < TAG_PRIME || found > TAG_BASE) + { + fprintf (stderr, "Invalid keyword for DSA, line %d\n", lineno); + continue; + } + break; + default: + /* We should never reach this unless the file format is invalid. */ + fprintf (stderr, "Invalid file format, no algorithm specified.\n"); + return 1; + } + + /* Ok, now we just have to deal with the fields as they appear. */ + + if (bitfield & (1 << found)) + { + fprintf (stderr, "Duplicate \"%s\" field, line %d - ignored\n", + dns_tags[found], lineno); + continue; /* XXX return 1; ? */ + } + bitfield |= (1 << found); + + m = b64_pton (valuestr, data, MAX_LINE); + bn = BN_bin2bn (data, m, NULL); + if (!bn) + { + fprintf (stderr, + "BN_bin2bn() failed for field \"%s\", line %d.\n", + dns_tags[found], lineno); + return 1; + } + + switch (found) + { + case TAG_MODULUS: /* RSA modulus */ + rsa->n = bn; + break; + case TAG_PUBEXP: /* RSA public exponent */ + rsa->e = bn; + break; + case TAG_PRIVEXP: /* RSA private exponent */ + rsa->d = bn; + break; + case TAG_PRIME1: /* RSA prime 1 */ + rsa->p = bn; + break; + case TAG_PRIME2: /* RSA prime 2 */ + rsa->q = bn; + break; + case TAG_EXP1: /* RSA exponent 1 */ + rsa->dmp1 = bn; + break; + case TAG_EXP2: /* RSA exponent 2 */ + rsa->dmq1 = bn; + break; + case TAG_COEFF: /* RSA coefficient */ + rsa->iqmp = bn; + break; + + case TAG_GENERATOR: /* DH generator(g) */ + dh->g = bn; + break; + + case TAG_PRIME: /* DH/DSA prime(p) */ + if (algorithm == DNS_KEYALG_DH) + dh->p = bn; + else + dsa->p = bn; + break; + + case TAG_PUBVAL: /* DH/DSA private-value(x) */ + if (algorithm == DNS_KEYALG_DH) + dh->pub_key = bn; + else + dsa->pub_key = bn; + break; + + case TAG_PRIVVAL: /* DH/DSA public-value(y) */ + if (algorithm == DNS_KEYALG_DH) + dh->priv_key = bn; + else + dsa->priv_key = bn; + break; + + case TAG_SUBPRIME: /* DSA Subprime(q) */ + dsa->q = bn; + break; + case TAG_BASE: /* DSA Base(g) */ + dsa->g = bn; + break; + + default: + break; + } + } + fclose (ii); + + /* Check results */ + switch (algorithm) + { + case DNS_KEYALG_RSAMD5: + m = (RSA_check_key (rsa) < 1 ? 0 : 1); + break; + case DNS_KEYALG_DH: + dh_code = 0; + m = DH_check (dh, &dh_code); + break; + case DNS_KEYALG_DSA: + /* No check available. */ + default: + m = -1; + break; + } + + if (m == 1) + fprintf (stderr, "Key succeeded validation check.\n"); + else if (m == 0) + { + fprintf (stderr, "Key failed validation check.\n"); + return 1; + } + + oo = fopen (file_out, "w"); + if (!oo) + fprintf (stderr, "Cannot open output file %.100s\n", file_out); + else + fprintf (stderr, "Writing output file \"%s\".\n", file_out); + + /* Write result and cleanup */ + switch (algorithm) + { + case DNS_KEYALG_RSAMD5: + if (oo) + PEM_write_RSAPrivateKey (oo, rsa, NULL, NULL, 0, NULL, NULL); + RSA_free (rsa); + break; + + case DNS_KEYALG_DH: + if (oo) + PEM_write_DHparams (oo, dh); + DH_free (dh); + break; + + case DNS_KEYALG_DSA: + if (oo) + PEM_write_DSAPrivateKey (oo, dsa, NULL, NULL, 0, NULL, NULL); + DSA_free (dsa); + break; + + default: + break; + } + + if (oo) + fclose (oo); + return 0; +} + +char * +bn_to_b64 (const BIGNUM *bn) +{ + static char b64data[MAX_LINE]; + char data[MAX_LINE]; + int dlen, blen; + + if (!bn) + { + blen = 0; + goto out; + } + + dlen = BN_bn2bin (bn, data); + blen = b64_ntop (data, dlen, b64data, MAX_LINE); + + out: + b64data[blen] = (char) 0; + return b64data; +} + +void +write_RSA_public_key (char *fname, RSA *rsa) +{ + FILE *out; + int e_bytes, mod_bytes, dlen, blen; + char data[MAX_LINE], b64buf[MAX_LINE]; + + out = fopen (fname, "w"); + if (!out) + { + fprintf (stderr, "Couldn't open public key file for writing.\n"); + return; + } + + fprintf (stderr, "Writing output file \"%.100s\".\n", fname); + dlen = 0; + + e_bytes = BN_num_bytes (rsa->e); + mod_bytes = BN_num_bytes (rsa->n); + + fprintf (out, ";name IN KEY %d %d %d ", DNS_KEYOWNER_ENTITY, + DNS_KEYPROTO_IPSEC, DNS_KEYALG_RSAMD5); + + if (e_bytes <= 0xFF) + data[dlen++] = (char)e_bytes; + else + { + data[dlen++] = (char)0; + data[dlen++] = (char)(e_bytes >> 8); + data[dlen++] = (char)(e_bytes & 0xFF); + } + dlen += BN_bn2bin (rsa->e, data + dlen); + dlen += BN_bn2bin (rsa->n, data + dlen); + + blen = b64_ntop (data, dlen, b64buf, MAX_LINE); + b64buf[blen] = (char)0; + + fprintf (out, "%s\n", b64buf); + fclose (out); +} + +void +write_DSA_public_key (char *fname, DSA *dsa) +{ + FILE *out; + int p_bytes, dlen, blen, p; + char data[MAX_LINE], b64buf[MAX_LINE]; + + out = fopen (fname, "w"); + if (!out) + { + fprintf (stderr, "Couldn't open public key file for writing.\n"); + return; + } + + fprintf (stderr, "Writing output file \"%.100s\".\n", fname); + + p_bytes = BN_num_bytes (dsa->p); + if (((p_bytes - 64) / 8) > 8) + { + fprintf (stderr, "Invalid DSA key.\n"); + fclose (out); + return; + } + + dlen = blen = 0; + *(data + dlen++) = (p_bytes - 64) / 8; + + /* Fields in DSA public key are zero-padded (left) */ +#define PAD_ADD(var,len) \ + for (p = 0; p < (len - BN_num_bytes (var)); p++) \ + *(data + dlen++) = (char)0; \ + BN_bn2bin (var, data + dlen); \ + dlen += BN_num_bytes (var) + + PAD_ADD (dsa->q, SHA_DIGEST_LENGTH); + PAD_ADD (dsa->p, p_bytes); + PAD_ADD (dsa->g, p_bytes); + PAD_ADD (dsa->pub_key, p_bytes); + + blen = b64_ntop (data, dlen, b64buf, MAX_LINE); + b64buf[blen] = (char)0; + + fprintf (out, ";name IN KEY %d %d %d %s\n", DNS_KEYOWNER_ENTITY, + DNS_KEYPROTO_IPSEC, DNS_KEYALG_DSA, b64buf); + fclose (out); +} + +void +write_DH_public_key (char *fname, DH *dh) +{ + FILE *out; + int dlen, blen, p, g; + char data[MAX_LINE], b64buf[MAX_LINE]; + + out = fopen (fname, "w"); + if (!out) + { + fprintf (stderr, "Couldn't open public key file for writing.\n"); + return; + } + + fprintf (stderr, "Writing output file \"%.100s\".\n", fname); + + dlen = p = g = 0; + if (BN_cmp (dh->g, &bn_b2) == 0) + { + p = 1; + if (BN_cmp (dh->p, &bn_oakley1) == 0) + *(data + dlen++) = (char)1; + else if (BN_cmp (dh->p, &bn_oakley2) == 0) + *(data + dlen++) = (char)2; + else if (BN_cmp (dh->p, &bn_oakley5) == 0) + *(data + dlen++) = (char)5; + else + p = 0; + } + + if (p == 0) + { + p = BN_num_bytes (dh->p); + g = BN_num_bytes (dh->g); + dlen += BN_bn2bin (dh->p, data + dlen); + if (g > 0) + dlen += BN_bn2bin (dh->p, data + dlen); + } + + dlen += BN_bn2bin (dh->pub_key, data + dlen); + + blen = b64_ntop (data, dlen, b64buf, MAX_LINE); + b64buf[blen] = (char)0; + + fprintf (out, ";name IN KEY %d %d %d %s\n", DNS_KEYOWNER_ENTITY, + DNS_KEYPROTO_IPSEC, DNS_KEYALG_DH, b64buf); + + fclose (out); +} + +int +convert_openssl_to_dns (char *file_in, char *file_out) +{ + FILE *ii, *oo; + RSA *rsa; + DSA *dsa; + DH *dh; + enum DNS_TAGS tag; + char *pubname; + + rsa = NULL; + dsa = NULL; + dh = NULL; + + ii = fopen (file_in, "r"); + if (!ii) + { + fprintf (stderr, "Cannot open input file %.100s for reading\n", file_in); + return 1; + } + + /* Try to load PEM */ + rsa = PEM_read_RSAPrivateKey (ii, NULL, NULL, NULL); + if (!rsa) + { + rewind (ii); + dsa = PEM_read_DSAPrivateKey (ii, NULL, NULL, NULL); + if (!dsa) + { + rewind (ii); + dh = PEM_read_DHparams (ii, NULL, NULL, NULL); + if (!dh) + { + fprintf (stderr, "Unrecognized infile format.\n"); + fclose (ii); + return 1; + } + } + } + fclose (ii); + + oo = fopen (file_out, "w"); + if (!oo) + { + fprintf (stderr, "Cannot open output file %.100s\n", file_out); + return 1; + } + + pubname = strdup (file_out); + sprintf (pubname + strlen (pubname) - 7, "key"); + + fprintf (stderr, "Writing output file \"%s\".\n", file_out); + fprintf (oo, "%s v%d.%d\n", dns_tags[TAG_VERSION], MAJOR_VERSION, + MINOR_VERSION); + + /* XXX Algorithm dependent validation checks? */ + + if (rsa) + { + /* Write private key */ + fprintf (oo, "%s %d (%s)\n", dns_tags[TAG_ALGORITHM], + DNS_KEYALG_RSAMD5, "RSA"); + tag = TAG_MODULUS; + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->n)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->e)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->d)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->p)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->q)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->dmp1)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->dmq1)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (rsa->iqmp)); + + write_RSA_public_key (pubname, rsa); + } + else if (dsa) + { + /* DSA_print_fp (stdout, dsa, 2); */ + + fprintf (oo, "%s %d (%s)\n", dns_tags[TAG_ALGORITHM], + DNS_KEYALG_DSA, "DSA"); + tag = TAG_PRIME; + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dsa->p)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dsa->priv_key)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dsa->pub_key)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dsa->q)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dsa->g)); + + write_DSA_public_key (pubname, dsa); + } + else if (dh) + { + /* + * OpenSSL never stored private and public key values, so + * we have to regenerate them from p and g. + */ + if (DH_generate_key (dh) == 0) + { + fprintf (stderr, "DH key generation failed\n"); + fclose (oo); + unlink (file_out); + return 1; + } + + fprintf (oo, "%s %d (%s)\n", dns_tags[TAG_ALGORITHM], + DNS_KEYALG_DH, "DH"); + tag = TAG_GENERATOR; + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dh->g)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dh->p)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dh->priv_key)); + fprintf (oo, "%s %s\n", dns_tags[tag++], bn_to_b64 (dh->pub_key)); + + write_DH_public_key (pubname, dh); + } + else + { + fprintf (stderr, "PEM_read_PrivateKey() read unknown data.\n"); + fclose (oo); + unlink (file_out); + return 1; + } + + fclose (oo); + return 0; +} + +int +main (int argc, char **argv) +{ + char *p; + int ret, dns_to_openssl = -1; + + progname = argv[0]; + + if (argc != 3) + usage_exit (); + + /* Check input syntax */ + for (p = argv[1] + strlen (argv[1]); p > argv[1] && *p != '.'; p--) ; + if (*p != '.') + usage_exit (); + + if (strlen (p) == 4 && strcasecmp (p, OPENSSL_DEFAULT_EXT) == 0) + dns_to_openssl = 0; + else if (strlen (p) == 8 && strcasecmp (p, DNSSEC_DEFAULT_EXT) == 0) + dns_to_openssl = 1; + else + usage_exit (); + + /* Check second argument */ + for (p = argv[2] + strlen (argv[2]); p > argv[2] && *p != '.'; p--) ; + if (*p != '.') + usage_exit (); + + if (dns_to_openssl) + { + if (strlen (p) != 4 || strcasecmp (p, OPENSSL_DEFAULT_EXT) != 0) + usage_exit (); + } + else + if (strlen (p) != 8 || strcasecmp (p, DNSSEC_DEFAULT_EXT) != 0) + usage_exit (); + + init_DH_constants (); + + /* Convert. */ + if (dns_to_openssl) + ret = convert_dns_to_openssl (argv[1], argv[2]); + else + ret = convert_openssl_to_dns (argv[1], argv[2]); + + if (ret) + fprintf (stderr, "Operation failed.\n"); + + return 0; +} |