diff --git a/AUTHORS b/AUTHORS index 19cd4c4..41f6ba5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ -Christopher Brown \ No newline at end of file +Christopher Brown +Seth Falcon diff --git a/include/chef_certgen.hrl b/include/chef_certgen.hrl new file mode 100644 index 0000000..11db048 --- /dev/null +++ b/include/chef_certgen.hrl @@ -0,0 +1,40 @@ +%%------------------------------------------------------------------- +%% @author Seth Falcon +%% @copyright 2012, Opscode, Inc. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%%------------------------------------------------------------------- + +-record(rsa_key_pair, { + public_key :: binary(), + private_key :: binary() + }). + +-record(x509_subject, { + 'CN' :: string(), + 'O' :: string(), + 'OU' :: string(), + 'C' :: string(), + 'ST' :: string(), + 'L' :: string() + }). + +-record(x509_input, { + signing_key :: #rsa_key_pair{}, + issuer_cert :: binary(), + newcert_public_key :: #rsa_key_pair{}, % FIXME: should just be pub key here + subject :: #x509_subject{}, + serial :: non_neg_integer(), + expiry :: non_neg_integer() + }). diff --git a/src/chef_certgen.erl b/src/chef_certgen.erl index 5ed3e76..1371451 100644 --- a/src/chef_certgen.erl +++ b/src/chef_certgen.erl @@ -40,6 +40,8 @@ -on_load(on_load/0). +-include_lib("chef_certgen.hrl"). + on_load() -> %% FIXME: why do we need to start crypto here instead of relying %% on the release or user? @@ -60,17 +62,22 @@ manual_start() -> manual_stop() -> application:stop(chef_certgen). +-spec rsa_generate_keypair(non_neg_integer()) -> #rsa_key_pair{}. rsa_generate_keypair(KeyLen) -> {ok, PemPublicKey, PemPrivateKey} = rsa_generate_key_nif(KeyLen), - {keypair, [{public_key, PemPublicKey}, {private_key, PemPrivateKey}]}. - -x509_make_cert([{signing_key, CaKeyPair}, {issuer_cert, CaCertPem}, - {newcert_public_key, GeneratedKeypair}, - Subject, - {serial, Serial}, {expiry, Expiry}])-> - x509_make_cert_nif([{signing_key, CaKeyPair}, {issuer_cert, CaCertPem}, - {newcert_public_key, GeneratedKeypair}, - Subject, + #rsa_key_pair{public_key = PemPublicKey, private_key = PemPrivateKey}. + +-spec x509_make_cert(#x509_input{}) -> {x509_cert, _}. +x509_make_cert(#x509_input{ + signing_key = CaKeyPair, + issuer_cert = CaCertPem, + newcert_public_key = PublicKey, + subject = Subject, + serial = Serial, + expiry = Expiry}) -> + x509_make_cert_nif([{signing_key, convert_key_pair(CaKeyPair)}, {issuer_cert, CaCertPem}, + {newcert_public_key, convert_single_key(PublicKey)}, + convert_subject(Subject), {serial, Serial}, {expiry, Expiry}]). version() -> @@ -80,6 +87,24 @@ version() -> %% ----------------------------------------- %% internal functions %% ----------------------------------------- +convert_single_key(Public) -> + {keypair, [{public_key, Public}, {private_key, <<"">>}]}. + +convert_key_pair(#rsa_key_pair{public_key = Public, private_key = Private}) -> + {keypair, [{public_key, Public}, {private_key, Private}]}. + +convert_subject(#x509_subject{'CN' = CN, + 'O' = O, + 'OU' = OU, + 'C' = C, + 'ST' = ST, + 'L' = L}) -> + {subject, [{'CN', CN}, + {'O', O}, + {'OU', OU}, + {'C', C}, + {'ST', ST}, + {'L', L}]}. rsa_generate_key_nif(_KeyLen) -> ?NIF_STUB. diff --git a/test/chef_certgen_tests.erl b/test/chef_certgen_tests.erl index 641be25..0c85af4 100644 --- a/test/chef_certgen_tests.erl +++ b/test/chef_certgen_tests.erl @@ -23,10 +23,12 @@ -module(chef_certgen_tests). -include_lib("eunit/include/eunit.hrl"). +-include_lib("chef_certgen/include/chef_certgen.hrl"). generate_keypair_test() -> - Keypair = chef_certgen:rsa_generate_keypair(2048), - ?assertMatch({keypair, _}, Keypair). + ?assertMatch(#rsa_key_pair{public_key = <<_Pub/binary>>, + private_key = <<_Priv/binary>>}, + chef_certgen:rsa_generate_keypair(2048)). create_x509_certificate_test() -> CaDir = filename:join(["..", "test"]), @@ -34,19 +36,21 @@ create_x509_certificate_test() -> CaKeypairName = filename:join(CaDir, "server_key.pem"), {ok, CaCertPem} = file:read_file(CaCertName), {ok, CaKeypairPem} = file:read_file(CaKeypairName), - CaKeypair = {keypair, [{public_key, list_to_binary("")}, {private_key, CaKeypairPem}]}, - CommonName = "Bob", - Subject = {subject, [{'CN', CommonName}, - {'O', "Opscode, Inc."}, - {'OU', "Certificate Service"}, - {'C', "US"}, - {'ST', "Washington"}, {'L', "Seattle"}]}, - GeneratedKeypair = chef_certgen:rsa_generate_keypair(2048), - ?assertMatch({keypair, _}, GeneratedKeypair), - TestCertResult = chef_certgen:x509_make_cert([{signing_key, CaKeypair}, - {issuer_cert, CaCertPem}, - {newcert_public_key, GeneratedKeypair}, - Subject, - {serial, 1}, - {expiry, 10*365}]), - ?assertMatch({x509_cert, _}, TestCertResult). + CaKeyPair = #rsa_key_pair{public_key = <<"">>, + private_key = CaKeypairPem}, + + Subject = #x509_subject{'CN' = "Bob", + 'O' = "Opscode, Inc.", + 'OU' = "Certificate Service", + 'C' = "US", + 'ST' = "Washington", + 'L' = "Seattle"}, + GeneratedKeyPair = chef_certgen:rsa_generate_keypair(2048), + X509In = #x509_input{signing_key = CaKeyPair, + issuer_cert = CaCertPem, + newcert_public_key = GeneratedKeyPair#rsa_key_pair.public_key, + subject = Subject, + serial = 1, + expiry = 10*365}, + TestCertResult = chef_certgen:x509_make_cert(X509In), + ?assertMatch({x509_cert, <<_/binary>>}, TestCertResult).