Skip to content

Commit 12f2f6a

Browse files
committed
Merge branch 'ingela/ssl/handle-key-file/OTP-19780' into maint
* ingela/ssl/handle-key-file/OTP-19780: ssl: Relax requierments on keyfile
2 parents 3c51e2a + c492385 commit 12f2f6a

File tree

3 files changed

+187
-15
lines changed

3 files changed

+187
-15
lines changed

lib/ssl/src/ssl_config.erl

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,8 @@ init_private_key(undefined, CertKey, DbHandle) ->
390390
KeyFile ->
391391
Password = maps:get(password, CertKey, undefined),
392392
try
393-
{ok, List} = ssl_manager:cache_pem_file(KeyFile, DbHandle),
394-
[PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List,
395-
PKey =:= 'RSAPrivateKey' orelse
396-
PKey =:= 'DSAPrivateKey' orelse
397-
PKey =:= 'ECPrivateKey' orelse
398-
PKey =:= 'PrivateKeyInfo'
399-
],
393+
{ok, PemEntries} = ssl_manager:cache_pem_file(KeyFile, DbHandle),
394+
[PemEntry] = key_entry(PemEntries),
400395
public_key:pem_entry_decode(PemEntry, Password)
401396
catch
402397
_:Reason ->
@@ -2179,24 +2174,32 @@ do_handle_cert_file(File, PemCacheName) ->
21792174
handle_key_file(#{keyfile := File} = CertKey, PemCacheName) ->
21802175
case file:read_file(File) of
21812176
{ok, Pem} ->
2182-
case public_key:pem_decode(Pem) of
2177+
PemEntries = public_key:pem_decode(Pem),
2178+
Password = maps:get(password, CertKey, ""),
2179+
case key_entry(PemEntries) of
21832180
[KeyEntry] ->
2184-
Password = maps:get(password, CertKey, ""),
21852181
try public_key:pem_entry_decode(KeyEntry, Password) of
21862182
Key ->
2187-
handle_key(PemCacheName, File, Key, [KeyEntry])
2183+
handle_key(PemCacheName, File, Key, PemEntries)
21882184
catch _:_ ->
21892185
{error, wrong_password}
21902186
end;
2191-
Unexpected ->
2192-
{error, {unexpected_content, Unexpected}}
2187+
_ ->
2188+
{error, {unexpected_content, {missing_single_key, File}}}
21932189
end;
21942190
{error, _} = Error ->
21952191
Error
21962192
end;
21972193
handle_key_file(_,_) ->
21982194
ok.
21992195

2196+
key_entry(PemEntries) ->
2197+
[PemEntry || PemEntry = {PKey, _ , _} <- PemEntries,
2198+
PKey =:= 'RSAPrivateKey' orelse
2199+
PKey =:= 'DSAPrivateKey' orelse
2200+
PKey =:= 'ECPrivateKey' orelse
2201+
PKey =:= 'PrivateKeyInfo'].
2202+
22002203
handle_key(PemCacheName, File, Key, Content) ->
22012204
case check_key(Key) of
22022205
ok ->

lib/ssl/test/ssl_api_SUITE.erl

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@
197197
exporter_master_secret_consumed/1,
198198
legacy_prf/0,
199199
legacy_prf/1,
200-
listen_pem_file_failure/1
200+
listen_pem_file_failure/1,
201+
same_file_for_key_and_cert/0,
202+
same_file_for_key_and_cert/1
201203
]).
202204

203205
%% Apply export
@@ -331,7 +333,8 @@ gen_api_tests() ->
331333
cipher_listing,
332334
export_key_materials,
333335
legacy_prf,
334-
listen_pem_file_failure
336+
listen_pem_file_failure,
337+
same_file_for_key_and_cert
335338
].
336339

337340
handshake_paus_tests() ->
@@ -3849,6 +3852,7 @@ listen_pem_file_failure(Config) when is_list(Config) ->
38493852
BadDH = filename:join(DataDir, "dHParam-invalid.pem"),
38503853
BaseOpts = [{versions, [Version]}, {protocol, tls_or_dtls(Version)}],
38513854
NoCACerts = filename:join(DataDir, "nocacerts.pem"),
3855+
CertAndKey = filename:join(DataDir, "cert_and_key.pem"),
38523856
{error, {options, {keyfile, {Key, wrong_password}}}} =
38533857
ssl:listen(0, [{certfile, Cert}, {keyfile, Key}] ++ BaseOpts),
38543858
{error, {options, {certfile, {Key, no_certs}}}} =
@@ -3870,11 +3874,50 @@ listen_pem_file_failure(Config) when is_list(Config) ->
38703874
ssl:listen(0, ServerOpts ++ BaseOpts ++ [{dhfile, BadDH}]);
38713875
_ ->
38723876
ok
3873-
end.
3877+
end,
3878+
%% Shall not fail to have both cert and key in same file
3879+
{ok, L} = ssl:listen(0, [{certfile, CertAndKey}, {keyfile, CertAndKey}] ++ BaseOpts),
3880+
ssl:close(L).
3881+
3882+
%%--------------------------------------------------------------------
3883+
same_file_for_key_and_cert() ->
3884+
["Test that it works to put entity cert (can be entity cert chain also) and key in same file"].
3885+
3886+
same_file_for_key_and_cert(Config) when is_list(Config) ->
3887+
SHA = sha256,
3888+
#{client_config := ClientOpts0,
3889+
server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(rsa, [{server_chain,
3890+
[[{digest, SHA}],
3891+
[{digest, SHA}],
3892+
[{digest, SHA}]]},
3893+
{client_chain,
3894+
[[{digest, SHA}],
3895+
[{digest, SHA}],
3896+
[{digest, SHA}]]}
3897+
]),
3898+
ServerCert = proplists:get_value(cert, ServerOpts0),
3899+
{SKeyType, SKey} = proplists:get_value(key, ServerOpts0),
3900+
ClientCert = proplists:get_value(cert, ClientOpts0),
3901+
{CKeyType, CKey} = proplists:get_value(key, ClientOpts0),
3902+
PrivDir = proplists:get_value(priv_dir, Config),
3903+
SCertAndKeyFile = filename:join(PrivDir, "server_cert_and_key.pem"),
3904+
CCertAndKeyFile = filename:join(PrivDir, "client_cert_and_key.pem"),
3905+
SPemE = [{'Certificate', ServerCert, not_encrypted}, {SKeyType, SKey, not_encrypted}],
3906+
CPemE = [{'Certificate', ClientCert, not_encrypted}, {CKeyType, CKey, not_encrypted}],
3907+
ok = file:write_file(SCertAndKeyFile, public_key:pem_encode(SPemE)),
3908+
ok = file:write_file(CCertAndKeyFile, public_key:pem_encode(CPemE)),
3909+
ssl_test_lib:basic_test(replace_cerkey_der_with_file(CCertAndKeyFile, ClientOpts0),
3910+
replace_cerkey_der_with_file(SCertAndKeyFile, ServerOpts0), Config).
3911+
38743912
%%--------------------------------------------------------------------
38753913
%% Internal functions
38763914
%%--------------------------------------------------------------------
38773915

3916+
replace_cerkey_der_with_file(File, Opts0) ->
3917+
Opts = proplists:delete(key, proplists:delete(cert, Opts0)),
3918+
[{certfile, File}, {keyfile, File} | Opts].
3919+
3920+
38783921
establish_connection(Id, ServerNode, ServerOpts, ClientNode, ClientOpts, Hostname) ->
38793922
Server =
38803923
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// %CopyrightBegin%
2+
//
3+
// SPDX-License-Identifier: BSD-3-Clause
4+
//
5+
// Copyright (c) 2010 IETF Trust and the persons identified as the document authors. All rights reserved.
6+
// Copyright Ericsson AB 2025. All Rights Reserved.
7+
//
8+
// Redistribution and use in source and binary forms, with or without
9+
// modification, are permitted provided that the following conditions are met:
10+
//
11+
// 1. Redistributions of source code must retain the above copyright notice,
12+
// this list of conditions and the following disclaimer.
13+
//
14+
// 2. Redistributions in binary form must reproduce the above copyright notice,
15+
// this list of conditions and the following disclaimer in the documentation
16+
// and/or other materials provided with the distribution.
17+
//
18+
// 3. Neither the name of the copyright holder nor the names of its contributors
19+
// may be used to endorse or promote products derived from this software
20+
// without specific prior written permission.
21+
//
22+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
23+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32+
// POSSIBILITY OF SUCH DAMAGE.
33+
//
34+
// %CopyrightEnd%
35+
36+
-----BEGIN CERTIFICATE-----
37+
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
120+
AAAAABcmOEM=
121+
-----END CERTIFICATE-----
122+
123+
-----BEGIN PRIVATE KEY-----
124+
MDQCAQAwCwYJYIZIAWUDBAMRBCKAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZ
125+
GhscHR4f
126+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)