Merge branch 'pubkeys'

Adds support to pki --pub to convert public keys to other formats
including SSH keys and DNSKEYs.  SSH public keys can also be read
from files in the format used by OpenSSH.
This commit is contained in:
Tobias Brunner
2013-09-13 15:30:40 +02:00
15 changed files with 349 additions and 37 deletions
+2 -2
View File
@@ -1008,8 +1008,8 @@ ADD_PLUGIN([pkcs7], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts cmd])
ADD_PLUGIN([pgp], [s charon])
ADD_PLUGIN([dnskey], [s charon])
ADD_PLUGIN([sshkey], [s charon nm cmd])
ADD_PLUGIN([dnskey], [s charon pki])
ADD_PLUGIN([sshkey], [s charon pki nm cmd])
ADD_PLUGIN([ipseckey], [c charon])
ADD_PLUGIN([pem], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([padlock], [s charon])
@@ -87,6 +87,8 @@ enum cred_encoding_type_t {
PRIVKEY_PGP,
/** DNSKEY encoding */
PUBKEY_DNSKEY,
/** SSHKEY encoding (Base64) */
PUBKEY_SSHKEY,
/** ASN.1 DER encoded certificate */
CERT_ASN1_DER,
@@ -245,33 +245,23 @@ METHOD(public_key_t, get_encoding, bool,
private_openssl_ec_public_key_t *this, cred_encoding_type_t type,
chunk_t *encoding)
{
bool success = TRUE;
u_char *p;
switch (type)
*encoding = chunk_alloc(i2d_EC_PUBKEY(this->ec, NULL));
p = encoding->ptr;
i2d_EC_PUBKEY(this->ec, &p);
if (type != PUBKEY_SPKI_ASN1_DER)
{
case PUBKEY_SPKI_ASN1_DER:
case PUBKEY_PEM:
{
bool success = TRUE;
chunk_t asn1_encoding = *encoding;
*encoding = chunk_alloc(i2d_EC_PUBKEY(this->ec, NULL));
p = encoding->ptr;
i2d_EC_PUBKEY(this->ec, &p);
if (type == PUBKEY_PEM)
{
chunk_t asn1_encoding = *encoding;
success = lib->encoding->encode(lib->encoding, PUBKEY_PEM,
NULL, encoding, CRED_PART_ECDSA_PUB_ASN1_DER,
asn1_encoding, CRED_PART_END);
chunk_clear(&asn1_encoding);
}
return success;
}
default:
return FALSE;
success = lib->encoding->encode(lib->encoding, type,
NULL, encoding, CRED_PART_ECDSA_PUB_ASN1_DER,
asn1_encoding, CRED_PART_END);
chunk_clear(&asn1_encoding);
}
return success;
}
METHOD(public_key_t, get_ref, public_key_t*,
@@ -19,6 +19,7 @@
#ifndef OPENSSL_NO_RSA
#include "openssl_rsa_public_key.h"
#include "openssl_util.h"
#include <utils/debug.h>
@@ -248,6 +249,7 @@ METHOD(public_key_t, get_encoding, bool,
private_openssl_rsa_public_key_t *this, cred_encoding_type_t type,
chunk_t *encoding)
{
bool success = FALSE;
u_char *p;
switch (type)
@@ -255,11 +257,10 @@ METHOD(public_key_t, get_encoding, bool,
case PUBKEY_SPKI_ASN1_DER:
case PUBKEY_PEM:
{
bool success = TRUE;
*encoding = chunk_alloc(i2d_RSA_PUBKEY(this->rsa, NULL));
p = encoding->ptr;
i2d_RSA_PUBKEY(this->rsa, &p);
success = TRUE;
if (type == PUBKEY_PEM)
{
@@ -280,7 +281,20 @@ METHOD(public_key_t, get_encoding, bool,
return TRUE;
}
default:
return FALSE;
{
chunk_t n = chunk_empty, e = chunk_empty;
if (openssl_bn2chunk(this->rsa->n, &n) &&
openssl_bn2chunk(this->rsa->e, &e))
{
success = lib->encoding->encode(lib->encoding, type, NULL,
encoding, CRED_PART_RSA_MODULUS, n,
CRED_PART_RSA_PUB_EXP, e, CRED_PART_END);
}
chunk_free(&n);
chunk_free(&e);
return success;
}
}
}
@@ -123,6 +123,24 @@ bool openssl_bn_split(chunk_t chunk, BIGNUM *a, BIGNUM *b)
return TRUE;
}
/**
* Described in header.
*/
bool openssl_bn2chunk(BIGNUM *bn, chunk_t *chunk)
{
*chunk = chunk_alloc(BN_num_bytes(bn));
if (BN_bn2bin(bn, chunk->ptr) == chunk->len)
{
if (chunk->len && chunk->ptr[0] & 0x80)
{ /* if MSB is set, prepend a zero to make it non-negative */
*chunk = chunk_cat("cm", chunk_from_chars(0x00), *chunk);
}
return TRUE;
}
chunk_free(chunk);
return FALSE;
}
/**
* Described in header.
*/
@@ -66,6 +66,15 @@ bool openssl_bn_cat(int len, BIGNUM *a, BIGNUM *b, chunk_t *chunk);
*/
bool openssl_bn_split(chunk_t chunk, BIGNUM *a, BIGNUM *b);
/**
* Exports the given bignum (assumed to be a positive number) to a chunk in
* two's complement format (i.e. a zero byte is added if the MSB is set).
*
* @param bn the BIGNUM to export
* @param chunk the chunk (data gets allocated)
* @return TRUE on success, FALSE otherwise
*/
bool openssl_bn2chunk(BIGNUM *bn, chunk_t *chunk);
/**
* Allocate a chunk using the i2d function of a given object
+2 -1
View File
@@ -12,6 +12,7 @@ endif
libstrongswan_sshkey_la_SOURCES = \
sshkey_plugin.h sshkey_plugin.c \
sshkey_builder.h sshkey_builder.c
sshkey_builder.h sshkey_builder.c \
sshkey_encoder.h sshkey_encoder.c
libstrongswan_sshkey_la_LDFLAGS = -module -avoid-version
@@ -13,6 +13,10 @@
* for more details.
*/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include "sshkey_builder.h"
#include <asn1/oid.h>
@@ -124,12 +128,85 @@ static sshkey_public_key_t *parse_public_key(chunk_t blob)
return NULL;
}
/**
* Load SSH key from a FILE stream, closes the stream
*/
static sshkey_public_key_t *load_from_stream(FILE *file)
{
sshkey_public_key_t *public = NULL;
chunk_t blob = chunk_empty;
enumerator_t *enumerator;
char line[1024], *token;
while (!public && fgets(line, sizeof(line), file))
{ /* the format is: ssh-[rsa|ecdsa-...] <key(base64)> <identifier> */
if (!strpfx(line, "ssh-"))
{
continue;
}
enumerator = enumerator_create_token(line, " ", " ");
if (enumerator->enumerate(enumerator, &token) &&
enumerator->enumerate(enumerator, &token))
{
blob = chunk_from_base64(chunk_from_str(token), NULL);
}
enumerator->destroy(enumerator);
if (blob.ptr)
{
public = parse_public_key(blob);
chunk_free(&blob);
}
}
fclose(file);
return public;
}
/**
* Load SSH key from FD
*/
static sshkey_public_key_t *load_from_fd(int fd)
{
FILE *stream;
/* dup the FD as it gets closed in fclose() */
fd = dup(fd);
if (fd == -1)
{
return NULL;
}
stream = fdopen(fd, "r");
if (!stream)
{
close(fd);
return NULL;
}
return load_from_stream(stream);
}
/**
* Load SSH key from file
*/
static sshkey_public_key_t *load_from_file(char *file)
{
FILE *stream;
stream = fopen(file, "r");
if (!stream)
{
DBG1(DBG_LIB, " opening '%s' failed: %s", file, strerror(errno));
return NULL;
}
return load_from_stream(stream);
}
/**
* See header.
*/
sshkey_public_key_t *sshkey_public_key_load(key_type_t type, va_list args)
{
chunk_t blob = chunk_empty;
char *file = NULL;
int fd = -1;
while (TRUE)
{
@@ -138,6 +215,12 @@ sshkey_public_key_t *sshkey_public_key_load(key_type_t type, va_list args)
case BUILD_BLOB_SSHKEY:
blob = va_arg(args, chunk_t);
continue;
case BUILD_FROM_FILE:
file = va_arg(args, char*);
continue;
case BUILD_FROM_FD:
fd = va_arg(args, int);
continue;
case BUILD_END:
break;
default:
@@ -145,9 +228,17 @@ sshkey_public_key_t *sshkey_public_key_load(key_type_t type, va_list args)
}
break;
}
if (blob.ptr && type == KEY_ANY)
if (blob.ptr)
{
return parse_public_key(blob);
}
if (file)
{
return load_from_file(file);
}
if (fd != -1)
{
return load_from_fd(fd);
}
return NULL;
}
@@ -14,7 +14,7 @@
*/
/**
* @defgroup sshky_public_key sshky_public_key
* @defgroup sshkey_public_key sshkey_public_key
* @{ @ingroup sshkey_p
*/
@@ -0,0 +1,125 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* 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.
*/
#include "sshkey_encoder.h"
#include <asn1/asn1.h>
#include <asn1/oid.h>
#include <bio/bio_writer.h>
#define ECDSA_PREFIX "ecdsa-sha2-"
/**
* Write an EC domain parameter identifier as defined in RFC 5656
*/
static void write_ec_identifier(bio_writer_t *writer, char *prefix, int oid,
chunk_t enc)
{
char *curve, identifier[128];
switch (oid)
{
case OID_PRIME256V1:
curve = strdup("nistp256");
break;
case OID_SECT384R1:
curve = strdup("nistp384");
break;
case OID_SECT521R1:
curve = strdup("nistp521");
break;
default:
curve = asn1_oid_to_string(enc);
break;
}
if (curve && snprintf(identifier, sizeof(identifier), "%s%s", prefix,
curve) < sizeof(identifier))
{
writer->write_data32(writer, chunk_from_str(identifier));
}
free(curve);
}
/**
* Encode the public key as Base64 encoded SSH key blob
*/
static bool build_public_key(chunk_t *encoding, va_list args)
{
bio_writer_t *writer;
chunk_t n, e;
if (cred_encoding_args(args, CRED_PART_RSA_MODULUS, &n,
CRED_PART_RSA_PUB_EXP, &e, CRED_PART_END))
{
writer = bio_writer_create(0);
writer->write_data32(writer, chunk_from_str("ssh-rsa"));
writer->write_data32(writer, e);
writer->write_data32(writer, n);
*encoding = chunk_to_base64(writer->get_buf(writer), NULL);
writer->destroy(writer);
return TRUE;
}
else if (cred_encoding_args(args, CRED_PART_ECDSA_PUB_ASN1_DER, &n,
CRED_PART_END))
{
chunk_t params, alg, q;
int oid;
/* parse subjectPublicKeyInfo */
if (asn1_unwrap(&n, &n) != ASN1_SEQUENCE)
{
return FALSE;
}
oid = asn1_parse_algorithmIdentifier(n, 1, &params);
if (oid != OID_EC_PUBLICKEY ||
asn1_unwrap(&params, &params) != ASN1_OID)
{
return FALSE;
}
oid = asn1_known_oid(params);
if (oid == OID_UNKNOWN)
{
return FALSE;
}
if (asn1_unwrap(&n, &alg) != ASN1_SEQUENCE ||
asn1_unwrap(&n, &q) != ASN1_BIT_STRING)
{
return FALSE;
}
writer = bio_writer_create(0);
write_ec_identifier(writer, ECDSA_PREFIX, oid, params);
write_ec_identifier(writer, "", oid, params);
q = chunk_skip_zero(q);
writer->write_data32(writer, q);
*encoding = chunk_to_base64(writer->get_buf(writer), NULL);
writer->destroy(writer);
return TRUE;
}
return FALSE;
}
bool sshkey_encoder_encode(cred_encoding_type_t type, chunk_t *encoding,
va_list args)
{
switch (type)
{
case PUBKEY_SSHKEY:
return build_public_key(encoding, args);
default:
return FALSE;
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* 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.
*/
/**
* @defgroup sshkey_encoder sshkey_encoder
* @{ @ingroup sshkey_p
*/
#ifndef SSHKEY_ENCODER_H_
#define SSHKEY_ENCODER_H_
#include <credentials/cred_encoding.h>
/**
* Encoding of public keys to RFC 4253 format.
*/
bool sshkey_encoder_encode(cred_encoding_type_t type, chunk_t *encoding,
va_list args);
#endif /** SSHKEY_ENCODER_H_ @}*/
@@ -17,6 +17,7 @@
#include <library.h>
#include "sshkey_builder.h"
#include "sshkey_encoder.h"
typedef struct private_sshkey_plugin_t private_sshkey_plugin_t;
@@ -51,6 +52,7 @@ METHOD(plugin_t, get_features, int,
METHOD(plugin_t, destroy, void,
private_sshkey_plugin_t *this)
{
lib->encoding->remove_encoder(lib->encoding, sshkey_encoder_encode);
free(this);
}
@@ -70,6 +72,7 @@ plugin_t *sshkey_plugin_create()
},
},
);
lib->encoding->add_encoder(lib->encoding, sshkey_encoder_encode);
return &this->public.plugin;
}
+16 -2
View File
@@ -51,6 +51,11 @@ static int pub()
type = CRED_PRIVATE_KEY;
subtype = KEY_ECDSA;
}
else if (streq(arg, "pub"))
{
type = CRED_PUBLIC_KEY;
subtype = KEY_ANY;
}
else if (streq(arg, "pkcs10"))
{
type = CRED_CERTIFICATE;
@@ -116,6 +121,15 @@ static int pub()
public = private->get_public_key(private);
private->destroy(private);
}
else if (type == CRED_PUBLIC_KEY)
{
public = cred;
if (!public)
{
fprintf(stderr, "parsing public key failed\n");
return 1;
}
}
else
{
cert = cred;
@@ -157,8 +171,8 @@ static void __attribute__ ((constructor))reg()
command_register((command_t) {
pub, 'p', "pub",
"extract the public key from a private key/certificate",
{"[--in file|--keyid hex] [--type rsa|ecdsa|pkcs10|x509]",
"[--outform der|pem|dnskey]"},
{"[--in file|--keyid hex] [--type rsa|ecdsa|pub|pkcs10|x509]",
"[--outform der|pem|dnskey|sshkey]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "input file, default: stdin"},
+6 -4
View File
@@ -48,12 +48,14 @@ Input file. If not given the input is read from \fISTDIN\fR.
.TP
.BI "\-t, \-\-type " type
Type of input. One of \fIrsa\fR (RSA private key), \fIecdsa\fR (ECDSA
private key), \fIpkcs10\fR (PKCS#10 certificate request), \fIx509\fR (X.509
certificate), defaults to \fIrsa\fR.
private key), \fIpub\fR (public key),
\fIpkcs10\fR (PKCS#10 certificate request), or \fIx509\fR (X.509 certificate),
defaults to \fIrsa\fR.
.TP
.BI "\-f, \-\-outform " encoding
Encoding of the extracted public key. One of \fIder\fR (ASN.1 DER), \fIpem\fR
(Base64 PEM), or \fIdnskey\fR (RFC 3110 DNS key), defaults to \fIder\fR.
(Base64 PEM), \fIdnskey\fR (RFC 3110 DNS key), or \fIsshkey\fR (RFC 4253 SSH
key), defaults to \fIder\fR.
.
.SH "EXAMPLES"
.
@@ -72,4 +74,4 @@ Extract the public key from an X.509 certificate:
.
.SH "SEE ALSO"
.
.BR pki (1)
.BR pki (1)
+12 -1
View File
@@ -81,7 +81,18 @@ bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type)
switch (type)
{
case CRED_PUBLIC_KEY:
*enc =PUBKEY_DNSKEY;
*enc = PUBKEY_DNSKEY;
return TRUE;
default:
return FALSE;
}
}
else if (streq(form, "sshkey"))
{
switch (type)
{
case CRED_PUBLIC_KEY:
*enc = PUBKEY_SSHKEY;
return TRUE;
default:
return FALSE;