summaryrefslogtreecommitdiff
path: root/src/pluto/pkcs1.c
diff options
context:
space:
mode:
authorRene Mayrhofer <rene@mayrhofer.eu.org>2007-04-12 20:30:08 +0000
committerRene Mayrhofer <rene@mayrhofer.eu.org>2007-04-12 20:30:08 +0000
commitb0d8ed94fe9e74afb49fdf5f11e4add29879c65c (patch)
treeb20167235628771046e940a82a906a6d0991ee4a /src/pluto/pkcs1.c
parentea939d07c84d2a8e51215458063fc05e9c399290 (diff)
downloadvyos-strongswan-b0d8ed94fe9e74afb49fdf5f11e4add29879c65c.tar.gz
vyos-strongswan-b0d8ed94fe9e74afb49fdf5f11e4add29879c65c.zip
[svn-upgrade] Integrating new upstream version, strongswan (4.1.1)
Diffstat (limited to 'src/pluto/pkcs1.c')
-rw-r--r--src/pluto/pkcs1.c674
1 files changed, 674 insertions, 0 deletions
diff --git a/src/pluto/pkcs1.c b/src/pluto/pkcs1.c
new file mode 100644
index 000000000..ade5fdd94
--- /dev/null
+++ b/src/pluto/pkcs1.c
@@ -0,0 +1,674 @@
+/* Support of PKCS#1 private key data structures
+ * Copyright (C) 2005 Jan Hutter, Martin Willi
+ * Copyright (C) 2002-2005 Andreas Steffen
+ * Hochschule fuer Technik Rapperswil, Switzerland
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id: pkcs1.c,v 1.17 2006/01/04 21:00:43 as Exp $
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+#include <libsha2/sha2.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "mp_defs.h"
+#include "asn1.h"
+#include "oid.h"
+#include "log.h"
+#include "pkcs1.h"
+#include "md2.h"
+#include "md5.h"
+#include "sha1.h"
+#include "rnd.h"
+
+const struct fld RSA_private_field[] =
+{
+ { "Modulus", offsetof(RSA_private_key_t, pub.n) },
+ { "PublicExponent", offsetof(RSA_private_key_t, pub.e) },
+
+ { "PrivateExponent", offsetof(RSA_private_key_t, d) },
+ { "Prime1", offsetof(RSA_private_key_t, p) },
+ { "Prime2", offsetof(RSA_private_key_t, q) },
+ { "Exponent1", offsetof(RSA_private_key_t, dP) },
+ { "Exponent2", offsetof(RSA_private_key_t, dQ) },
+ { "Coefficient", offsetof(RSA_private_key_t, qInv) },
+};
+
+/* ASN.1 definition of a PKCS#1 RSA private key */
+
+static const asn1Object_t privkeyObjects[] = {
+ { 0, "RSAPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */
+ { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 2 */
+ { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 3 */
+ { 1, "privateExponent", ASN1_INTEGER, ASN1_BODY }, /* 4 */
+ { 1, "prime1", ASN1_INTEGER, ASN1_BODY }, /* 5 */
+ { 1, "prime2", ASN1_INTEGER, ASN1_BODY }, /* 6 */
+ { 1, "exponent1", ASN1_INTEGER, ASN1_BODY }, /* 7 */
+ { 1, "exponent2", ASN1_INTEGER, ASN1_BODY }, /* 8 */
+ { 1, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 9 */
+ { 1, "otherPrimeInfos", ASN1_SEQUENCE, ASN1_OPT |
+ ASN1_LOOP }, /* 10 */
+ { 2, "otherPrimeInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 11 */
+ { 3, "prime", ASN1_INTEGER, ASN1_BODY }, /* 12 */
+ { 3, "exponent", ASN1_INTEGER, ASN1_BODY }, /* 13 */
+ { 3, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 14 */
+ { 1, "end opt or loop", ASN1_EOC, ASN1_END } /* 15 */
+};
+
+#define PKCS1_PRIV_KEY_VERSION 1
+#define PKCS1_PRIV_KEY_MODULUS 2
+#define PKCS1_PRIV_KEY_PUB_EXP 3
+#define PKCS1_PRIV_KEY_COEFF 9
+#define PKCS1_PRIV_KEY_ROOF 16
+
+
+/*
+ * forms the FreeS/WAN keyid from the public exponent e and modulus n
+ */
+void
+form_keyid(chunk_t e, chunk_t n, char* keyid, unsigned *keysize)
+{
+ /* eliminate leading zero bytes in modulus from ASN.1 coding */
+ while (n.len > 1 && *n.ptr == 0x00)
+ {
+ n.ptr++; n.len--;
+ }
+
+ /* form the FreeS/WAN keyid */
+ keyid[0] = '\0'; /* in case of splitkeytoid failure */
+ splitkeytoid(e.ptr, e.len, n.ptr, n.len, keyid, KEYID_BUF);
+
+ /* return the RSA modulus size in octets */
+ *keysize = n.len;
+}
+
+/*
+ * initialize an RSA_public_key_t object
+ */
+void
+init_RSA_public_key(RSA_public_key_t *rsa, chunk_t e, chunk_t n)
+{
+ n_to_mpz(&rsa->e, e.ptr, e.len);
+ n_to_mpz(&rsa->n, n.ptr, n.len);
+
+ form_keyid(e, n, rsa->keyid, &rsa->k);
+}
+
+#ifdef DEBUG
+static void
+RSA_show_key_fields(RSA_private_key_t *k, int fieldcnt)
+{
+ const struct fld *p;
+
+ DBG_log(" keyid: *%s", k->pub.keyid);
+
+ for (p = RSA_private_field; p < &RSA_private_field[fieldcnt]; p++)
+ {
+ MP_INT *n = (MP_INT *) ((char *)k + p->offset);
+ size_t sz = mpz_sizeinbase(n, 16);
+ char buf[RSA_MAX_OCTETS * 2 + 2]; /* ought to be big enough */
+
+ passert(sz <= sizeof(buf));
+ mpz_get_str(buf, 16, n);
+
+ DBG_log(" %s: 0x%s", p->name, buf);
+ }
+}
+
+/* debugging info that compromises security! */
+void
+RSA_show_private_key(RSA_private_key_t *k)
+{
+ RSA_show_key_fields(k, elemsof(RSA_private_field));
+}
+
+void
+RSA_show_public_key(RSA_public_key_t *k)
+{
+ /* Kludge: pretend that it is a private key, but only display the
+ * first two fields (which are the public key).
+ */
+ passert(offsetof(RSA_private_key_t, pub) == 0);
+ RSA_show_key_fields((RSA_private_key_t *)k, 2);
+}
+#endif
+
+err_t
+RSA_private_key_sanity(RSA_private_key_t *k)
+{
+ /* note that the *last* error found is reported */
+ err_t ugh = NULL;
+ mpz_t t, u, q1;
+
+#ifdef DEBUG /* debugging info that compromises security */
+ DBG(DBG_PRIVATE, RSA_show_private_key(k));
+#endif
+
+ /* PKCS#1 1.5 section 6 requires modulus to have at least 12 octets.
+ * We actually require more (for security).
+ */
+ if (k->pub.k < RSA_MIN_OCTETS)
+ return RSA_MIN_OCTETS_UGH;
+
+ /* we picked a max modulus size to simplify buffer allocation */
+ if (k->pub.k > RSA_MAX_OCTETS)
+ return RSA_MAX_OCTETS_UGH;
+
+ mpz_init(t);
+ mpz_init(u);
+ mpz_init(q1);
+
+ /* check that n == p * q */
+ mpz_mul(u, &k->p, &k->q);
+ if (mpz_cmp(u, &k->pub.n) != 0)
+ ugh = "n != p * q";
+
+ /* check that e divides neither p-1 nor q-1 */
+ mpz_sub_ui(t, &k->p, 1);
+ mpz_mod(t, t, &k->pub.e);
+ if (mpz_cmp_ui(t, 0) == 0)
+ ugh = "e divides p-1";
+
+ mpz_sub_ui(t, &k->q, 1);
+ mpz_mod(t, t, &k->pub.e);
+ if (mpz_cmp_ui(t, 0) == 0)
+ ugh = "e divides q-1";
+
+ /* check that d is e^-1 (mod lcm(p-1, q-1)) */
+ /* see PKCS#1v2, aka RFC 2437, for the "lcm" */
+ mpz_sub_ui(q1, &k->q, 1);
+ mpz_sub_ui(u, &k->p, 1);
+ mpz_gcd(t, u, q1); /* t := gcd(p-1, q-1) */
+ mpz_mul(u, u, q1); /* u := (p-1) * (q-1) */
+ mpz_divexact(u, u, t); /* u := lcm(p-1, q-1) */
+
+ mpz_mul(t, &k->d, &k->pub.e);
+ mpz_mod(t, t, u);
+ if (mpz_cmp_ui(t, 1) != 0)
+ ugh = "(d * e) mod (lcm(p-1, q-1)) != 1";
+
+ /* check that dP is d mod (p-1) */
+ mpz_sub_ui(u, &k->p, 1);
+ mpz_mod(t, &k->d, u);
+ if (mpz_cmp(t, &k->dP) != 0)
+ ugh = "dP is not congruent to d mod (p-1)";
+
+ /* check that dQ is d mod (q-1) */
+ mpz_sub_ui(u, &k->q, 1);
+ mpz_mod(t, &k->d, u);
+ if (mpz_cmp(t, &k->dQ) != 0)
+ ugh = "dQ is not congruent to d mod (q-1)";
+
+ /* check that qInv is (q^-1) mod p */
+ mpz_mul(t, &k->qInv, &k->q);
+ mpz_mod(t, t, &k->p);
+ if (mpz_cmp_ui(t, 1) != 0)
+ ugh = "qInv is not conguent ot (q^-1) mod p";
+
+ mpz_clear(t);
+ mpz_clear(u);
+ mpz_clear(q1);
+ return ugh;
+}
+
+/*
+ * Check the equality of two RSA public keys
+ */
+bool
+same_RSA_public_key(const RSA_public_key_t *a, const RSA_public_key_t *b)
+{
+ return a == b
+ || (a->k == b->k && mpz_cmp(&a->n, &b->n) == 0 && mpz_cmp(&a->e, &b->e) == 0);
+}
+
+/*
+ * Parses a PKCS#1 private key
+ */
+bool
+pkcs1_parse_private_key(chunk_t blob, RSA_private_key_t *key)
+{
+ err_t ugh = NULL;
+ asn1_ctx_t ctx;
+ chunk_t object, modulus, exp;
+ u_int level;
+ int objectID = 0;
+
+ asn1_init(&ctx, blob, 0, FALSE, DBG_PRIVATE);
+
+ while (objectID < PKCS1_PRIV_KEY_ROOF) {
+
+ if (!extract_object(privkeyObjects, &objectID, &object, &level, &ctx))
+ return FALSE;
+
+ if (objectID == PKCS1_PRIV_KEY_VERSION)
+ {
+ if (object.len > 0 && *object.ptr != 0)
+ {
+ plog(" wrong PKCS#1 private key version");
+ return FALSE;
+ }
+ }
+ else if (objectID >= PKCS1_PRIV_KEY_MODULUS &&
+ objectID <= PKCS1_PRIV_KEY_COEFF)
+ {
+ MP_INT *u = (MP_INT *) ((char *)key
+ + RSA_private_field[objectID - PKCS1_PRIV_KEY_MODULUS].offset);
+
+ n_to_mpz(u, object.ptr, object.len);
+
+ if (objectID == PKCS1_PRIV_KEY_MODULUS)
+ modulus = object;
+ else if (objectID == PKCS1_PRIV_KEY_PUB_EXP)
+ exp = object;
+ }
+ objectID++;
+ }
+ form_keyid(exp, modulus, key->pub.keyid, &key->pub.k);
+ ugh = RSA_private_key_sanity(key);
+ return (ugh == NULL);
+}
+
+/*
+ * compute a digest over a binary blob
+ */
+bool
+compute_digest(chunk_t tbs, int alg, chunk_t *digest)
+{
+ switch (alg)
+ {
+ case OID_MD2:
+ case OID_MD2_WITH_RSA:
+ {
+ MD2_CTX context;
+
+ MD2Init(&context);
+ MD2Update(&context, tbs.ptr, tbs.len);
+ MD2Final(digest->ptr, &context);
+ digest->len = MD2_DIGEST_SIZE;
+ return TRUE;
+ }
+ case OID_MD5:
+ case OID_MD5_WITH_RSA:
+ {
+ MD5_CTX context;
+
+ MD5Init(&context);
+ MD5Update(&context, tbs.ptr, tbs.len);
+ MD5Final(digest->ptr, &context);
+ digest->len = MD5_DIGEST_SIZE;
+ return TRUE;
+ }
+ case OID_SHA1:
+ case OID_SHA1_WITH_RSA:
+ case OID_SHA1_WITH_RSA_OIW:
+ {
+ SHA1_CTX context;
+
+ SHA1Init(&context);
+ SHA1Update(&context, tbs.ptr, tbs.len);
+ SHA1Final(digest->ptr, &context);
+ digest->len = SHA1_DIGEST_SIZE;
+ return TRUE;
+ }
+ case OID_SHA256:
+ case OID_SHA256_WITH_RSA:
+ {
+ sha256_context context;
+
+ sha256_init(&context);
+ sha256_write(&context, tbs.ptr, tbs.len);
+ sha256_final(&context);
+ memcpy(digest->ptr, context.sha_out, SHA2_256_DIGEST_SIZE);
+ digest->len = SHA2_256_DIGEST_SIZE;
+ return TRUE;
+ }
+ case OID_SHA384:
+ case OID_SHA384_WITH_RSA:
+ {
+ sha512_context context;
+
+ sha384_init(&context);
+ sha512_write(&context, tbs.ptr, tbs.len);
+ sha512_final(&context);
+ memcpy(digest->ptr, context.sha_out, SHA2_384_DIGEST_SIZE);
+ digest->len = SHA2_384_DIGEST_SIZE;
+ return TRUE;
+ }
+ case OID_SHA512:
+ case OID_SHA512_WITH_RSA:
+ {
+ sha512_context context;
+
+ sha512_init(&context);
+ sha512_write(&context, tbs.ptr, tbs.len);
+ sha512_final(&context);
+ memcpy(digest->ptr, context.sha_out, SHA2_512_DIGEST_SIZE);
+ digest->len = SHA2_512_DIGEST_SIZE;
+ return TRUE;
+ }
+ default:
+ digest->len = 0;
+ return FALSE;
+ }
+}
+
+/*
+ * compute an RSA signature with PKCS#1 padding
+ */
+void
+sign_hash(const RSA_private_key_t *k, const u_char *hash_val, size_t hash_len
+ , u_char *sig_val, size_t sig_len)
+{
+ chunk_t ch;
+ mpz_t t1, t2;
+ size_t padlen;
+ u_char *p = sig_val;
+
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("signing hash with RSA Key *%s", k->pub.keyid)
+ )
+ /* PKCS#1 v1.5 8.1 encryption-block formatting */
+ *p++ = 0x00;
+ *p++ = 0x01; /* BT (block type) 01 */
+ padlen = sig_len - 3 - hash_len;
+ memset(p, 0xFF, padlen);
+ p += padlen;
+ *p++ = 0x00;
+ memcpy(p, hash_val, hash_len);
+ passert(p + hash_len - sig_val == (ptrdiff_t)sig_len);
+
+ /* PKCS#1 v1.5 8.2 octet-string-to-integer conversion */
+ n_to_mpz(t1, sig_val, sig_len); /* (could skip leading 0x00) */
+
+ /* PKCS#1 v1.5 8.3 RSA computation y = x^c mod n
+ * Better described in PKCS#1 v2.0 5.1 RSADP.
+ * There are two methods, depending on the form of the private key.
+ * We use the one based on the Chinese Remainder Theorem.
+ */
+ mpz_init(t2);
+
+ mpz_powm(t2, t1, &k->dP, &k->p); /* m1 = c^dP mod p */
+
+ mpz_powm(t1, t1, &k->dQ, &k->q); /* m2 = c^dQ mod Q */
+
+ mpz_sub(t2, t2, t1); /* h = qInv (m1 - m2) mod p */
+ mpz_mod(t2, t2, &k->p);
+ mpz_mul(t2, t2, &k->qInv);
+ mpz_mod(t2, t2, &k->p);
+
+ mpz_mul(t2, t2, &k->q); /* m = m2 + h q */
+ mpz_add(t1, t1, t2);
+
+ /* PKCS#1 v1.5 8.4 integer-to-octet-string conversion */
+ ch = mpz_to_n(t1, sig_len);
+ memcpy(sig_val, ch.ptr, sig_len);
+ pfree(ch.ptr);
+
+ mpz_clear(t1);
+ mpz_clear(t2);
+}
+
+/*
+ * encrypt data with an RSA public key after padding
+ */
+chunk_t
+RSA_encrypt(const RSA_public_key_t *key, chunk_t in)
+{
+ u_char padded[RSA_MAX_OCTETS];
+ u_char *pos = padded;
+ int padding = key->k - in.len - 3;
+ int i;
+
+ if (padding < 8 || key->k > RSA_MAX_OCTETS)
+ return empty_chunk;
+
+ /* add padding according to PKCS#1 7.2.1 1.+2. */
+ *pos++ = 0x00;
+ *pos++ = 0x02;
+
+ /* pad with pseudo random bytes unequal to zero */
+ get_rnd_bytes(pos, padding);
+ for (i = 0; i < padding; i++)
+ {
+ while (!*pos)
+ get_rnd_bytes(pos, 1);
+ pos++;
+ }
+
+ /* append the padding terminator */
+ *pos++ = 0x00;
+
+ /* now add the data */
+ memcpy(pos, in.ptr, in.len);
+ DBG(DBG_RAW,
+ DBG_dump_chunk("data for rsa encryption:\n", in);
+ DBG_dump("padded data for rsa encryption:\n", padded, key->k)
+ )
+
+ /* convert chunk to integer (PKCS#1 7.2.1 3.a) */
+ {
+ chunk_t out;
+ mpz_t m, c;
+
+ mpz_init(c);
+ n_to_mpz(m, padded, key->k);
+
+ /* encrypt(PKCS#1 7.2.1 3.b) */
+ mpz_powm(c, m, &key->e, &key->n);
+
+ /* convert integer back to a chunk (PKCS#1 7.2.1 3.c) */
+ out = mpz_to_n(c, key->k);
+ mpz_clear(c);
+ mpz_clear(m);
+
+ DBG(DBG_RAW,
+ DBG_dump_chunk("rsa encrypted data:\n", out)
+ )
+ return out;
+ }
+}
+
+/*
+ * decrypt data with an RSA private key and remove padding
+ */
+bool
+RSA_decrypt(const RSA_private_key_t *key, chunk_t in, chunk_t *out)
+{
+ chunk_t padded;
+ u_char *pos;
+ mpz_t t1, t2;
+
+ n_to_mpz(t1, in.ptr,in.len);
+
+ /* PKCS#1 v1.5 8.3 RSA computation y = x^c mod n
+ * Better described in PKCS#1 v2.0 5.1 RSADP.
+ * There are two methods, depending on the form of the private key.
+ * We use the one based on the Chinese Remainder Theorem.
+ */
+ mpz_init(t2);
+
+ mpz_powm(t2, t1, &key->dP, &key->p); /* m1 = c^dP mod p */
+ mpz_powm(t1, t1, &key->dQ, &key->q); /* m2 = c^dQ mod Q */
+
+ mpz_sub(t2, t2, t1); /* h = qInv (m1 - m2) mod p */
+ mpz_mod(t2, t2, &key->p);
+ mpz_mul(t2, t2, &key->qInv);
+ mpz_mod(t2, t2, &key->p);
+
+ mpz_mul(t2, t2, &key->q); /* m = m2 + h q */
+ mpz_add(t1, t1, t2);
+
+ padded = mpz_to_n(t1, key->pub.k);
+ mpz_clear(t1);
+ mpz_clear(t2);
+
+ DBG(DBG_PRIVATE,
+ DBG_dump_chunk("rsa decrypted data with padding:\n", padded)
+ )
+ pos = padded.ptr;
+
+ /* PKCS#1 v1.5 8.1 encryption-block formatting (EB = 00 || 02 || PS || 00 || D) */
+
+ /* check for hex pattern 00 02 in decrypted message */
+ if ((*pos++ != 0x00) || (*(pos++) != 0x02))
+ {
+ plog("incorrect padding - probably wrong RSA key");
+ freeanychunk(padded);
+ return FALSE;
+ }
+ padded.len -= 2;
+
+ /* the plaintext data starts after first 0x00 byte */
+ while (padded.len-- > 0 && *pos++ != 0x00)
+
+ if (padded.len == 0)
+ {
+ plog("no plaintext data");
+ freeanychunk(padded);
+ return FALSE;
+ }
+
+ clonetochunk(*out, pos, padded.len, "decrypted data");
+ freeanychunk(padded);
+ return TRUE;
+}
+
+/*
+ * build signatureValue
+ */
+chunk_t
+pkcs1_build_signature(chunk_t tbs, int hash_alg, const RSA_private_key_t *key
+, bool bit_string)
+{
+
+ size_t siglen = key->pub.k;
+
+ u_char digest_buf[MAX_DIGEST_LEN];
+ chunk_t digest = { digest_buf, MAX_DIGEST_LEN };
+ chunk_t digestInfo, alg_id, signatureValue;
+ u_char *pos;
+
+ switch (hash_alg)
+ {
+ case OID_MD5:
+ case OID_MD5_WITH_RSA:
+ alg_id = ASN1_md5_id;
+ break;
+ case OID_SHA1:
+ case OID_SHA1_WITH_RSA:
+ alg_id = ASN1_sha1_id;
+ break;
+ default:
+ return empty_chunk;
+ }
+ compute_digest(tbs, hash_alg, &digest);
+
+ /* according to PKCS#1 v2.1 digest must be packaged into
+ * an ASN.1 structure for encryption
+ */
+ digestInfo = asn1_wrap(ASN1_SEQUENCE, "cm"
+ , alg_id
+ , asn1_simple_object(ASN1_OCTET_STRING, digest));
+
+ /* generate the RSA signature */
+ if (bit_string)
+ {
+ pos = build_asn1_object(&signatureValue, ASN1_BIT_STRING, 1 + siglen);
+ *pos++ = 0x00;
+ }
+ else
+ {
+ pos = build_asn1_object(&signatureValue, ASN1_OCTET_STRING, siglen);
+ }
+ sign_hash(key, digestInfo.ptr, digestInfo.len, pos, siglen);
+ pfree(digestInfo.ptr);
+
+ return signatureValue;
+}
+
+/*
+ * build a DER-encoded PKCS#1 private key object
+ */
+chunk_t
+pkcs1_build_private_key(const RSA_private_key_t *key)
+{
+ chunk_t pkcs1 = asn1_wrap(ASN1_SEQUENCE, "cmmmmmmmm"
+ , ASN1_INTEGER_0
+ , asn1_integer_from_mpz(&key->pub.n)
+ , asn1_integer_from_mpz(&key->pub.e)
+ , asn1_integer_from_mpz(&key->d)
+ , asn1_integer_from_mpz(&key->p)
+ , asn1_integer_from_mpz(&key->q)
+ , asn1_integer_from_mpz(&key->dP)
+ , asn1_integer_from_mpz(&key->dQ)
+ , asn1_integer_from_mpz(&key->qInv));
+
+ DBG(DBG_PRIVATE,
+ DBG_dump_chunk("PKCS#1 encoded private key:", pkcs1)
+ )
+ return pkcs1;
+}
+
+/*
+ * build a DER-encoded PKCS#1 public key object
+ */
+chunk_t
+pkcs1_build_public_key(const RSA_public_key_t *rsa)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "mm"
+ , asn1_integer_from_mpz(&rsa->n)
+ , asn1_integer_from_mpz(&rsa->e));
+}
+
+/*
+ * build a DER-encoded publicKeyInfo object
+ */
+chunk_t
+pkcs1_build_publicKeyInfo(const RSA_public_key_t *rsa)
+{
+ chunk_t publicKey;
+ chunk_t rawKey = pkcs1_build_public_key(rsa);
+
+ u_char *pos = build_asn1_object(&publicKey, ASN1_BIT_STRING
+ , 1 + rawKey.len);
+ *pos++ = 0x00;
+ mv_chunk(&pos, rawKey);
+
+ return asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_rsaEncryption_id
+ , publicKey);
+}
+void
+free_RSA_public_content(RSA_public_key_t *rsa)
+{
+ mpz_clear(&rsa->n);
+ mpz_clear(&rsa->e);
+}
+
+void
+free_RSA_private_content(RSA_private_key_t *rsak)
+{
+ free_RSA_public_content(&rsak->pub);
+ mpz_clear(&rsak->d);
+ mpz_clear(&rsak->p);
+ mpz_clear(&rsak->q);
+ mpz_clear(&rsak->dP);
+ mpz_clear(&rsak->dQ);
+ mpz_clear(&rsak->qInv);
+}
+