4
%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
24
%%% Purpose: Reading and writing of PEM type encoded files for SSL.
26
%% NB write_file/2 is only preliminary.
28
%% PEM encoded files have the following structure:
31
%% -----BEGIN SOMETHING-----<CR><LF>
32
%% <Base64 encoding line><CR><LF>
33
%% <Base64 encoding line><CR><LF>
35
%% -----END SOMETHING-----<CR><LF>
38
%% A file can contain several BEGIN/END blocks. Text lines between
39
%% blocks are ignored.
41
-export([read_file/1, read_file/2, write_file/2]).
43
%% Read a PEM file and return each decoding as a binary.
46
read_file(File, no_passwd).
48
read_file(File, Passwd) ->
49
{ok, Fd} = file:open(File, [read]),
50
Result = decode_file(Fd, Passwd),
54
decode_file(Fd, Passwd) ->
55
decode_file(Fd, [], [], notag, [Passwd]).
57
decode_file(Fd, _RLs, Ens, notag, Info) ->
58
case io:get_line(Fd, "") of
59
"-----BEGIN CERTIFICATE REQUEST-----" ++ _ ->
60
decode_file(Fd, [], Ens, cert_req, Info);
61
"-----BEGIN CERTIFICATE-----" ++ _ ->
62
decode_file(Fd, [], Ens, cert, Info);
63
"-----BEGIN RSA PRIVATE KEY-----" ++ _ ->
64
decode_file(Fd, [], Ens, rsa_private_key, Info);
66
{ok, lists:reverse(Ens)};
68
decode_file(Fd, [], Ens, notag, Info)
70
decode_file(Fd, RLs, Ens, Tag, Info0) ->
71
case io:get_line(Fd, "") of
72
"Proc-Type: 4,ENCRYPTED"++_ ->
73
Info = dek_info(Fd, Info0),
74
decode_file(Fd, RLs, Ens, Tag, Info);
75
"-----END" ++ _ -> % XXX sloppy
76
Cs = lists:flatten(lists:reverse(RLs)),
77
Bin = ssl_base64:join_decode(Cs),
79
[Password, Cipher, SaltHex | Info1] ->
80
Decoded = decode_key(Bin, Password, Cipher, unhex(SaltHex)),
81
decode_file(Fd, [], [{Tag, Decoded}| Ens], notag, Info1);
83
decode_file(Fd, [], [{Tag, Bin}| Ens], notag, Info0)
86
{ok, lists:reverse(Ens)};
88
decode_file(Fd, [L|RLs], Ens, Tag, Info0)
92
Line = io:get_line(Fd, ""),
93
[_, DekInfo0] = string:tokens(Line, ": "),
94
DekInfo1 = string:tokens(DekInfo0, ",\n"),
102
unhex([D1, D2 | Rest], Acc) ->
103
unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]).
105
decode_key(Data, Password, "DES-CBC", Salt) ->
106
Key = password_to_key(Password, Salt, 8),
108
crypto:des_cbc_decrypt(Key, IV, Data);
109
decode_key(Data, Password, "DES-EDE3-CBC", Salt) ->
110
Key = password_to_key(Password, Salt, 24),
112
<<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
113
crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data).
115
write_file(File, Ds) ->
116
file:write_file(File, encode_file(Ds)).
119
[encode_file_1(D) || D <- Ds].
121
encode_file_1({cert, Bin}) ->
123
["-----BEGIN CERTIFICATE-----\n",
124
ssl_base64:encode_split(Bin),
125
"-----END CERTIFICATE-----\n\n"];
126
encode_file_1({cert_req, Bin}) ->
128
["-----BEGIN CERTIFICATE REQUEST-----\n",
129
ssl_base64:encode_split(Bin),
130
"-----END CERTIFICATE REQUEST-----\n\n"];
131
encode_file_1({rsa_private_key, Bin}) ->
133
["XXX Following key assumed not encrypted\n",
134
"-----BEGIN RSA PRIVATE KEY-----\n",
135
ssl_base64:encode_split(Bin),
136
"-----END RSA PRIVATE KEY-----\n\n"].
138
password_to_key(Data, Salt, KeyLen) ->
139
<<Key:KeyLen/binary, _/binary>> =
140
password_to_key(<<>>, Data, Salt, KeyLen, <<>>),
143
password_to_key(_, _, _, Len, Acc) when Len =< 0 ->
145
password_to_key(Prev, Data, Salt, Len, Acc) ->
146
M = crypto:md5([Prev, Data, Salt]),
147
password_to_key(M, Data, Salt, Len - byte_size(M), <<Acc/binary, M/binary>>).