5 // Created by Martin Baulig on 3/8/16.
6 // Copyright © 2016 Xamarin. All rights reserved.
9 #include "btls-pkcs12.h"
10 #include <openssl/pkcs12.h>
16 struct MonoBtlsPkcs12
{
17 STACK_OF(X509
) *certs
;
18 EVP_PKEY
*private_key
;
19 CRYPTO_refcount_t references
;
22 // Passwords are marsahled directly in pinvokes from a SafePasswordHandle that uses different encoding
23 // depending on used PAL (SafePasswordHande.Unix.cs, SafePasswordHande.Windows.cs).
24 // On Unix:es, passwords are stored using Marshal.StringToHGlobalAnsi that can be passed directly to OpenSSL
25 // but on Windows passwords are stored using Marshal.StringToHGlobalUni, most likely because Windows Crypto API
26 // uses Unicode strings for passwords. Instead of changing this pattern (passing passwords as SafePasswordHandle
27 // throught pinvokes) at this particular call site convert things to UTF-8 before calling into OpenSSL.
30 deallocate_btls_password (char *password
)
33 memset (password
, 0, strlen (password
));
39 allocate_btls_password (const void *password
)
42 int buffer_size
= WideCharToMultiByte (CP_UTF8
, 0, (PCWCH
)password
, -1, NULL
, 0, NULL
, NULL
);
43 if (buffer_size
!= 0) {
44 buffer
= malloc (buffer_size
);
46 buffer
[buffer_size
- 1] = '\0';
47 if (WideCharToMultiByte (CP_UTF8
, 0, (PCWCH
)password
, -1, buffer
, buffer_size
, NULL
, NULL
) == 0) {
48 // Failed to convert buffer.
49 deallocate_btls_password (buffer
);
59 deallocate_btls_password (char *password
)
65 allocate_btls_password (const void *password
)
67 return (char *)password
;
72 mono_btls_pkcs12_new (void)
74 MonoBtlsPkcs12
*pkcs12
= (MonoBtlsPkcs12
*)OPENSSL_malloc (sizeof (MonoBtlsPkcs12
));
78 memset (pkcs12
, 0, sizeof(MonoBtlsPkcs12
));
79 pkcs12
->certs
= sk_X509_new_null ();
80 pkcs12
->references
= 1;
85 mono_btls_pkcs12_get_count (MonoBtlsPkcs12
*pkcs12
)
87 return (int)sk_X509_num (pkcs12
->certs
);
91 mono_btls_pkcs12_get_cert (MonoBtlsPkcs12
*pkcs12
, int index
)
95 if ((size_t)index
>= sk_X509_num (pkcs12
->certs
))
97 cert
= sk_X509_value (pkcs12
->certs
, index
);
104 mono_btls_pkcs12_get_certs (MonoBtlsPkcs12
*pkcs12
)
106 return pkcs12
->certs
;
110 mono_btls_pkcs12_free (MonoBtlsPkcs12
*pkcs12
)
112 if (!CRYPTO_refcount_dec_and_test_zero (&pkcs12
->references
))
115 sk_X509_pop_free (pkcs12
->certs
, X509_free
);
116 OPENSSL_free (pkcs12
);
121 mono_btls_pkcs12_up_ref (MonoBtlsPkcs12
*pkcs12
)
123 CRYPTO_refcount_inc (&pkcs12
->references
);
128 mono_btls_pkcs12_add_cert (MonoBtlsPkcs12
*pkcs12
, X509
*x509
)
131 sk_X509_push (pkcs12
->certs
, x509
);
135 btls_pkcs12_import (MonoBtlsPkcs12
*pkcs12
, const void *data
, int len
, const char *btls_password
)
138 CBS_init (&cbs
, data
, len
);
141 ret
= PKCS12_get_key_and_certs (&pkcs12
->private_key
, pkcs12
->certs
, &cbs
, btls_password
);
142 if ((ret
== 1) || (btls_password
&& strlen (btls_password
) > 0))
145 // When passed an empty password, we try both NULL and the empty string.
146 CBS_init (&cbs
, data
, len
);
148 return PKCS12_get_key_and_certs (&pkcs12
->private_key
, pkcs12
->certs
, &cbs
, NULL
);
150 return PKCS12_get_key_and_certs (&pkcs12
->private_key
, pkcs12
->certs
, &cbs
, "");
154 mono_btls_pkcs12_import (MonoBtlsPkcs12
*pkcs12
, const void *data
, int len
, const void *password
)
156 char *btls_password
= allocate_btls_password (password
);
157 int ret
= btls_pkcs12_import (pkcs12
, data
, len
, btls_password
);
158 deallocate_btls_password (btls_password
);
163 mono_btls_pkcs12_has_private_key (MonoBtlsPkcs12
*pkcs12
)
165 return pkcs12
->private_key
!= NULL
;
169 mono_btls_pkcs12_get_private_key (MonoBtlsPkcs12
*pkcs12
)
171 if (!pkcs12
->private_key
)
173 return EVP_PKEY_up_ref (pkcs12
->private_key
);