gtlscertificate: Allow any type of private key in PEM files

Allow any type of private key in PEM files by treating PEM guards ending
with "PRIVATE KEY-----" as a private key instead of looking for a
pre-defined set of PEM guards. This enables the possibility for custom
GTlsBackend to add support for new key types.

Test cases have been expanded to ensure PEM parsing works for private
key when either header or footer is missing.

Encrypted PKCS#8 is still rejected. Test case has been added for this to
ensure behaviour is the same before and after this change.
This commit is contained in:
Fredrik Ternerot 2018-12-10 08:48:45 +01:00
parent 73ca761a8d
commit a437a50694
6 changed files with 128 additions and 45 deletions

View File

@ -218,12 +218,11 @@ g_tls_certificate_new_internal (const gchar *certificate_pem,
#define PEM_CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----" #define PEM_CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----"
#define PEM_CERTIFICATE_FOOTER "-----END CERTIFICATE-----" #define PEM_CERTIFICATE_FOOTER "-----END CERTIFICATE-----"
#define PEM_PKCS1_PRIVKEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" #define PEM_PRIVKEY_HEADER_BEGIN "-----BEGIN "
#define PEM_PKCS1_PRIVKEY_FOOTER "-----END RSA PRIVATE KEY-----" #define PEM_PRIVKEY_HEADER_END "PRIVATE KEY-----"
#define PEM_PKCS8_PRIVKEY_HEADER "-----BEGIN PRIVATE KEY-----" #define PEM_PRIVKEY_FOOTER_BEGIN "-----END "
#define PEM_PKCS8_PRIVKEY_FOOTER "-----END PRIVATE KEY-----" #define PEM_PRIVKEY_FOOTER_END "PRIVATE KEY-----"
#define PEM_PKCS8_ENCRYPTED_HEADER "-----BEGIN ENCRYPTED PRIVATE KEY-----" #define PEM_PKCS8_ENCRYPTED_HEADER "-----BEGIN ENCRYPTED PRIVATE KEY-----"
#define PEM_PKCS8_ENCRYPTED_FOOTER "-----END ENCRYPTED PRIVATE KEY-----"
static gchar * static gchar *
parse_private_key (const gchar *data, parse_private_key (const gchar *data,
@ -231,45 +230,47 @@ parse_private_key (const gchar *data,
gboolean required, gboolean required,
GError **error) GError **error)
{ {
const gchar *start, *end, *footer; const gchar *header_start = NULL, *header_end, *footer_start = NULL, *footer_end;
start = g_strstr_len (data, data_len, PEM_PKCS1_PRIVKEY_HEADER); header_end = g_strstr_len (data, data_len, PEM_PRIVKEY_HEADER_END);
if (start) if (header_end)
footer = PEM_PKCS1_PRIVKEY_FOOTER; header_start = g_strrstr_len (data, header_end - data, PEM_PRIVKEY_HEADER_BEGIN);
else
if (!header_start)
{ {
start = g_strstr_len (data, data_len, PEM_PKCS8_PRIVKEY_HEADER); if (required)
if (start) g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
footer = PEM_PKCS8_PRIVKEY_FOOTER; _("No PEM-encoded private key found"));
else
{ return NULL;
start = g_strstr_len (data, data_len, PEM_PKCS8_ENCRYPTED_HEADER);
if (start)
{
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
_("Cannot decrypt PEM-encoded private key"));
}
else if (required)
{
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
_("No PEM-encoded private key found"));
}
return NULL;
}
} }
end = g_strstr_len (start, data_len - (start - data), footer); header_end += strlen (PEM_PRIVKEY_HEADER_END);
if (!end)
if (strncmp (header_start, PEM_PKCS8_ENCRYPTED_HEADER, header_end - header_start) == 0)
{
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
_("Cannot decrypt PEM-encoded private key"));
return NULL;
}
footer_end = g_strstr_len (header_end, data_len - (header_end - data), PEM_PRIVKEY_FOOTER_END);
if (footer_end)
footer_start = g_strrstr_len (header_end, footer_end - header_end, PEM_PRIVKEY_FOOTER_BEGIN);
if (!footer_start)
{ {
g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
_("Could not parse PEM-encoded private key")); _("Could not parse PEM-encoded private key"));
return NULL; return NULL;
} }
end += strlen (footer);
while (*end == '\r' || *end == '\n')
end++;
return g_strndup (start, end - start); footer_end += strlen (PEM_PRIVKEY_FOOTER_END);
while (*footer_end == '\r' || *footer_end == '\n')
footer_end++;
return g_strndup (header_start, footer_end - header_start);
} }

View File

@ -225,17 +225,20 @@ tls_certificate_SOURCES = \
dist_test_data += $(cert_data_files) dist_test_data += $(cert_data_files)
cert_data_files = $(addprefix cert-tests/,$(cert_tests)) cert_data_files = $(addprefix cert-tests/,$(cert_tests))
cert_tests = \ cert_tests = \
cert1.pem \ cert1.pem \
cert2.pem \ cert2.pem \
cert3.pem \ cert3.pem \
cert-crlf.pem \ cert-crlf.pem \
cert-key.pem \ cert-key.pem \
cert-list.pem \ cert-list.pem \
key8.pem \ key8.pem \
key-cert.pem \ key8enc.pem \
key.pem \ key-cert.pem \
key-crlf.pem \ key.pem \
nothing.pem \ key-crlf.pem \
key_missing-footer.pem \
key_missing-header.pem \
nothing.pem \
$(NULL) $(NULL)
uninstalled_test_extra_programs += socket-client uninstalled_test_extra_programs += socket-client

View File

@ -0,0 +1,18 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIdjDoEOJTH2ICAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDNLC2sDVjClaQyT8BfXTt1BIIC
gCN4s9Z5bmfKogL7YHIJly2zLX5uILHeCr3iQpoPS8057V9Af1wqB/8AUOJrLY96
R2amkXjlxuqA0BebEk4gcR4tWvCNQ2VCOqvQozUt8LnA+2xQRgzNwaW0HPxcAUzf
6GVZKL7xfpwFD2ootfLwTHB2zAIVMo8nwgEzdDz93ZwsMmXJmOfSO7vpDQUnVqUX
jVlue0i8n7fO4ClQ8fz5J8zyvPj403bR9qxsIJjQZACNVLMIksQXjTDngymy/ziI
lZD4JDLXCQwAOgFz6N6vsyD/mHROyL4/4q8ujYFPmVpuAlQzuZJe6TFnmZHiSfoI
we6wi1Nee1rbM4VzsGFzMa4Fr0ZhElHEKBXXje4YKWCAOWEo3tLjow4+0dQxNx5W
tsbQdRt2fRYNYTgt18O55kq3DVfy93aMQVYIMuXkxwAuCWBeiLQrCfAM5r7kDwfc
owp2AQ5Ndf+aAwr89k2fYUpexz9kZzU+eIY2K1cRhpUlLRAr5SG2oVy7n9IvYs1m
O7/hjVBvXeAPDADVOtx/YNxPYr9ZI1X2QNDYGxNuSUNF1qGps66Gj+fcRe2NO+Ej
YfSyfBvw+0h8sad81ZPepCSpIkYX91p6lCdCmRnJWYBwYyn6V5tXOx6tn5ntKJZ9
9OtTGr7CMm7PLs9S8b03MV9IDJH+TBqR7msP1KWZbTxCNOws28EXo75tQ51ywElF
FJI6ZU2gBYaX39i8WyvEMXFRRqzYUMzV0Yw2KeVRiGLh0ZX/4rlh2PQqVXGyakvn
XttDRKEYPEvXDSRpO+tIvESlq9T0Pfo/rpnD4xJd2JWO6z/CSrn8cujs80e1+YjT
HXksoJzsoLGeiYG2DzTK9lY=
-----END ENCRYPTED PRIVATE KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCslrTl+gp3lQsEGMegjhQMow5yhinpQ7yji36soGuUpUynQPs7
P5tm3ojZPeQGNmYof3Csc2yTrMLrCidt8xFkcgtZydOJ9sQSEgwc2of4nlvffIZb
dIR+13WOPVRmS5tBVkQWFrlSFtQ70BDhNK3SZfkAb4+QmLSR1MJFaUPWrQIDAQAB
AoGAUTnskYAIhRdEQ/1Vlp7HmNr05bl26C3VDjOMvroRZ7gUR3MxykS5YsTBK10R
gEsB8XVpFgCMzUO1yODShdCsEg9kCB3fzSWkunK8+TF2TKOM5uWlQwifKJvcNisR
Nbg3r8WygMMXaWSFA3xWoRuZ5It0jOX18v+x5RHHon/kaRECQQDl6FSwgJLeNAkR
pMNQGdRhmMesHWmNNBv3Wozqm6Wpkwo5ZXPsLt3pprd0GN5jX0IG7clT1/eMD9/G
+3UGqTj3AkEAwC0M2gv+QUhbaB+KSlOZDOi4gsnhnsnaM7HQGDJJ5no4y2EvnYI3
Y5rPJWedeYlCV3ccMitjnjcIJHInRZBIewJBANgsamVDn9Ua7GQQni1U/COAek7V
oQfKNXmRROrbyxr1TSnGwQcU0kf+IIUjVQfu67CEKUeSzAqAapM4oULQHuUCQQC9
J9qdiO6DXXAzRdA9pplgHnT2rzV3sSEoft3f4yfgRu8+KHPQqkpQrSE1pQ5YgWUe
aGwFabXNFkfab839562fAkBl8jPidQdKWEgSa6h5pm4++sXLdWl7p6jiyetH64W7
HnhRryE3ptrRGO0hSV1v4bx3DKzeJiJRlWUWiSl7828t

View File

@ -0,0 +1,14 @@
MIICXQIBAAKBgQCslrTl+gp3lQsEGMegjhQMow5yhinpQ7yji36soGuUpUynQPs7
P5tm3ojZPeQGNmYof3Csc2yTrMLrCidt8xFkcgtZydOJ9sQSEgwc2of4nlvffIZb
dIR+13WOPVRmS5tBVkQWFrlSFtQ70BDhNK3SZfkAb4+QmLSR1MJFaUPWrQIDAQAB
AoGAUTnskYAIhRdEQ/1Vlp7HmNr05bl26C3VDjOMvroRZ7gUR3MxykS5YsTBK10R
gEsB8XVpFgCMzUO1yODShdCsEg9kCB3fzSWkunK8+TF2TKOM5uWlQwifKJvcNisR
Nbg3r8WygMMXaWSFA3xWoRuZ5It0jOX18v+x5RHHon/kaRECQQDl6FSwgJLeNAkR
pMNQGdRhmMesHWmNNBv3Wozqm6Wpkwo5ZXPsLt3pprd0GN5jX0IG7clT1/eMD9/G
+3UGqTj3AkEAwC0M2gv+QUhbaB+KSlOZDOi4gsnhnsnaM7HQGDJJ5no4y2EvnYI3
Y5rPJWedeYlCV3ccMitjnjcIJHInRZBIewJBANgsamVDn9Ua7GQQni1U/COAek7V
oQfKNXmRROrbyxr1TSnGwQcU0kf+IIUjVQfu67CEKUeSzAqAapM4oULQHuUCQQC9
J9qdiO6DXXAzRdA9pplgHnT2rzV3sSEoft3f4yfgRu8+KHPQqkpQrSE1pQ5YgWUe
aGwFabXNFkfab839562fAkBl8jPidQdKWEgSa6h5pm4++sXLdWl7p6jiyetH64W7
HnhRryE3ptrRGO0hSV1v4bx3DKzeJiJRlWUWiSl7828t
-----END RSA PRIVATE KEY-----

View File

@ -260,6 +260,22 @@ from_files (const Reference *ref)
g_clear_error (&error); g_clear_error (&error);
g_assert_null (cert); g_assert_null (cert);
/* Missing header private key */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key_missing-header.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
/* Missing footer private key */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key_missing-footer.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
/* Missing certificate */ /* Missing certificate */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL), cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL), g_test_get_filename (G_TEST_DIST, "cert-tests", "key.pem", NULL),
@ -333,6 +349,21 @@ from_files_pkcs8 (const Reference *ref)
g_object_unref (cert); g_object_unref (cert);
} }
static void
from_files_pkcs8enc (const Reference *ref)
{
GTlsCertificate *cert;
GError *error = NULL;
/* Mare sure an error is returned for encrypted key */
cert = g_tls_certificate_new_from_files (g_test_get_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL),
g_test_get_filename (G_TEST_DIST, "cert-tests", "key8enc.pem", NULL),
&error);
g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
g_clear_error (&error);
g_assert_null (cert);
}
static void static void
list_from_file (const Reference *ref) list_from_file (const Reference *ref)
{ {
@ -429,6 +460,8 @@ main (int argc,
&ref, (GTestDataFunc)from_files_crlf); &ref, (GTestDataFunc)from_files_crlf);
g_test_add_data_func ("/tls-certificate/from_files_pkcs8", g_test_add_data_func ("/tls-certificate/from_files_pkcs8",
&ref, (GTestDataFunc)from_files_pkcs8); &ref, (GTestDataFunc)from_files_pkcs8);
g_test_add_data_func ("/tls-certificate/from_files_pkcs8enc",
&ref, (GTestDataFunc)from_files_pkcs8enc);
g_test_add_data_func ("/tls-certificate/list_from_file", g_test_add_data_func ("/tls-certificate/list_from_file",
&ref, (GTestDataFunc)list_from_file); &ref, (GTestDataFunc)list_from_file);