1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2011-2017. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%% Create test certificates
22
23-module(erl_make_certs).
24-include_lib("public_key/include/public_key.hrl").
25
26-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3,
27         gen_dsa/2, gen_ec/1,
28         pem_to_der/1, der_to_pem/2
29        ]).
30
31%%--------------------------------------------------------------------
32%% @doc  Create and return a der encoded certificate
33%%   Option                                         Default
34%%   -------------------------------------------------------
35%%   digest                                         sha1
36%%   validity                                       {date(), date() + week()}
37%%   version                                        3
38%%   subject                                        [] list of the following content
39%%      {name,  Name}
40%%      {email, Email}
41%%      {city,  City}
42%%      {state, State}
43%%      {org, Org}
44%%      {org_unit, OrgUnit}
45%%      {country, Country}
46%%      {serial, Serial}
47%%      {title, Title}
48%%      {dnQualifer, DnQ}
49%%   issuer = {Issuer, IssuerKey}                   true (i.e. a ca cert is created)
50%%                                                  (obs IssuerKey migth be {Key, Password}
51%%   key = KeyFile|KeyBin|rsa|dsa|ec                Subject PublicKey rsa, dsa or ec generates key
52%%
53%%
54%%   (OBS: The generated keys are for testing only)
55%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
56%% @end
57%%--------------------------------------------------------------------
58
59make_cert(Opts) ->
60    SubjectPrivateKey = get_key(Opts),
61    {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
62    Cert = public_key:pkix_sign(TBSCert, IssuerKey),
63    true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
64    {Cert, encode_key(SubjectPrivateKey)}.
65
66%%--------------------------------------------------------------------
67%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
68%% @spec (::string(), ::string(), {Cert,Key}) -> ok
69%% @end
70%%--------------------------------------------------------------------
71write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
72    ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"),
73			       [{'Certificate', Cert, not_encrypted}]),
74    ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
75
76%%--------------------------------------------------------------------
77%% @doc Creates a rsa key (OBS: for testing only)
78%%   the size are in bytes
79%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
80%% @end
81%%--------------------------------------------------------------------
82gen_rsa(Size) when is_integer(Size) ->
83    Key = gen_rsa2(Size),
84    {Key, encode_key(Key)}.
85
86%%--------------------------------------------------------------------
87%% @doc Creates a dsa key (OBS: for testing only)
88%%   the sizes are in bytes
89%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
90%% @end
91%%--------------------------------------------------------------------
92gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
93    Key = gen_dsa2(LSize, NSize),
94    {Key, encode_key(Key)}.
95
96%%--------------------------------------------------------------------
97%% @doc Creates a ec key (OBS: for testing only)
98%%   the sizes are in bytes
99%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
100%% @end
101%%--------------------------------------------------------------------
102gen_ec(Curve) when is_atom(Curve) ->
103    Key = gen_ec2(Curve),
104    {Key, encode_key(Key)}.
105
106%%--------------------------------------------------------------------
107%% @doc Verifies cert signatures
108%% @spec (::binary(), ::tuple()) -> ::boolean()
109%% @end
110%%--------------------------------------------------------------------
111verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
112    Key = decode_key(DerKey),
113    case Key of
114	{#'RSAPrivateKey'{modulus=Mod, publicExponent=Exp}, #'RSASSA-PSS-params'{}=P} ->
115	    public_key:pkix_verify(DerEncodedCert,
116				   {#'RSAPublicKey'{modulus=Mod, publicExponent=Exp}, P});
117	#'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
118	    public_key:pkix_verify(DerEncodedCert,
119				   #'RSAPublicKey'{modulus=Mod, publicExponent=Exp});
120	#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
121	    public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}});
122	#'ECPrivateKey'{version = _Version, privateKey = _PrivKey,
123			parameters = Params, publicKey = PubKey} ->
124	    public_key:pkix_verify(DerEncodedCert, {#'ECPoint'{point = PubKey}, Params})
125    end.
126
127%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128
129get_key(Opts) ->
130    case proplists:get_value(key, Opts) of
131	undefined -> make_key(rsa, Opts);
132	rsa ->       make_key(rsa, Opts);
133	dsa ->       make_key(dsa, Opts);
134	ec ->        make_key(ec, Opts);
135	Key ->
136	    Password = proplists:get_value(password, Opts, no_passwd),
137	    decode_key(Key, Password)
138    end.
139
140decode_key({#'RSAPrivateKey'{},#'RSASSA-PSS-params'{}}=Key) ->
141	Key;
142decode_key({Key, Pw}) ->
143    decode_key(Key, Pw);
144decode_key(Key) ->
145    decode_key(Key, no_passwd).
146
147
148decode_key(#'RSAPublicKey'{} = Key,_) ->
149    Key;
150decode_key(#'RSAPrivateKey'{} = Key,_) ->
151    Key;
152decode_key({#'RSAPrivateKey'{},#'RSASSA-PSS-params'{}} = Key,_) ->
153    Key;
154decode_key(#'DSAPrivateKey'{} = Key,_) ->
155    Key;
156decode_key(#'ECPrivateKey'{} = Key,_) ->
157    Key;
158decode_key(PemEntry = {_,_,_}, Pw) ->
159    public_key:pem_entry_decode(PemEntry, Pw);
160decode_key(PemBin, Pw) ->
161    [KeyInfo] = public_key:pem_decode(PemBin),
162    decode_key(KeyInfo, Pw).
163
164encode_key(Key = #'RSAPrivateKey'{}) ->
165    {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
166    {'RSAPrivateKey', Der, not_encrypted};
167encode_key(Key = {#'RSAPrivateKey'{},#'RSASSA-PSS-params'{}}) ->
168    Der = public_key:der_encode('PrivateKeyInfo', Key),
169    {'PrivateKeyInfo', Der, not_encrypted};
170encode_key(Key = #'DSAPrivateKey'{}) ->
171    {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
172    {'DSAPrivateKey', Der, not_encrypted};
173encode_key(Key = #'ECPrivateKey'{}) ->
174    {ok, Der} = 'OTP-PUB-KEY':encode('ECPrivateKey', Key),
175    {'ECPrivateKey', Der, not_encrypted}.
176
177make_tbs(SubjectKey, Opts) ->
178    Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
179
180    IssuerProp = proplists:get_value(issuer, Opts, true),
181    {Issuer, IssuerKey}  = issuer(IssuerProp, Opts, SubjectKey),
182
183    {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
184
185    SignAlgo = #'SignatureAlgorithm'{algorithm  = Algo,
186				     parameters = Parameters},
187    Subject = case IssuerProp of
188		  true -> %% Is a Root Ca
189		      Issuer;
190		  _ ->
191		      subject(proplists:get_value(subject, Opts),false)
192	      end,
193    Rnd = rand:uniform( 1000000000000 ),
194    %% 1 =< Rnd < 1000000000001
195    {#'OTPTBSCertificate'{serialNumber = Rnd,
196			  signature    = SignAlgo,
197			  issuer       = Issuer,
198			  validity     = validity(Opts),
199			  subject      = Subject,
200			  subjectPublicKeyInfo = publickey(SubjectKey),
201			  version      = Version,
202			  extensions   = extensions(Opts)
203			 }, IssuerKey}.
204
205issuer(true, Opts, SubjectKey) ->
206    %% Self signed
207    {subject(proplists:get_value(subject, Opts), true), SubjectKey};
208issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) ->
209    {issuer_der(Issuer), decode_key(IssuerKey)};
210issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) ->
211    {ok, [{cert, Cert, _}|_]} = pem_to_der(File),
212    {issuer_der(Cert), decode_key(IssuerKey)}.
213
214issuer_der(Issuer) ->
215    Decoded = public_key:pkix_decode_cert(Issuer, otp),
216    #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
217    #'OTPTBSCertificate'{subject=Subject} = Tbs,
218    Subject.
219
220subject(undefined, IsRootCA) ->
221    User = if IsRootCA -> "RootCA"; true -> os:getenv("USER", "test_user") end,
222    Opts = [{email, User ++ "@erlang.org"},
223	    {name, User},
224	    {city, "Stockholm"},
225	    {country, "SE"},
226	    {org, "erlang"},
227	    {org_unit, "testing dep"}],
228    subject(Opts);
229subject(Opts, _) ->
230    subject(Opts).
231
232subject(SubjectOpts) when is_list(SubjectOpts) ->
233    Encode = fun(Opt) ->
234		     {Type,Value} = subject_enc(Opt),
235		     [#'AttributeTypeAndValue'{type=Type, value=Value}]
236	     end,
237    {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
238
239%% Fill in the blanks
240subject_enc({name,  Name}) ->       {?'id-at-commonName', {printableString, Name}};
241subject_enc({email, Email}) ->      {?'id-emailAddress', Email};
242subject_enc({city,  City}) ->       {?'id-at-localityName', {printableString, City}};
243subject_enc({state, State}) ->      {?'id-at-stateOrProvinceName', {printableString, State}};
244subject_enc({org, Org}) ->          {?'id-at-organizationName', {printableString, Org}};
245subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
246subject_enc({country, Country}) ->  {?'id-at-countryName', Country};
247subject_enc({serial, Serial}) ->    {?'id-at-serialNumber', Serial};
248subject_enc({title, Title}) ->      {?'id-at-title', {printableString, Title}};
249subject_enc({dnQualifer, DnQ}) ->   {?'id-at-dnQualifier', DnQ};
250subject_enc(Other) ->               Other.
251
252
253extensions(Opts) ->
254    case proplists:get_value(extensions, Opts, []) of
255	false ->
256	    asn1_NOVALUE;
257	Exts  ->
258	    lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
259    end.
260
261default_extensions(Exts) ->
262    Def = [{key_usage, default},
263	   {subject_altname, undefined},
264	   {issuer_altname, undefined},
265	   {basic_constraints, default},
266	   {name_constraints, undefined},
267	   {policy_constraints, undefined},
268	   {ext_key_usage, undefined},
269	   {inhibit_any, undefined},
270	   {auth_key_id, undefined},
271	   {subject_key_id, undefined},
272	   {policy_mapping, undefined}],
273    Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
274    Exts ++ lists:foldl(Filter, Def, Exts).
275
276extension({_, undefined}) -> [];
277
278extension({basic_constraints, Data}) ->
279    case Data of
280	default ->
281	    #'Extension'{extnID = ?'id-ce-basicConstraints',
282			 extnValue = #'BasicConstraints'{cA=true},
283			 critical=true};
284	false ->
285	    [];
286	Len when is_integer(Len) ->
287	    #'Extension'{extnID = ?'id-ce-basicConstraints',
288			 extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
289			 critical=true};
290	_ ->
291	    #'Extension'{extnID = ?'id-ce-basicConstraints',
292			 extnValue = Data}
293    end;
294
295extension({key_usage, default}) ->
296    #'Extension'{extnID = ?'id-ce-keyUsage',
297		 extnValue = [keyCertSign], critical = true};
298
299extension({Id, Data, Critical}) ->
300    #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
301
302
303publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
304    Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
305    Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
306    #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
307			       subjectPublicKey = Public};
308publickey({#'RSAPrivateKey'{modulus=N, publicExponent=E},#'RSASSA-PSS-params'{}=P}) ->
309    Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
310    Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-RSASSA-PSS', parameters=P},
311    #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
312			       subjectPublicKey = Public};
313publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
314    Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa',
315				 parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}},
316    #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y};
317publickey(#'ECPrivateKey'{version = _Version,
318			  privateKey = _PrivKey,
319			  parameters = Params,
320			  publicKey = PubKey}) ->
321    Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params},
322    #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
323			       subjectPublicKey = #'ECPoint'{point = PubKey}}.
324
325validity(Opts) ->
326    DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
327    DefTo0   = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
328    {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
329    Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
330    #'Validity'{notBefore={generalTime, Format(DefFrom)},
331		notAfter ={generalTime, Format(DefTo)}}.
332
333sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
334    Type = case proplists:get_value(digest, Opts, sha1) of
335	       sha1 ->   ?'sha1WithRSAEncryption';
336	       sha512 -> ?'sha512WithRSAEncryption';
337	       sha384 -> ?'sha384WithRSAEncryption';
338	       sha256 -> ?'sha256WithRSAEncryption';
339	       md5    -> ?'md5WithRSAEncryption';
340	       md2    -> ?'md2WithRSAEncryption'
341	   end,
342    {Type, 'NULL'};
343sign_algorithm({#'RSAPrivateKey'{},#'RSASSA-PSS-params'{}=P}, Opts) ->
344    {?'id-RSASSA-PSS', P};
345sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
346    {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}};
347sign_algorithm(#'ECPrivateKey'{parameters = Parms}, Opts) ->
348    Type = case proplists:get_value(digest, Opts, sha1) of
349	       sha1 ->   ?'ecdsa-with-SHA1';
350	       sha512 -> ?'ecdsa-with-SHA512';
351	       sha384 -> ?'ecdsa-with-SHA384';
352	       sha256 -> ?'ecdsa-with-SHA256'
353	   end,
354    {Type, Parms}.
355
356make_key(rsa, _Opts) ->
357    %% (OBS: for testing only)
358    gen_rsa2(64);
359make_key(dsa, _Opts) ->
360    gen_dsa2(128, 20);  %% Bytes i.e. {1024, 160}
361make_key(ec, _Opts) ->
362    %% (OBS: for testing only)
363    gen_ec2(secp256k1).
364
365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366%% RSA key generation  (OBS: for testing only)
367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368
369gen_rsa2(Size) ->
370    try
371        %% The numbers 2048,17 is choosen to not cause the cryptolib on
372        %% FIPS-enabled test machines be mad at us.
373        public_key:generate_key({rsa, 2048, 17})
374    catch
375        error:notsup ->
376            %% Disabled dirty_schedulers => crypto:generate_key not working
377            weak_gen_rsa2(Size)
378    end.
379
380
381-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
382		       47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
383
384weak_gen_rsa2(Size) ->
385    P = prime(Size),
386    Q = prime(Size),
387    N = P*Q,
388    Tot = (P - 1) * (Q - 1),
389    [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
390    {D1,D2} = extended_gcd(E, Tot),
391    D = erlang:max(D1,D2),
392    case D < E of
393	true ->
394	    gen_rsa2(Size);
395	false ->
396	    {Co1,Co2} = extended_gcd(Q, P),
397	    Co = erlang:max(Co1,Co2),
398	    #'RSAPrivateKey'{version = 'two-prime',
399			     modulus = N,
400			     publicExponent  = E,
401			     privateExponent = D,
402			     prime1 = P,
403			     prime2 = Q,
404			     exponent1 = D rem (P-1),
405			     exponent2 = D rem (Q-1),
406			     coefficient = Co
407			    }
408    end.
409
410%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411%% DSA key generation  (OBS: for testing only)
412%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
413%% and the fips_186-3.pdf
414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415gen_dsa2(LSize, NSize) ->
416    Q  = prime(NSize),  %% Choose N-bit prime Q
417    X0 = prime(LSize),
418    P0 = prime((LSize div 2) +1),
419
420    %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
421    case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
422	error ->
423	    gen_dsa2(LSize, NSize);
424	P ->
425	    G = crypto:mod_pow(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
426	    %%                 such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
427
428	    X = prime(20),               %% Choose x by some random method, where 0 < x < q.
429	    Y = crypto:mod_pow(G, X, P), %% Calculate y = g^x mod p.
430
431	    #'DSAPrivateKey'{version=0, p = P, q = Q,
432			     g = crypto:bytes_to_integer(G), y = crypto:bytes_to_integer(Y), x = X}
433    end.
434
435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436%% EC key generation  (OBS: for testing only)
437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438
439gen_ec2(CurveId) ->
440     {PubKey, PrivKey} = crypto:generate_key(ecdh, CurveId),
441
442    #'ECPrivateKey'{version = 1,
443		    privateKey = PrivKey,
444		    parameters = {namedCurve, pubkey_cert_records:namedCurves(CurveId)},
445		    publicKey = PubKey}.
446
447%% See fips_186-3.pdf
448dsa_search(T, P0, Q, Iter) when Iter > 0 ->
449    P = 2*T*Q*P0 + 1,
450    case is_prime(P, 50) of
451	true -> P;
452	false -> dsa_search(T+1, P0, Q, Iter-1)
453    end;
454dsa_search(_,_,_,_) ->
455    error.
456
457
458%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459prime(ByteSize) ->
460    Rand = odd_rand(ByteSize),
461    prime_odd(Rand, 0).
462
463prime_odd(Rand, N) ->
464    case is_prime(Rand, 50) of
465	true ->
466	    Rand;
467	false ->
468	    prime_odd(Rand+2, N+1)
469    end.
470
471%% see http://en.wikipedia.org/wiki/Fermat_primality_test
472is_prime(_, 0) -> true;
473is_prime(Candidate, Test) ->
474    CoPrime = odd_rand(10000, Candidate),
475    Result = crypto:mod_pow(CoPrime, Candidate, Candidate) ,
476    is_prime(CoPrime, crypto:bytes_to_integer(Result), Candidate, Test).
477
478is_prime(CoPrime, CoPrime, Candidate, Test) ->
479    is_prime(Candidate, Test-1);
480is_prime(_,_,_,_) ->
481    false.
482
483odd_rand(Size) ->
484    Min = 1 bsl (Size*8-1),
485    Max = (1 bsl (Size*8))-1,
486    odd_rand(Min, Max).
487
488odd_rand(Min,Max) ->
489    %% Odd random number N such that Min =< N =< Max
490    Rand = (Min-1) + rand:uniform(Max-Min), %  Min =< Rand < Max
491    case Rand rem 2 of
492	0 ->
493	    Rand + 1;
494	_ ->
495	    Rand
496    end.
497
498extended_gcd(A, B) ->
499    case A rem B of
500	0 ->
501	    {0, 1};
502	N ->
503	    {X, Y} = extended_gcd(B, N),
504	    {Y, X-Y*(A div B)}
505    end.
506
507pem_to_der(File) ->
508    {ok, PemBin} = file:read_file(File),
509    public_key:pem_decode(PemBin).
510
511der_to_pem(File, Entries) ->
512    PemBin = public_key:pem_encode(Entries),
513    file:write_file(File, PemBin).
514
515