From 15dd88385fccd4880ba0b017ebdb83e05330a9c2 Mon Sep 17 00:00:00 2001 From: Konstantinos Kallas Date: Sat, 19 Aug 2017 16:58:06 +0300 Subject: [PATCH] Delete a development acme module --- src/acme_challenge.erl | 1 + src/acme_experimental.erl | 607 -------------------------------------- src/ejabberd_acme.erl | 2 +- 3 files changed, 2 insertions(+), 608 deletions(-) delete mode 100644 src/acme_experimental.erl diff --git a/src/acme_challenge.erl b/src/acme_challenge.erl index 081e10429..de0df8363 100644 --- a/src/acme_challenge.erl +++ b/src/acme_challenge.erl @@ -87,6 +87,7 @@ solve_challenge1(Challenge, _Key) -> ?INFO_MSG("Challenge: ~p~n", [Challenge]). +%% Old way of solving challenges save_key_authorization(Chal, Tkn, KeyAuthz, HttpDir) -> FileLocation = HttpDir ++ "/.well-known/acme-challenge/" ++ bitstring_to_list(Tkn), case file:write_file(FileLocation, KeyAuthz) of diff --git a/src/acme_experimental.erl b/src/acme_experimental.erl deleted file mode 100644 index 08fdc6ff4..000000000 --- a/src/acme_experimental.erl +++ /dev/null @@ -1,607 +0,0 @@ --module(acme_experimental). - --behaviour(gen_server). - -%% API --export([ start/0 - , stop/1 - %% I tried to follow the naming convention found in the acme spec - , directory/2 - , new_nonce/2 - %% Account - , new_reg/2 - , update_account/2 - , account_info/2 %% TODO: Maybe change to get_account - , account_key_change/2 - , deactivate_account/2 - %% Orders/Certificates - , new_cert/2 - , new_authz/2 - , get_certificate/2 - , get_authz/2 - , complete_challenge/2 - , deactivate_authz/2 - , revoke_cert/2]). - --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3]). - --export([scenario/0]). - --include("ejabberd.hrl"). --include("logger.hrl"). - --include("xmpp.hrl"). --include_lib("public_key/include/public_key.hrl"). - -% -define(CA_URL, "https://acme-v01.api.letsencrypt.org"). - - - --define(DEFAULT_DIRECTORY, ?CA_URL ++ "/directory"). --define(DEFAULT_NEW_NONCE, ?CA_URL ++ "/acme/new_nonce"). - --define(DEFAULT_KEY_FILE, "private_key_temporary"). - - - - --define(LOCAL_TESTING, true). - --ifdef(LOCAL_TESTING). --define(CA_URL, "http://localhost:4000"). --define(DEFAULT_ACCOUNT, "2"). --define(DEFAULT_TOS, <<"http://boulder:4000/terms/v1">>). --define(DEFAULT_AUTHZ, - <<"http://localhost:4000/acme/authz/XDAfMW6xBdRogD2-VIfTxlzo4RTlaE2U6x0yrwxnXlw">>). --else. --define(CA_URL, "https://acme-staging.api.letsencrypt.org"). --define(DEFAULT_ACCOUNT, "2273801"). --define(DEFAULT_TOS, <<"https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf">>). --define(DEFAULT_AUTHZ, <<"">>). --endif. - --record(state, { - ca_url = ?CA_URL :: list(), - dir_url = ?DEFAULT_DIRECTORY :: list(), - dirs = maps:new(), - nonce = "", - account = none - }). - -%% This will be initially just be filled with stub functions - -start() -> - gen_server:start(?MODULE, [], []). - -stop(Pid) -> - gen_server:stop(Pid). - -%% Stub functions -directory(Pid, Options) -> - gen_server:call(Pid, ?FUNCTION_NAME). - -new_nonce(Pid, Options) -> - gen_server:call(Pid, ?FUNCTION_NAME). - -new_reg(Pid, Options) -> - gen_server:call(Pid, ?FUNCTION_NAME). - -update_account(Pid, AccountId) -> - %% TODO: This has to have more info ofcourse - gen_server:call(Pid, {?FUNCTION_NAME, AccountId}). - -account_info(Pid, AccountId) -> - gen_server:call(Pid, {?FUNCTION_NAME, AccountId}). - -account_key_change(Pid, Options) -> - ok. - -deactivate_account(Pid, Options) -> - ok. - -new_cert(Pid, Options) -> - gen_server:call(Pid, ?FUNCTION_NAME). - -new_authz(Pid, Options) -> - gen_server:call(Pid, ?FUNCTION_NAME). - -get_certificate(Pid, Options) -> - ok. - -get_authz(Pid, Options) -> - gen_server:call(Pid, ?FUNCTION_NAME). - -complete_challenge(Pid, Options) -> - gen_server:call(Pid, {?FUNCTION_NAME, Options}). - -deactivate_authz(Pid, Options) -> - ok. - -revoke_cert(Pid, Options) -> - ok. - - - -%% GEN SERVER - -init([]) -> - %% TODO: Not the correct way of doing it - ok = application:start(inets), - ok = application:start(crypto), - ok = application:start(asn1), - ok = application:start(public_key), - ok = application:start(ssl), - - ok = application:start(base64url), - ok = application:start(jose), - - {ok, #state{}}. - -handle_call(directory, _From, S = #state{dir_url=Url, dirs=Dirs}) -> - %% Make the get request - {ok, {_Status, Head, Body}} = httpc:request(get, {Url, []}, [], []), - - %% Decode the json string - {Directories} = jiffy:decode(Body), - StrDirectories = [{bitstring_to_list(X), bitstring_to_list(Y)} || - {X,Y} <- Directories], - - % Find and save the replay nonce - % io:format("Directory Head Response: ~p~n", [Head]), - Nonce = get_nonce(Head), - - %% Update the directories in state - NewDirs = maps:from_list(StrDirectories), - % io:format("New directories: ~p~n", [NewDirs]), - - {reply, {ok, {Directories}}, S#state{dirs = NewDirs, nonce = Nonce}}; -handle_call(new_nonce, _From, S = #state{dirs=Dirs}) -> - %% Get url from all directories - #{"new_nonce" := Url} = Dirs, - {ok, {Status, Head, []}} = - httpc:request(head, {Url, []}, [], []), - {reply, {ok, {Status, Head}}, S}; -handle_call(new_reg, _From, S = #state{ca_url = Ca, dirs=Dirs, nonce = Nonce}) -> - %% Get url from all directories - #{"new-reg" := Url} = Dirs, - - %% Make the request body - ReqBody = jiffy:encode({ - [ { <<"contact">>, [<<"mailto:cert-admin@example.com">>]} - , { <<"resource">>, <<"new-reg">>} - ]}), - - %% Generate a key for the first time use - Key = generate_key(), - - %% Write the key to a file - jose_jwk:to_file(?DEFAULT_KEY_FILE, Key), - - %% Jose - {_, SignedBody} = sign_a_json_object_using_jose(Key, ReqBody, Url, Nonce), - % io:format("Signed Body: ~p~n", [SignedBody]), - - %% Encode the Signed body with jiffy - FinalBody = jiffy:encode(SignedBody), - - %% Post request - {ok, {Status, Head, Body}} = - httpc:request(post, {Url, [], "application/jose+json", FinalBody}, [], []), - - %% Get and save the new nonce - NewNonce = get_nonce(Head), - - {reply, {ok, {Status, Head, Body}}, S#state{nonce=NewNonce}}; -handle_call({account_info, AccountId}, _From, S = #state{ca_url = Ca, dirs=Dirs, nonce = Nonce}) -> - %% Get url from accountId - Url = Ca ++ "/acme/reg/" ++ AccountId, - - %% Make the request body - ReqBody = jiffy:encode({[ - { <<"resource">>, <<"reg">>} - ]}), - - %% Get the key from a file - Key = jose_jwk:from_file(?DEFAULT_KEY_FILE), - - %% Jose - {_, SignedBody} = sign_a_json_object_using_jose(Key, ReqBody, Url, Nonce), - % io:format("Signed Body: ~p~n", [SignedBody]), - - %% Encode the Signed body with jiffy - FinalBody = jiffy:encode(SignedBody), - - %% Post request - {ok, {Status, Head, Body}} = - httpc:request(post, {Url, [], "application/jose+json", FinalBody}, [], []), - - % Get and save the new nonce - NewNonce = get_nonce(Head), - - {reply, {ok, {Status, Head, Body}}, S#state{nonce=NewNonce}}; -handle_call({update_account, AccountId}, _From, S = #state{ca_url = Ca, dirs=Dirs, nonce = Nonce}) -> - %% Get url from accountId - Url = Ca ++ "/acme/reg/" ++ AccountId, - - %% Make the request body - ReqBody = jiffy:encode({[ - { <<"resource">>, <<"reg">>}, - { <<"agreement">>, ?DEFAULT_TOS} - ]}), - - %% Get the key from a file - Key = jose_jwk:from_file(?DEFAULT_KEY_FILE), - - %% Jose - {_, SignedBody} = sign_a_json_object_using_jose(Key, ReqBody, Url, Nonce), - % io:format("Signed Body: ~p~n", [SignedBody]), - - %% Encode the Signed body with jiffy - FinalBody = jiffy:encode(SignedBody), - - %% Post request - {ok, {Status, Head, Body}} = - httpc:request(post, {Url, [], "application/jose+json", FinalBody}, [], []), - - % Get and save the new nonce - NewNonce = get_nonce(Head), - - {reply, {ok, {Status, Head, Body}}, S#state{nonce=NewNonce}}; -handle_call(new_cert, _From, S = #state{ca_url = Ca, dirs=Dirs, nonce = Nonce}) -> - %% Get url from all directories - #{"new-cert" := Url} = Dirs, - - MyCSR = make_csr(), - % file:write_file("myCSR.der", CSR), - % {ok, CSR} = file:read_file("CSR.der"), - % io:format("CSR: ~p~nMy Encoded CSR: ~p~nCorrect Encoded CSR: ~p~n", - % [ public_key:der_decode('CertificationRequest', CSR) - % , MyCSR - % , CSR]), - - CSRbase64 = base64url:encode(MyCSR), - - % io:format("CSR base64: ~p~n", [CSRbase64]), - {MegS, Sec, MicS} = erlang:timestamp(), - NotBefore = xmpp_util:encode_timestamp({MegS-1, Sec, MicS}), - NotAfter = xmpp_util:encode_timestamp({MegS+1, Sec, MicS}), - - %% Make the request body - ReqBody = jiffy:encode({[ - {<<"resource">>, <<"new-cert">>}, - {<<"csr">>, CSRbase64}, - {<<"notBefore">>, NotBefore}, - {<<"NotAfter">>, NotAfter} - ]}), - %% Get the key from a file - Key = jose_jwk:from_file(?DEFAULT_KEY_FILE), - - %% Jose - {_, SignedBody} = sign_a_json_object_using_jose(Key, ReqBody, Url, Nonce), - % io:format("Signed Body: ~p~n", [SignedBody]), - - %% Encode the Signed body with jiffy - FinalBody = jiffy:encode(SignedBody), - - %% Post request - {ok, {Status, Head, Body}} = - httpc:request(post, {Url, [], "application/pkix-cert", FinalBody}, [], []), - - % Get and save the new nonce - NewNonce = get_nonce(Head), - - {reply, {ok, {Status, Head, Body}}, S#state{nonce=NewNonce}}; -handle_call(new_authz, _From, S = #state{ca_url = Ca, dirs=Dirs, nonce = Nonce}) -> - %% Get url from all directories - #{"new-authz" := Url} = Dirs, - - %% Make the request body - ReqBody = jiffy:encode({ - [ { <<"identifier">>, { - [ {<<"type">>, <<"dns">>} - , {<<"value">>, <<"my-acme-test-ejabberd.com">>} - ] }} - , {<<"existing">>, <<"accept">>} - , { <<"resource">>, <<"new-authz">>} - ] }), - - %% Get the key from a file - Key = jose_jwk:from_file(?DEFAULT_KEY_FILE), - - %% Jose - {_, SignedBody} = sign_a_json_object_using_jose(Key, ReqBody, Url, Nonce), - % io:format("Signed Body: ~p~n", [SignedBody]), - - %% Encode the Signed body with jiffy - FinalBody = jiffy:encode(SignedBody), - - %% Post request - {ok, {Status, Head, Body}} = - httpc:request(post, {Url, [], "application/jose+json", FinalBody}, [], []), - - % Get and save the new nonce - NewNonce = get_nonce(Head), - - {reply, {ok, {Status, Head, Body}}, S#state{nonce=NewNonce}}; -handle_call(get_authz, _From, S = #state{ca_url = Ca, dirs=Dirs, nonce = Nonce}) -> - %% Get url from all directories - Url = bitstring_to_list(?DEFAULT_AUTHZ), - - %% Post request - {ok, {Status, Head, Body}} = - % httpc:request(post, {Url, [], "application/jose+json", FinalBody}, [], []), - httpc:request(Url), - - % Get and save the new nonce - NewNonce = get_nonce(Head), - - {reply, {ok, {Status, Head, Body}}, S#state{nonce=NewNonce}}; -handle_call({complete_challenge, [Solution]}, _From, S = #state{ca_url = Ca, dirs=Dirs, nonce = Nonce}) -> - %% Get url from all directories - {ChallengeType, BitUrl, KeyAuthz} = Solution, - Url = bitstring_to_list(BitUrl), - - %% Make the request body - ReqBody = jiffy:encode({ - [ { <<"keyAuthorization">>, KeyAuthz} - , {<<"type">>, ChallengeType} - , { <<"resource">>, <<"challenge">>} - ] }), - - %% Get the key from a file - Key = jose_jwk:from_file(?DEFAULT_KEY_FILE), - - %% Jose - {_, SignedBody} = sign_a_json_object_using_jose(Key, ReqBody, Url, Nonce), - % io:format("Signed Body: ~p~n", [SignedBody]), - - %% Encode the Signed body with jiffy - FinalBody = jiffy:encode(SignedBody), - - %% Post request - {ok, {Status, Head, Body}} = - httpc:request(post, {Url, [], "application/jose+json", FinalBody}, [], []), - - % Get and save the new nonce - NewNonce = get_nonce(Head), - - {reply, {ok, {Status, Head, Body}}, S#state{nonce=NewNonce}}; -handle_call(stop, _From, State) -> - {stop, normal, ok, State}. - -handle_cast(Msg, State) -> - ?WARNING_MSG("unexpected cast: ~p", [Msg]), - {noreply, State}. - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%% Util functions - -final_url(Urls) -> - Joined = lists:join("/", Urls), - lists:flatten(Joined). - -get_nonce(Head) -> - {"replay-nonce", Nonce} = proplists:lookup("replay-nonce", Head), - Nonce. - -get_challenges({Body}) -> - {<<"challenges">>, Challenges} = proplists:lookup(<<"challenges">>, Body), - Challenges. - - -%% Test - -generate_key() -> - % Generate a key for now - Key = jose_jwk:generate_key({ec, secp256r1}), - io:format("Key: ~p~n", [Key]), - Key. - -sign_a_json_object_using_jose(Key, Json, Url, Nonce) -> - % Generate a public key - PubKey = jose_jwk:to_public(Key), - % io:format("Public Key: ~p~n", [PubKey]), - {_, BinaryPubKey} = jose_jwk:to_binary(PubKey), - % io:format("Public Key: ~p~n", [BinaryPubKey]), - PubKeyJson = jiffy:decode(BinaryPubKey), - % io:format("Public Key: ~p~n", [PubKeyJson]), - - % Jws object containing the algorithm - JwsObj = jose_jws:from( - #{ <<"alg">> => <<"ES256">> - %% Im not sure if it is needed - % , <<"b64">> => true - , <<"jwk">> => PubKeyJson - , <<"nonce">> => list_to_bitstring(Nonce) - }), - % io:format("Jws: ~p~n", [JwsObj]), - - %% Signed Message - Signed = jose_jws:sign(Key, Json, JwsObj), - % io:format("Signed: ~p~n", [Signed]), - - %% Peek protected - Protected = jose_jws:peek_protected(Signed), - % io:format("Protected: ~p~n", [jiffy:decode(Protected)]), - - %% Peek Payload - Payload = jose_jws:peek_payload(Signed), - io:format("Payload: ~p~n", [jiffy:decode(Payload)]), - - %% Verify - % {true, _} = jose_jws:verify(Key, Signed), - % io:format("Verify: ~p~n", [jose_jws:verify(Key, Signed)]), - - Signed. - -make_csr() -> - - SigningKey = jose_jwk:from_pem_file("csr_signing_private_key.key"), - {_, PrivateKey} = jose_jwk:to_key(SigningKey), - % io:format("PrivateKey: ~p~n", [PrivateKey]), - - PubKey = jose_jwk:to_public(SigningKey), - % io:format("Public Key: ~p~n", [PubKey]), - - {_, BinaryPubKey} = jose_jwk:to_binary(PubKey), - % io:format("Public Key: ~p~n", [BinaryPubKey]), - - {_, RawPubKey} = jose_jwk:to_key(PubKey), - % io:format("Raw Public Key: ~p~n", [RawPubKey]), - {{_, RawBinPubKey}, _} = RawPubKey, - % io:format("Encoded Raw Public Key: ~p~n", [RawBinPubKey]), - - %% TODO: Understand how to extract the information below from the key struct - AlgoID = #'CertificationRequestInfo_subjectPKInfo_algorithm'{ - algorithm = {1,2,840,10045,2,1}, %% Very dirty - parameters = {asn1_OPENTYPE,<<6,8,42,134,72,206,61,3,1,7>>} - }, - SubPKInfo = #'CertificationRequestInfo_subjectPKInfo'{ - algorithm = AlgoID, %% Very dirty - subjectPublicKey = RawBinPubKey %% public_key:der_encode('ECPoint', RawPubKey) - }, - - CommonName = #'AttributeTypeAndValue'{ - type = {2,5,4,3}, - % value = list_to_bitstring([12,25] ++ "my-acme-test-ejabberd.com") - value = length_bitstring(<<"my-acme-test-ejabberd.com">>) - }, - CountryName = #'AttributeTypeAndValue'{ - type = {2,5,4,6}, - value = length_bitstring(<<"US">>) - }, - StateOrProvinceName = #'AttributeTypeAndValue'{ - type = {2,5,4,8}, - value = length_bitstring(<<"California">>) - }, - LocalityName = #'AttributeTypeAndValue'{ - type = {2,5,4,7}, - value = length_bitstring(<<"San Jose">>) - }, - OrganizationName = #'AttributeTypeAndValue'{ - type = {2,5,4,10}, - value = length_bitstring(<<"Example">>) - }, - CRI = #'CertificationRequestInfo'{ - version = 0, - % subject = {rdnSequence, [[CommonName]]}, - subject = {rdnSequence, - [ [CommonName] - , [CountryName] - , [StateOrProvinceName] - , [LocalityName] - , [OrganizationName]]}, - subjectPKInfo = SubPKInfo, - attributes = [] - }, - EncodedCRI = public_key:der_encode( - 'CertificationRequestInfo', - CRI), - - SignedCRI = public_key:sign(EncodedCRI, 'sha256', PrivateKey), - - SigningAlgoID = #'CertificationRequest_signatureAlgorithm'{ - algorithm = [1,2,840,10045,4,3,2], %% Very dirty - parameters = asn1_NOVALUE - }, - - CSR = #'CertificationRequest'{ - certificationRequestInfo = CRI, - signatureAlgorithm = SigningAlgoID, - signature = SignedCRI - }, - Result = public_key:der_encode( - 'CertificationRequest', - CSR), - % io:format("My CSR: ~p~n", [CSR]), - - Result. - -%% TODO: Find a correct function to do this -length_bitstring(Bitstring) -> - Size = size(Bitstring), - case Size < 127 of - true -> - <<12, Size, Bitstring/binary>>; - false -> - error(not_implemented) - end. - -scenario() -> - % scenario_new_account(). - scenario_old_account(). - -scenario_old_account() -> - {ok, Pid} = start(), - io:format("Server started: ~p~n", [Pid]), - - {ok, Result} = directory(Pid, []), - io:format("Directory result: ~p~n", [Result]), - - %% Get the info of an existing account - % {ok, {Status1, Head1, Body1}} = account_info(Pid, ?DEFAULT_ACCOUNT), - % io:format("Account: ~p~nHead: ~p~nBody: ~p~n", - % [?DEFAULT_ACCOUNT, {Status1, Head1}, jiffy:decode(Body1)]), - - %% Update the account to agree to terms and services - {ok, {Status1, Head1, Body1}} = update_account(Pid, ?DEFAULT_ACCOUNT), - io:format("Account: ~p~nHead: ~p~nBody: ~p~n", - [?DEFAULT_ACCOUNT, {Status1, Head1}, jiffy:decode(Body1)]), - - %% New authorization - % {ok, {Status2, Head2, Body2}} = new_authz(Pid, []), - % io:format("New Authz~nHead: ~p~nBody: ~p~n", - % [{Status2, Head2}, jiffy:decode(Body2)]), - - %% Get authorization - {ok, {Status2, Head2, Body2}} = get_authz(Pid, []), - io:format("Get Authz~nHead: ~p~nBody: ~p~n", - [{Status2, Head2}, jiffy:decode(Body2)]), - - % Challenges = get_challenges(jiffy:decode(Body2)), - % io:format("Challenges: ~p~n", [Challenges]), - - % ChallengeObjects = acme_challenge:challenges_to_objects(Challenges), - % % io:format("Challenges: ~p~n", [ChallengeObjects]), - - % %% Create a key-authorization - % Key = jose_jwk:from_file(?DEFAULT_KEY_FILE), - % % acme_challenge:key_authorization(<<"pipi">>, Key), - - % Solutions = acme_challenge:solve_challenges(ChallengeObjects, Key), - % io:format("Solutions: ~p~n", [Solutions]), - - % {ok, {Status3, Head3, Body3}} = - % complete_challenge(Pid, [X || X <- Solutions, X =/= ok]), - % io:format("Complete_challenge~nHead: ~p~nBody: ~p~n", - % [{Status3, Head3}, jiffy:decode(Body3)]), - - % Get a certification - {ok, {Status4, Head4, Body4}} = - new_cert(Pid, []), - io:format("New Cert~nHead: ~p~nBody: ~p~n", - [{Status4, Head4}, Body4]), - - % make_csr(), - - ok. - -scenario_new_account() -> - {ok, Pid} = start(), - io:format("Server started: ~p~n", [Pid]), - - {ok, Result} = directory(Pid, []), - io:format("Directory result: ~p~n", [Result]), - - %% Request the creation of a new account - {ok, {Status, Head, Body}} = new_reg(Pid, []), - io:format("New account~nHead: ~p~nBody: ~p~n", [{Status, Head}, jiffy:decode(Body)]). \ No newline at end of file diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index 260d994b8..41947ee41 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -1,7 +1,7 @@ -module (ejabberd_acme). -export([%% Ejabberdctl Commands - get_certificates/2, + get_certificates/1, renew_certificates/0, list_certificates/1, revoke_certificate/1,