1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
18
%%% Description: Reading and writing of PEM type encoded files.
19
%% PEM encoded files have the following structure:
22
%% -----BEGIN SOMETHING-----<CR><LF>
23
%% <Base64 encoding line><CR><LF>
24
%% <Base64 encoding line><CR><LF>
26
%% -----END SOMETHING-----<CR><LF>
29
%% A file can contain several BEGIN/END blocks. Text lines between
30
%% blocks are ignored.
32
%% The encoding is divided into lines separated by <NL>, and each line
33
%% is precisely 64 characters long (excluding the <NL> characters,
34
%% except the last line which 64 characters long or shorter. <NL> may
35
%% follow the last line.
39
-export([read_file/1, read_file/2, write_file/2]).
40
-export([decode_key/2]).
42
-define(ENCODED_LINE_LENGTH, 64).
44
%%====================================================================
45
%% Internal application API
46
%%====================================================================
48
read_file(File, no_passwd).
50
read_file(File, Passwd) ->
51
{ok, Bin} = file:read_file(File),
52
Result = decode_file(split_bin(Bin), Passwd),
55
write_file(File, Ds) ->
56
file:write_file(File, encode_file(Ds)).
58
decode_key({_Type, Bin, not_encrypted}, _) ->
60
decode_key({_Type, Bin, {Chipher,Salt}}, Password) ->
61
decode_key(Bin, Password, Chipher, Salt).
63
%%--------------------------------------------------------------------
64
%%% Internal functions
65
%%--------------------------------------------------------------------
72
<<Line:N/binary, "\r\n", Rest/binary>> ->
73
[Line | split_bin(0, Rest)];
74
<<Line:N/binary, "\n", Rest/binary>> ->
75
[Line | split_bin(0, Rest)];
82
decode_file(Bin, Passwd) ->
83
decode_file(Bin, [], [Passwd]).
85
decode_file([<<"-----BEGIN CERTIFICATE REQUEST-----", _/binary>>|Rest], Ens, Info) ->
86
decode_file2(Rest, [], Ens, cert_req, Info);
87
decode_file([<<"-----BEGIN CERTIFICATE-----", _/binary>>|Rest], Ens, Info) ->
88
decode_file2(Rest, [], Ens, cert, Info);
89
decode_file([<<"-----BEGIN RSA PRIVATE KEY-----", _/binary>>|Rest], Ens, Info) ->
90
decode_file2(Rest, [], Ens, rsa_private_key, Info);
91
decode_file([<<"-----BEGIN DSA PRIVATE KEY-----", _/binary>>|Rest], Ens, Info) ->
92
decode_file2(Rest, [], Ens, dsa_private_key, Info);
93
decode_file([<<"-----BEGIN DH PARAMETERS-----", _/binary>>|Rest], Ens, Info) ->
94
decode_file2(Rest, [], Ens, dh_params, Info);
95
decode_file([_|Rest], Ens, Info) ->
96
decode_file(Rest, Ens, Info);
97
decode_file([], Ens, _Info) ->
98
{ok, lists:reverse(Ens)}.
100
decode_file2([<<"Proc-Type: 4,ENCRYPTED", _/binary>>| Rest0], RLs, Ens, Tag, Info0) ->
101
[InfoLine|Rest] = Rest0,
102
Info = dek_info(InfoLine, Info0),
103
decode_file2(Rest, RLs, Ens, Tag, Info);
104
decode_file2([<<"-----END", _/binary>>| Rest], RLs, Ens, Tag, Info0) ->
105
Cs = erlang:iolist_to_binary(lists:reverse(RLs)),
106
Bin = base64:mime_decode(Cs),
108
[Password, Cipher, SaltHex | Info1] ->
109
Salt = unhex(SaltHex),
110
Enc = {Cipher, Salt},
111
Decoded = decode_key(Bin, Password, Cipher, Salt),
112
decode_file(Rest, [{Tag, Decoded, Enc}| Ens], Info1);
114
decode_file(Rest, [{Tag, Bin, not_encrypted}| Ens], Info0)
116
decode_file2([L|Rest], RLs, Ens, Tag, Info0) ->
117
decode_file2(Rest, [L|RLs], Ens, Tag, Info0);
118
decode_file2([], _, Ens, _, _) ->
119
{ok, lists:reverse(Ens)}.
121
%% TODO Support same as decode_file
126
["-----BEGIN CERTIFICATE-----\n",
127
b64encode_and_split(Bin),
128
"-----END CERTIFICATE-----\n\n"];
131
["-----BEGIN CERTIFICATE REQUEST-----\n",
132
b64encode_and_split(Bin),
133
"-----END CERTIFICATE REQUEST-----\n\n"];
134
({rsa_private_key, Bin}) ->
136
["XXX Following key assumed not encrypted\n",
137
"-----BEGIN RSA PRIVATE KEY-----\n",
138
b64encode_and_split(Bin),
139
"-----END RSA PRIVATE KEY-----\n\n"]
142
dek_info(Line0, Info) ->
143
Line = binary_to_list(Line0),
144
[_, DekInfo0] = string:tokens(Line, ": "),
145
DekInfo1 = string:tokens(DekInfo0, ",\n"),
153
unhex([D1, D2 | Rest], Acc) ->
154
unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]).
156
decode_key(Data, no_passwd, _Alg, _Salt) ->
158
decode_key(Data, Password, "DES-CBC", Salt) ->
159
Key = password_to_key(Password, Salt, 8),
161
crypto:des_cbc_decrypt(Key, IV, Data);
162
decode_key(Data, Password, "DES-EDE3-CBC", Salt) ->
163
Key = password_to_key(Password, Salt, 24),
165
<<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
166
crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data).
168
password_to_key(Data, Salt, KeyLen) ->
169
<<Key:KeyLen/binary, _/binary>> =
170
password_to_key(<<>>, Data, Salt, KeyLen, <<>>),
173
password_to_key(_, _, _, Len, Acc) when Len =< 0 ->
175
password_to_key(Prev, Data, Salt, Len, Acc) ->
176
M = crypto:md5([Prev, Data, Salt]),
177
password_to_key(M, Data, Salt, Len - size(M), <<Acc/binary, M/binary>>).
179
b64encode_and_split(Bin) ->
180
split_lines(base64:encode(Bin)).
182
split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
183
[Text, $\n | split_lines(Rest)];