~ubuntu-branches/ubuntu/trusty/erlang/trusty

« back to all changes in this revision

Viewing changes to lib/public_key/test/erl_make_certs.erl

  • Committer: Bazaar Package Importer
  • Author(s): Clint Byrum
  • Date: 2011-05-05 15:48:43 UTC
  • mfrom: (3.5.13 sid)
  • Revision ID: james.westby@ubuntu.com-20110505154843-0om6ekzg6m7ugj27
Tags: 1:14.b.2-dfsg-3ubuntu1
* Merge from debian unstable.  Remaining changes:
  - Drop libwxgtk2.8-dev build dependency. Wx isn't in main, and not
    supposed to.
  - Drop erlang-wx binary.
  - Drop erlang-wx dependency from -megaco, -common-test, and -reltool, they
    do not really need wx. Also drop it from -debugger; the GUI needs wx,
    but it apparently has CLI bits as well, and is also needed by -megaco,
    so let's keep the package for now.
  - debian/patches/series: Do what I meant, and enable build-options.patch
    instead.
* Additional changes:
  - Drop erlang-wx from -et
* Dropped Changes:
  - patches/pcre-crash.patch: CVE-2008-2371: outer level option with
    alternatives caused crash. (Applied Upstream)
  - fix for ssl certificate verification in newSSL: 
    ssl_cacertfile_fix.patch (Applied Upstream)
  - debian/patches/series: Enable native.patch again, to get stripped beam
    files and reduce the package size again. (build-options is what
    actually accomplished this)
  - Remove build-options.patch on advice from upstream and because it caused
    odd build failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
%%
 
2
%% %CopyrightBegin%
 
3
%%
 
4
%% Copyright Ericsson AB 2010. All Rights Reserved.
 
5
%%
 
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/.
 
11
%%
 
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
 
15
%% under the License.
 
16
%%
 
17
%% %CopyrightEnd%
 
18
%%
 
19
 
 
20
%% Create test certificates
 
21
 
 
22
-module(erl_make_certs).
 
23
-include_lib("public_key/include/public_key.hrl").
 
24
 
 
25
-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]).
 
26
-compile(export_all).
 
27
 
 
28
%%--------------------------------------------------------------------
 
29
%% @doc  Create and return a der encoded certificate
 
30
%%   Option                                         Default
 
31
%%   -------------------------------------------------------
 
32
%%   digest                                         sha1
 
33
%%   validity                                       {date(), date() + week()}
 
34
%%   version                                        3
 
35
%%   subject                                        [] list of the following content
 
36
%%      {name,  Name}
 
37
%%      {email, Email} 
 
38
%%      {city,  City}
 
39
%%      {state, State}
 
40
%%      {org, Org}
 
41
%%      {org_unit, OrgUnit}
 
42
%%      {country, Country} 
 
43
%%      {serial, Serial}
 
44
%%      {title, Title}
 
45
%%      {dnQualifer, DnQ}
 
46
%%   issuer = {Issuer, IssuerKey}                   true (i.e. a ca cert is created) 
 
47
%%                                                  (obs IssuerKey migth be {Key, Password}
 
48
%%   key = KeyFile|KeyBin|rsa|dsa                   Subject PublicKey rsa or dsa generates key
 
49
%%   
 
50
%%
 
51
%%   (OBS: The generated keys are for testing only)
 
52
%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
 
53
%% @end
 
54
%%--------------------------------------------------------------------
 
55
 
 
56
make_cert(Opts) ->
 
57
    SubjectPrivateKey = get_key(Opts),
 
58
    {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
 
59
    Cert = public_key:pkix_sign(TBSCert, IssuerKey),
 
60
    true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
 
61
    {Cert, encode_key(SubjectPrivateKey)}.
 
62
 
 
63
%%--------------------------------------------------------------------
 
64
%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
 
65
%% @spec (::string(), ::string(), {Cert,Key}) -> ok
 
66
%% @end
 
67
%%--------------------------------------------------------------------
 
68
write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
 
69
    ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"),
 
70
                               [{'Certificate', Cert, not_encrypted}]),
 
71
    ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
 
72
 
 
73
%%--------------------------------------------------------------------
 
74
%% @doc Creates a rsa key (OBS: for testing only)
 
75
%%   the size are in bytes
 
76
%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
 
77
%% @end
 
78
%%--------------------------------------------------------------------
 
79
gen_rsa(Size) when is_integer(Size) ->
 
80
    Key = gen_rsa2(Size),
 
81
    {Key, encode_key(Key)}.
 
82
 
 
83
%%--------------------------------------------------------------------
 
84
%% @doc Creates a dsa key (OBS: for testing only)
 
85
%%   the sizes are in bytes
 
86
%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
 
87
%% @end
 
88
%%--------------------------------------------------------------------
 
89
gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
 
90
    Key = gen_dsa2(LSize, NSize),
 
91
    {Key, encode_key(Key)}.
 
92
 
 
93
%%--------------------------------------------------------------------
 
94
%% @doc Verifies cert signatures
 
95
%% @spec (::binary(), ::tuple()) -> ::boolean()
 
96
%% @end
 
97
%%--------------------------------------------------------------------
 
98
verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
 
99
    Key = decode_key(DerKey),
 
100
    case Key of 
 
101
        #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
 
102
            public_key:pkix_verify(DerEncodedCert, 
 
103
                                   #'RSAPublicKey'{modulus=Mod, publicExponent=Exp});
 
104
        #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
 
105
            public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}})
 
106
    end.
 
107
 
 
108
%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
109
 
 
110
get_key(Opts) ->
 
111
    case proplists:get_value(key, Opts) of
 
112
        undefined -> make_key(rsa, Opts);
 
113
        rsa ->       make_key(rsa, Opts);
 
114
        dsa ->       make_key(dsa, Opts);
 
115
        Key ->
 
116
            Password = proplists:get_value(password, Opts, no_passwd),
 
117
            decode_key(Key, Password)
 
118
    end.
 
119
 
 
120
decode_key({Key, Pw}) ->
 
121
    decode_key(Key, Pw);
 
122
decode_key(Key) ->
 
123
    decode_key(Key, no_passwd).
 
124
    
 
125
 
 
126
decode_key(#'RSAPublicKey'{} = Key,_) ->
 
127
    Key;
 
128
decode_key(#'RSAPrivateKey'{} = Key,_) ->
 
129
    Key;
 
130
decode_key(#'DSAPrivateKey'{} = Key,_) ->
 
131
    Key;
 
132
decode_key(PemEntry = {_,_,_}, Pw) ->
 
133
    public_key:pem_entry_decode(PemEntry, Pw);
 
134
decode_key(PemBin, Pw) ->
 
135
    [KeyInfo] = public_key:pem_decode(PemBin),
 
136
    decode_key(KeyInfo, Pw).
 
137
 
 
138
encode_key(Key = #'RSAPrivateKey'{}) ->
 
139
    {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
 
140
    {'RSAPrivateKey', list_to_binary(Der), not_encrypted};   
 
141
encode_key(Key = #'DSAPrivateKey'{}) ->
 
142
    {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
 
143
    {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
 
144
 
 
145
make_tbs(SubjectKey, Opts) ->    
 
146
    Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
 
147
 
 
148
    IssuerProp = proplists:get_value(issuer, Opts, true),
 
149
    {Issuer, IssuerKey}  = issuer(IssuerProp, Opts, SubjectKey),
 
150
 
 
151
    {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
 
152
    
 
153
    SignAlgo = #'SignatureAlgorithm'{algorithm  = Algo,
 
154
                                     parameters = Parameters},    
 
155
    Subject = case IssuerProp of
 
156
                  true -> %% Is a Root Ca
 
157
                      Issuer;
 
158
                  _ ->
 
159
                      subject(proplists:get_value(subject, Opts),false)
 
160
              end,
 
161
 
 
162
    {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1,
 
163
                          signature    = SignAlgo,
 
164
                          issuer       = Issuer,
 
165
                          validity     = validity(Opts),
 
166
                          subject      = Subject,
 
167
                          subjectPublicKeyInfo = publickey(SubjectKey),
 
168
                          version      = Version,
 
169
                          extensions   = extensions(Opts)
 
170
                         }, IssuerKey}.
 
171
 
 
172
issuer(true, Opts, SubjectKey) ->
 
173
    %% Self signed
 
174
    {subject(proplists:get_value(subject, Opts), true), SubjectKey};
 
175
issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) ->
 
176
    {issuer_der(Issuer), decode_key(IssuerKey)};
 
177
issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) ->
 
178
    {ok, [{cert, Cert, _}|_]} = public_key:pem_to_der(File),
 
179
    {issuer_der(Cert), decode_key(IssuerKey)}.
 
180
 
 
181
issuer_der(Issuer) ->
 
182
    Decoded = public_key:pkix_decode_cert(Issuer, otp),
 
183
    #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
 
184
    #'OTPTBSCertificate'{subject=Subject} = Tbs,
 
185
    Subject.
 
186
 
 
187
subject(undefined, IsRootCA) ->
 
188
    User = if IsRootCA -> "RootCA"; true -> os:getenv("USER") end,
 
189
    Opts = [{email, User ++ "@erlang.org"},
 
190
            {name, User},
 
191
            {city, "Stockholm"},
 
192
            {country, "SE"},
 
193
            {org, "erlang"},
 
194
            {org_unit, "testing dep"}],
 
195
    subject(Opts);
 
196
subject(Opts, _) ->
 
197
    subject(Opts).
 
198
 
 
199
subject(SubjectOpts) when is_list(SubjectOpts) ->
 
200
    Encode = fun(Opt) ->
 
201
                     {Type,Value} = subject_enc(Opt),
 
202
                     [#'AttributeTypeAndValue'{type=Type, value=Value}]
 
203
             end,
 
204
    {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
 
205
 
 
206
%% Fill in the blanks
 
207
subject_enc({name,  Name}) ->       {?'id-at-commonName', {printableString, Name}};
 
208
subject_enc({email, Email}) ->      {?'id-emailAddress', Email};
 
209
subject_enc({city,  City}) ->       {?'id-at-localityName', {printableString, City}};
 
210
subject_enc({state, State}) ->      {?'id-at-stateOrProvinceName', {printableString, State}};
 
211
subject_enc({org, Org}) ->          {?'id-at-organizationName', {printableString, Org}};
 
212
subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
 
213
subject_enc({country, Country}) ->  {?'id-at-countryName', Country};
 
214
subject_enc({serial, Serial}) ->    {?'id-at-serialNumber', Serial};
 
215
subject_enc({title, Title}) ->      {?'id-at-title', {printableString, Title}};
 
216
subject_enc({dnQualifer, DnQ}) ->   {?'id-at-dnQualifier', DnQ};
 
217
subject_enc(Other) ->               Other.
 
218
 
 
219
 
 
220
extensions(Opts) ->
 
221
    case proplists:get_value(extensions, Opts, []) of
 
222
        false -> 
 
223
            asn1_NOVALUE;
 
224
        Exts  -> 
 
225
            lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
 
226
    end.
 
227
 
 
228
default_extensions(Exts) ->
 
229
    Def = [{key_usage,undefined}, 
 
230
           {subject_altname, undefined},
 
231
           {issuer_altname, undefined},
 
232
           {basic_constraints, default},
 
233
           {name_constraints, undefined},
 
234
           {policy_constraints, undefined},
 
235
           {ext_key_usage, undefined},
 
236
           {inhibit_any, undefined},
 
237
           {auth_key_id, undefined},
 
238
           {subject_key_id, undefined},
 
239
           {policy_mapping, undefined}],
 
240
    Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
 
241
    Exts ++ lists:foldl(Filter, Def, Exts).
 
242
        
 
243
extension({_, undefined}) -> [];
 
244
extension({basic_constraints, Data}) ->
 
245
    case Data of
 
246
        default ->
 
247
            #'Extension'{extnID = ?'id-ce-basicConstraints',
 
248
                         extnValue = #'BasicConstraints'{cA=true},
 
249
                         critical=true};
 
250
        false -> 
 
251
            [];
 
252
        Len when is_integer(Len) ->
 
253
            #'Extension'{extnID = ?'id-ce-basicConstraints',
 
254
                         extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
 
255
                         critical=true};
 
256
        _ ->
 
257
            #'Extension'{extnID = ?'id-ce-basicConstraints',
 
258
                         extnValue = Data}
 
259
    end;
 
260
extension({Id, Data, Critical}) ->
 
261
    #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
 
262
 
 
263
 
 
264
publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
 
265
    Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
 
266
    Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
 
267
    #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
 
268
                               subjectPublicKey = Public};
 
269
publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
 
270
    Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', 
 
271
                                 parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}},
 
272
    #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}.
 
273
 
 
274
validity(Opts) ->
 
275
    DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
 
276
    DefTo0   = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
 
277
    {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
 
278
    Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
 
279
    #'Validity'{notBefore={generalTime, Format(DefFrom)},
 
280
                notAfter ={generalTime, Format(DefTo)}}.
 
281
 
 
282
sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
 
283
    Type = case proplists:get_value(digest, Opts, sha1) of
 
284
               sha1 ->   ?'sha1WithRSAEncryption';
 
285
               sha512 -> ?'sha512WithRSAEncryption';
 
286
               sha384 -> ?'sha384WithRSAEncryption';
 
287
               sha256 -> ?'sha256WithRSAEncryption';
 
288
               md5    -> ?'md5WithRSAEncryption';
 
289
               md2    -> ?'md2WithRSAEncryption'
 
290
           end,
 
291
    {Type, 'NULL'};
 
292
sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
 
293
    {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}.
 
294
 
 
295
make_key(rsa, _Opts) ->
 
296
    %% (OBS: for testing only)
 
297
    gen_rsa2(64);
 
298
make_key(dsa, _Opts) ->
 
299
    gen_dsa2(128, 20).  %% Bytes i.e. {1024, 160} 
 
300
    
 
301
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
302
%% RSA key generation  (OBS: for testing only)
 
303
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
304
 
 
305
-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
 
306
                       47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
 
307
 
 
308
gen_rsa2(Size) ->
 
309
    P = prime(Size),
 
310
    Q = prime(Size),
 
311
    N = P*Q,
 
312
    Tot = (P - 1) * (Q - 1),
 
313
    [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
 
314
    {D1,D2} = extended_gcd(E, Tot),
 
315
    D = erlang:max(D1,D2),
 
316
    case D < E of
 
317
        true ->
 
318
            gen_rsa2(Size);
 
319
        false ->
 
320
            {Co1,Co2} = extended_gcd(Q, P),
 
321
            Co = erlang:max(Co1,Co2),
 
322
            #'RSAPrivateKey'{version = 'two-prime',
 
323
                             modulus = N,
 
324
                             publicExponent  = E,
 
325
                             privateExponent = D, 
 
326
                             prime1 = P, 
 
327
                             prime2 = Q, 
 
328
                             exponent1 = D rem (P-1), 
 
329
                             exponent2 = D rem (Q-1), 
 
330
                             coefficient = Co
 
331
                            }
 
332
    end.
 
333
 
 
334
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
335
%% DSA key generation  (OBS: for testing only)
 
336
%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
 
337
%% and the fips_186-3.pdf
 
338
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
339
gen_dsa2(LSize, NSize) ->
 
340
    Q  = prime(NSize),  %% Choose N-bit prime Q
 
341
    X0 = prime(LSize),
 
342
    P0 = prime((LSize div 2) +1),
 
343
    
 
344
    %% Choose L-bit prime modulus P such that p–1 is a multiple of q.
 
345
    case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
 
346
        error -> 
 
347
            gen_dsa2(LSize, NSize);
 
348
        P ->        
 
349
            G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
 
350
            %%                 such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used.
 
351
            
 
352
            X = prime(20),               %% Choose x by some random method, where 0 < x < q.
 
353
            Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
 
354
            
 
355
            #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X}
 
356
    end.
 
357
    
 
358
%% See fips_186-3.pdf
 
359
dsa_search(T, P0, Q, Iter) when Iter > 0 ->
 
360
    P = 2*T*Q*P0 + 1,
 
361
    case is_prime(crypto:mpint(P), 50) of
 
362
        true -> P;
 
363
        false -> dsa_search(T+1, P0, Q, Iter-1)
 
364
    end;
 
365
dsa_search(_,_,_,_) -> 
 
366
    error.
 
367
 
 
368
 
 
369
%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
370
prime(ByteSize) ->
 
371
    Rand = odd_rand(ByteSize),
 
372
    crypto:erlint(prime_odd(Rand, 0)).
 
373
 
 
374
prime_odd(Rand, N) ->
 
375
    case is_prime(Rand, 50) of
 
376
        true -> 
 
377
            Rand;
 
378
        false -> 
 
379
            NotPrime = crypto:erlint(Rand),
 
380
            prime_odd(crypto:mpint(NotPrime+2), N+1)
 
381
    end.
 
382
 
 
383
%% see http://en.wikipedia.org/wiki/Fermat_primality_test
 
384
is_prime(_, 0) -> true;
 
385
is_prime(Candidate, Test) -> 
 
386
    CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate),
 
387
    case crypto:mod_exp(CoPrime, Candidate, Candidate) of
 
388
        CoPrime -> is_prime(Candidate, Test-1);
 
389
        _       -> false
 
390
    end.
 
391
 
 
392
odd_rand(Size) ->
 
393
    Min = 1 bsl (Size*8-1),
 
394
    Max = (1 bsl (Size*8))-1,
 
395
    odd_rand(crypto:mpint(Min), crypto:mpint(Max)).
 
396
 
 
397
odd_rand(Min,Max) ->
 
398
    Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max),
 
399
    BitSkip = (Sz+4)*8-1,
 
400
    case Rand of
 
401
        Odd  = <<_:BitSkip,  1:1>> -> Odd;
 
402
        Even = <<_:BitSkip,  0:1>> -> 
 
403
            crypto:mpint(crypto:erlint(Even)+1)
 
404
    end.
 
405
 
 
406
extended_gcd(A, B) ->
 
407
    case A rem B of
 
408
        0 ->
 
409
            {0, 1};
 
410
        N ->
 
411
            {X, Y} = extended_gcd(B, N),
 
412
            {Y, X-Y*(A div B)}
 
413
    end.
 
414
 
 
415
pem_to_der(File) ->
 
416
    {ok, PemBin} = file:read_file(File),
 
417
    public_key:pem_decode(PemBin).
 
418
 
 
419
der_to_pem(File, Entries) ->
 
420
    PemBin = public_key:pem_encode(Entries),
 
421
    file:write_file(File, PemBin).