diff options
-rw-r--r-- | components/authorize/src/authorize_keys.erl | 38 | ||||
-rw-r--r-- | components/authorize/src/authorize_rpc.erl | 13 | ||||
-rw-r--r-- | components/authorize/src/authorize_sig.erl | 46 | ||||
-rw-r--r-- | components/rvi_common/src/rvi_common.erl | 1 | ||||
-rw-r--r-- | rebar.config | 3 | ||||
-rw-r--r-- | rvi_backend.config | 4 | ||||
-rw-r--r-- | test_keys/jlr_com.pem | 27 |
7 files changed, 125 insertions, 7 deletions
diff --git a/components/authorize/src/authorize_keys.erl b/components/authorize/src/authorize_keys.erl new file mode 100644 index 0000000..99f6847 --- /dev/null +++ b/components/authorize/src/authorize_keys.erl @@ -0,0 +1,38 @@ +-module(authorize_keys). + +-export([get_key_pair/0]). + +-include_lib("lager/include/log.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +get_key_pair() -> + case application:get_env(rvi, key_pair, undefined) of + undefined -> + {undefined, undefined}; + {openssl_pem, Pem} -> + get_key_pair_from_pem(openssl, Pem) + end. + +get_key_pair_from_pem(openssl, Pem) -> + case file:read_file(Pem) of + {ok, Bin} -> + case public_key:pem_decode(Bin) of + [Entry] -> + case public_key:pem_entry_decode(Entry) of + #'RSAPrivateKey'{modulus = Mod, + publicExponent = PE} = Priv -> + Pub = #'RSAPublicKey'{modulus = Mod, + publicExponent = PE}, + {Priv, Pub}; + _ -> + ?debug("Unknown PEM entry (~p)~n", [Pem]), + {undefined, undefined} + end; + _ -> + ?debug("Unsupported PEM file (~p)~n", [Pem]), + {undefined, undefined} + end; + Error -> + ?debug("Cannot read PEM file (~p): ~p~n", [Pem, Error]), + {undefined, undefined} + end. diff --git a/components/authorize/src/authorize_rpc.erl b/components/authorize/src/authorize_rpc.erl index a37ecc1..f380783 100644 --- a/components/authorize/src/authorize_rpc.erl +++ b/components/authorize/src/authorize_rpc.erl @@ -28,7 +28,9 @@ -record(st, { next_transaction_id = 1, %% Sequentially incremented transaction id. services_tid = undefined, %% Known services. - cs = #component_spec{} + cs = #component_spec{}, + private_key = undefined, + public_key = undefined }). start_link() -> @@ -36,7 +38,10 @@ start_link() -> init([]) -> ?debug("authorize_rpc:init(): called."), - {ok, #st { cs = rvi_common:get_component_specification() } }. + {Priv, Pub} = authorize_keys:get_key_pair(), + {ok, #st { cs = rvi_common:get_component_specification(), + private_key = Priv, + public_key = Pub} }. start_json_server() -> ?debug("authorize_rpc:start_json_server(): called"), @@ -106,7 +111,7 @@ authorize_local_message(CompSpec, Service) -> ?debug("authorize_rpc:authorize_local_msg(): service: ~p ~n", [Service]), rvi_common:request(authorize, ?MODULE,authorize_local_message, [{ service, Service }], - [staus, signature, certificate], CompSpec). + [status, signature, certificate], CompSpec). @@ -118,7 +123,7 @@ authorize_remote_message(CompSpec, Service, Signature, Certificate) -> [{ service, Service}, { signature, Signature }, { certificate, Certificate }], - [staus], CompSpec). + [status], CompSpec). diff --git a/components/authorize/src/authorize_sig.erl b/components/authorize/src/authorize_sig.erl new file mode 100644 index 0000000..8fb2353 --- /dev/null +++ b/components/authorize/src/authorize_sig.erl @@ -0,0 +1,46 @@ +-module(authorize_sig). + +-compile(export_all). + +-define(DIGEST_TYPE, sha). + +%% Inspired by +%% http://blog.differentpla.net/blog/2015/04/19/jwt-rs256-erlang/ +decode_jwt(JWT, PubKey) -> + [H, P, S] = binary:split(JWT, <<".">>, [global]), + Header = decode_json(base64url:decode(H)), + Payload = decode_json(base64url:decode(P)), + Signature = base64url:decode(S), + case public_key:verify(P, ?DIGEST_TYPE, Signature, PubKey) of + false -> + invalid; + true -> + {Header, Payload} + end. + +encode_jwt(JSON, PrivKey) -> + encode_jwt(JSON, header(), PrivKey). + +encode_jwt(Payload0, Header0, PrivKey) -> + Header = base64url:encode(ensure_json(Header0)), + Payload = base64url:encode(ensure_json(Payload0)), + Signature = base64url:encode( + public_key:sign(Payload, ?DIGEST_TYPE, PrivKey)), + <<Header/binary, ".", Payload/binary, ".", Signature/binary>>. + +header() -> + "{\"alg\": \"RS256\"}". + +ensure_json("{" ++ _ = JSON) -> + list_to_binary(JSON); +ensure_json(<<"{", _/binary>> = JSON) -> + JSON; +ensure_json({struct, _} = JSON) -> + list_to_binary(exo_json:encode(JSON)). + +decode_json("{" ++ _ = JSON) -> + {ok, Res} = exo_json:decode_string(JSON), + Res; +decode_json(<<"{", _/binary>> = JSON) -> + {ok, Res} = exo_json:decode_string(binary_to_list(JSON)), + Res. diff --git a/components/rvi_common/src/rvi_common.erl b/components/rvi_common/src/rvi_common.erl index 2c16b40..95dff51 100644 --- a/components/rvi_common/src/rvi_common.erl +++ b/components/rvi_common/src/rvi_common.erl @@ -651,4 +651,3 @@ start_json_rpc_server(Component, Module, Supervisor) -> [ Component, Module ]), Err end. - diff --git a/rebar.config b/rebar.config index 96de275..07bd053 100644 --- a/rebar.config +++ b/rebar.config @@ -31,5 +31,6 @@ {dthread, ".*", {git, "git://github.com/tonyrog/dthread.git", "HEAD"}}, {uart, ".*", {git, "git://github.com/tonyrog/uart.git", "HEAD"}}, {gsms, ".*", {git, "git://github.com/tonyrog/gsms.git", "HEAD"}}, - {wse, ".*", {git, "git://github.com/tonyrog/wse.git", "HEAD"}} + {wse, ".*", {git, "git://github.com/tonyrog/wse.git", "HEAD"}}, + {base64url, ".*", {git, "git://github.com/dvv/base64url.git", "HEAD"}} ]}. diff --git a/rvi_backend.config b/rvi_backend.config index 9ca9392..6a2ea52 100644 --- a/rvi_backend.config +++ b/rvi_backend.config @@ -68,7 +68,9 @@ ] } ] - }, + }, + + {key_pair, {openssl_pem, "test_keys/jlr_com.pem"}}, { components, [ diff --git a/test_keys/jlr_com.pem b/test_keys/jlr_com.pem new file mode 100644 index 0000000..1c25795 --- /dev/null +++ b/test_keys/jlr_com.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA5atdLLEj9QQdPLNS3EeiKMqvU8O2luLgNrPu/3NZyazIHOj0 +XsyIfrrUH7M696yLPeon5RyTt9YS8G9mvvoEdWCs9QQxYCUj3cDWAAfSPaeCtzsc +sJPEkIxb/DN37SWiyEMD+X96YDi5QAbNbS4D3bVlCvdQCJHCYxwnWltH66jCbyYj +mmDSc4ECrFbsvqAt4Cj4WsrhrOOar8t3eEYQwJ7wtTUJ4ZWKiwOGjNspMXsiR/W8 +on/nI5tJjb4ie5i9wi5nScrcE0bkrERJAb8geVLvxv6UJ6KDhgHkYJ+N55whgZtm +xWK+Hyx2x16y+utyXA1w1KWtVgNuEXTumm/BgwIDAQABAoIBAQCqlNCrdRYj0V51 +yyrr+Thz27bFHEPtZazqdFI64U37AJ3Q+yUlk/x9Q/5xXQmbE0iIP/ZJsSP2EsYW +VmeVyNSu6ZMMmISjyHIPLj7D3wtER7pKpVj1vYrtYZKgiwmz1ZRF8aBuG0SlWvwc +lfDV+qtcbHIT5wRGmRwryKlh7xJH8LOxCn95859lqUO2/nao1FiToGg6L7GOjKnc +kyoHzMXc57Ju18iOf0ef+Rpjz3gYU8+jqhMVYc02ceXcTGUmKyFlUgLo5oTgswkv +ihQSvESdL4Wjy9N70CZ3LmbX4sQtbGmB//+TUhtLRz3QZ24UHyZftmPqmofxKl0u ++V9MPZZhAoGBAPrz9PUVh1qhB6h3o3zsaMqwghv+LGJP9e+pAfRrxOlsl9D+oXJ9 +RqtYZ1TSkG4zh7zjUtbAwBpOnhDwNmoWggdzAVQqY0dZkFWcIKMFwgJ2bdIctnqP +gb+5mdHIAFr2mMGkCakYah58rBFHTwscB+DJo1nQkeeAtZZVhjKNZWglAoGBAOpJ +0+EvMPmilxdu0JtEM0yCIgiWztzx8Xk/Dk94rvFL1hGlpqzWb1zrSmkn0nZJOMwp +czaqwPGKVVKga+FyG/Ph5EOjzeq6o6L4updhXVK4+FQyYjtW76Gd45d0SEtoFHDv +VnNACE2wKYy8eRn6YL8/1wWSK3J3Bl4R2wLKQ56HAoGAUZ50ph15Z1WGSxmWN7Qg +JQQhXP9e6h69i1P5ichfQ9gIzHV0husNx/65pNHTHbRuylVZBPWtxSCaskGeQQTP +B6M05G3g2jzb/6wGxV7DCdAAydyC0PvJpFS2HY+h5fPcHObAKZoCMaY0xG3f3Goi +ec667SQzwAqRhLKOMfTg7W0CgYAqH4H3v6tXhrMRpHvfHqD2hYMm9i5VLS7UNNEW +2sThX1gqrhTMiHVXSHqFc4J0f++1TVKIpqSwCMYUaAFddE2wSJHo01+nb00SdRPf +OcM0p0sGoFRnBNdqwF92EJMa86iRMYbii8WyahTeV9iSIiEY4ZqARuqG1v2PFjjB +RKppqwKBgDIxg44mmqYruXUIeJ7Z85Q4vbTwaQN/cWCTRU/RKI/WvwJVUZI77aDH +a4kKzRwB6yUxVsjCo6Pdv1nXLK4UpgXMkpObhni/5TfWxNlczbgVqtWwGGu2xlx6 +vKUuq8oFetWkDhIsaIdw20FKzSwFpQ7xFZU2iD1Nd5/etAIi7zxp +-----END RSA PRIVATE KEY----- |