Provide compatibility functions for PKCS#12 parsing.
In order to minimise the upstream diffs needed for bits of Android to build with BoringSSL, this change implements the old style PKCS#12 functions as wrappers around the modern parser. The function to read all the contents of a BIO could almost be a utility function but I'll wait until there are two uses for it first. The important change from the original functions is that these will always read the complete buffer/BIO/FILE passed in. Based on a survey of uses of d2i_PKCS12 that I found, this appears to be universally what callers want anyway. Change-Id: I3f5b84e710b161d975f91f4d16c83d44371368d1 Reviewed-on: https://boringssl-review.googlesource.com/1791 Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
parent
588d2528d1
commit
5127db3b4d
@ -705,13 +705,56 @@ static int test(const char *name, const uint8_t *der, size_t der_len) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_compat(const uint8_t *der, size_t der_len) {
|
||||
PKCS12 *p12;
|
||||
X509 *cert = NULL;
|
||||
STACK_OF(X509) *ca_certs = NULL;
|
||||
EVP_PKEY *key;
|
||||
BIO *bio;
|
||||
|
||||
bio = BIO_new_mem_buf((void*) der, der_len);
|
||||
|
||||
p12 = d2i_PKCS12_bio(bio, NULL);
|
||||
if (p12 == NULL) {
|
||||
fprintf(stderr, "PKCS12_parse failed.\n");
|
||||
BIO_print_errors_fp(stderr);
|
||||
return 0;
|
||||
}
|
||||
BIO_free(bio);
|
||||
|
||||
if (!PKCS12_parse(p12, "foo", &key, &cert, &ca_certs)) {
|
||||
fprintf(stderr, "PKCS12_parse failed.\n");
|
||||
BIO_print_errors_fp(stderr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (key == NULL || cert == NULL) {
|
||||
fprintf(stderr, "Bad result from PKCS12_parse.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(key);
|
||||
X509_free(cert);
|
||||
|
||||
if (sk_X509_num(ca_certs) != 0) {
|
||||
fprintf(stderr, "Bad result from PKCS12_parse.\n");
|
||||
return 0;
|
||||
}
|
||||
sk_X509_free(ca_certs);
|
||||
|
||||
PKCS12_free(p12);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CRYPTO_library_init();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
if (!test("OpenSSL", kOpenSSL, sizeof(kOpenSSL)) ||
|
||||
!test("NSS", kNSS, sizeof(kNSS)) ||
|
||||
!test("Windows", kWindows, sizeof(kWindows))) {
|
||||
!test("Windows", kWindows, sizeof(kWindows)) ||
|
||||
!test_compat(kWindows, sizeof(kWindows))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -55,8 +55,12 @@
|
||||
|
||||
#include <openssl/pkcs8.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/buf.h>
|
||||
#include <openssl/cipher.h>
|
||||
#include <openssl/digest.h>
|
||||
#include <openssl/err.h>
|
||||
@ -64,8 +68,6 @@
|
||||
#include <openssl/mem.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "../bytestring/internal.h"
|
||||
#include "../evp/internal.h"
|
||||
|
||||
@ -879,6 +881,7 @@ int PKCS12_get_key_and_certs(EVP_PKEY **out_key, STACK_OF(X509) *out_certs,
|
||||
/* See ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12v1.pdf, section
|
||||
* four. */
|
||||
if (!CBS_get_asn1(&in, &pfx, CBS_ASN1_SEQUENCE) ||
|
||||
CBS_len(&in) != 0 ||
|
||||
!CBS_get_asn1_uint64(&pfx, &version)) {
|
||||
OPENSSL_PUT_ERROR(PKCS8, PKCS12_parse, PKCS8_R_BAD_PKCS12_DATA);
|
||||
goto err;
|
||||
@ -1017,3 +1020,135 @@ err:
|
||||
}
|
||||
|
||||
void PKCS12_PBE_add(){};
|
||||
|
||||
struct pkcs12_st {
|
||||
uint8_t *ber_bytes;
|
||||
size_t ber_len;
|
||||
};
|
||||
|
||||
PKCS12* d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes, size_t ber_len) {
|
||||
PKCS12 *p12;
|
||||
|
||||
/* out_p12 must be NULL because we don't export the PKCS12 structure. */
|
||||
assert(out_p12 == NULL);
|
||||
|
||||
p12 = OPENSSL_malloc(sizeof(PKCS12));
|
||||
if (!p12) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p12->ber_bytes = OPENSSL_malloc(ber_len);
|
||||
if (!p12->ber_bytes) {
|
||||
OPENSSL_free(p12);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(p12->ber_bytes, *ber_bytes, ber_len);
|
||||
p12->ber_len = ber_len;
|
||||
*ber_bytes += ber_len;
|
||||
|
||||
return p12;
|
||||
}
|
||||
|
||||
PKCS12* d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12) {
|
||||
size_t used = 0;
|
||||
BUF_MEM *buf;
|
||||
const uint8_t *dummy;
|
||||
static const size_t kMaxSize = 256 * 1024;
|
||||
PKCS12 *ret = NULL;
|
||||
|
||||
buf = BUF_MEM_new();
|
||||
if (buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (BUF_MEM_grow(buf, 8192) == 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int n = BIO_read(bio, &buf->data[used], buf->length - used);
|
||||
if (n < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
used += n;
|
||||
|
||||
if (used < buf->length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf->length > kMaxSize ||
|
||||
BUF_MEM_grow(buf, buf->length * 2) == 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dummy = (uint8_t*) buf->data;
|
||||
ret = d2i_PKCS12(out_p12, &dummy, used);
|
||||
|
||||
out:
|
||||
BUF_MEM_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
PKCS12* d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12) {
|
||||
BIO *bio;
|
||||
PKCS12 *ret;
|
||||
|
||||
bio = BIO_new_fp(fp, 0 /* don't take ownership */);
|
||||
if (!bio) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = d2i_PKCS12_bio(bio, out_p12);
|
||||
BIO_free(bio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PKCS12_parse(const PKCS12 *p12, const char *password, EVP_PKEY **out_pkey,
|
||||
X509 **out_cert, STACK_OF(X509) **out_ca_certs) {
|
||||
CBS ber_bytes;
|
||||
STACK_OF(X509) *ca_certs = NULL;
|
||||
char ca_certs_alloced = 0;
|
||||
|
||||
if (out_ca_certs != NULL && *out_ca_certs != NULL) {
|
||||
ca_certs = *out_ca_certs;
|
||||
}
|
||||
|
||||
if (!ca_certs) {
|
||||
ca_certs = sk_X509_new_null();
|
||||
if (ca_certs == NULL) {
|
||||
return 0;
|
||||
}
|
||||
ca_certs_alloced = 1;
|
||||
}
|
||||
|
||||
CBS_init(&ber_bytes, p12->ber_bytes, p12->ber_len);
|
||||
if (!PKCS12_get_key_and_certs(out_pkey, ca_certs, &ber_bytes, password)) {
|
||||
if (ca_certs_alloced) {
|
||||
sk_X509_free(ca_certs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_cert = NULL;
|
||||
if (sk_X509_num(ca_certs) > 0) {
|
||||
*out_cert = sk_X509_shift(ca_certs);
|
||||
}
|
||||
|
||||
if (out_ca_certs) {
|
||||
*out_ca_certs = ca_certs;
|
||||
} else {
|
||||
sk_X509_pop_free(ca_certs, X509_free);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PKCS12_free(PKCS12 *p12) {
|
||||
OPENSSL_free(p12->ber_bytes);
|
||||
OPENSSL_free(p12);
|
||||
}
|
||||
|
@ -192,6 +192,7 @@ typedef struct hmac_ctx_st HMAC_CTX;
|
||||
typedef struct md4_state_st MD4_CTX;
|
||||
typedef struct md5_state_st MD5_CTX;
|
||||
typedef struct pkcs8_priv_key_info_st PKCS8_PRIV_KEY_INFO;
|
||||
typedef struct pkcs12_st PKCS12;
|
||||
typedef struct rand_meth_st RAND_METHOD;
|
||||
typedef struct rsa_meth_st RSA_METHOD;
|
||||
typedef struct rsa_st RSA;
|
||||
|
@ -59,6 +59,8 @@
|
||||
|
||||
#include <openssl/base.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
@ -129,9 +131,42 @@ OPENSSL_EXPORT int PKCS12_get_key_and_certs(EVP_PKEY **out_key,
|
||||
STACK_OF(X509) *out_certs,
|
||||
CBS *in, const char *password);
|
||||
|
||||
|
||||
/* Deprecated functions. */
|
||||
|
||||
/* PKCS12_PBE_add does nothing. It exists for compatibility with OpenSSL. */
|
||||
OPENSSL_EXPORT void PKCS12_PBE_add();
|
||||
|
||||
/* d2i_PKCS12 is a dummy function that copies |*ber_bytes| into a
|
||||
* |PKCS12| structure. The |out_p12| argument must be NULL. On exit,
|
||||
* |*ber_bytes| will be advanced by |ber_len|. It returns a fresh |PKCS12|
|
||||
* structure or NULL on error.
|
||||
*
|
||||
* Note: unlike other d2i functions, |d2i_PKCS12| will always consume |ber_len|
|
||||
* bytes.*/
|
||||
OPENSSL_EXPORT PKCS12 *d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes,
|
||||
size_t ber_len);
|
||||
|
||||
/* d2i_PKCS12_bio acts like |d2i_PKCS12| but reads from a |BIO|. */
|
||||
OPENSSL_EXPORT PKCS12* d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12);
|
||||
|
||||
/* d2i_PKCS12_fp acts like |d2i_PKCS12| but reads from a |FILE|. */
|
||||
OPENSSL_EXPORT PKCS12* d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12);
|
||||
|
||||
/* PKCS12_parse calls |PKCS12_get_key_and_certs| on the ASN.1 data stored in
|
||||
* |p12|. The |out_pkey| and |out_cert| arguments must not be NULL and, on
|
||||
* successful exit, the private key and first certificate will be stored in
|
||||
* them. The |out_ca_certs| argument may be NULL but, if not, then any extra
|
||||
* certificates will be appended to |*out_ca_certs|. If |*out_ca_certs| is NULL
|
||||
* then it will be set to a freshly allocated stack containing the extra certs.
|
||||
*
|
||||
* It returns one on success and zero on error. */
|
||||
OPENSSL_EXPORT int PKCS12_parse(const PKCS12 *p12, const char *password,
|
||||
EVP_PKEY **out_pkey, X509 **out_cert,
|
||||
STACK_OF(X509) **out_ca_certs);
|
||||
|
||||
/* PKCS12_free frees |p12| and its contents. */
|
||||
OPENSSL_EXPORT void PKCS12_free(PKCS12 *p12);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
|
@ -40,7 +40,7 @@ static const struct argument kArguments[] = {
|
||||
},
|
||||
};
|
||||
|
||||
bool PKCS12(const std::vector<std::string> &args) {
|
||||
bool DoPKCS12(const std::vector<std::string> &args) {
|
||||
std::map<std::string, std::string> args_map;
|
||||
|
||||
if (!ParseKeyValueArguments(&args_map, args, kArguments) ||
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
bool Client(const std::vector<std::string> &args);
|
||||
bool PKCS12(const std::vector<std::string> &args);
|
||||
bool DoPKCS12(const std::vector<std::string> &args);
|
||||
bool Speed(const std::vector<std::string> &args);
|
||||
|
||||
static void usage(const char *name) {
|
||||
@ -45,7 +45,7 @@ int main(int argc, char **argv) {
|
||||
} else if (tool == "s_client" || tool == "client") {
|
||||
return !Client(args);
|
||||
} else if (tool == "pkcs12") {
|
||||
return !PKCS12(args);
|
||||
return !DoPKCS12(args);
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
|
Loading…
Reference in New Issue
Block a user