diff options
Diffstat (limited to 'lib/public_key')
-rw-r--r-- | lib/public_key/doc/src/public_key.xml | 86 | ||||
-rw-r--r-- | lib/public_key/src/pubkey_cert.erl | 253 | ||||
-rw-r--r-- | lib/public_key/src/public_key.erl | 25 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE.erl | 100 |
4 files changed, 457 insertions, 7 deletions
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 942203bd12..5a4fdf057b 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -119,6 +119,10 @@ <tag><c>ec_private_key() =</c></tag> <item><p><c>#'ECPrivateKey'{}</c></p></item> + <tag><c>key_params() =</c></tag> + <item><p> #'DHParameter'{} | {namedCurve, oid()} | #'ECParameters'{} + | {rsa, Size::integer(), PubExp::integer()} </p></item> + <tag><c>public_crypt_options() =</c></tag> <item><p><c>[{rsa_pad, rsa_padding()}]</c></p></item> @@ -347,8 +351,7 @@ <name>generate_key(Params) -> {Public::binary(), Private::binary()} | #'ECPrivateKey'{} | #'RSAPrivateKey'{}</name> <fsummary>Generates a new keypair.</fsummary> <type> - <v>Params = #'DHParameter'{} | {namedCurve, oid()} | #'ECParameters'{} - | {rsa, Size::integer(), PubExp::integer} </v> + <v>Params = key_params()</v> </type> <desc> <p>Generates a new keypair. Note that except for Diffie-Hellman @@ -769,6 +772,85 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, </desc> </func> + <func> + <name>pkix_test_data(Options) -> Config </name> + <fsummary>Creates certificate test data.</fsummary> + <type> + <v>Options = #{chain_type() := chain_opts()} </v> + <d>Options for ROOT, Intermediate and Peer certs</d> + + <v>chain_type() = server_chain | client_chain </v> + + <v>chain_opts() = #{chain_end() := [cert_opt()], + intermediates => [[cert_opt()]]}</v> + <d>A valid chain must have at least a ROOT and a peer cert</d> + + <v>chain_end() = root | peer </v> + + <v>cert_opt() = {Key, Value}</v> + <d>For available options see <seealso marker="#cert_opt"> cert_opt()</seealso> below.</d> + + <v>Config = #{server_config := [conf_opt()], + client_config := [conf_opt()]}</v> + + <v>conf_opt() = {cert, der_encoded()} | {key, der_encoded()} |{cacerts, [der_encoded()]}</v> + <d>This is a subset of the type <seealso marker="ssl#type-ssloption"> ssl:ssl_option()</seealso> </d> + </type> + + <desc> + <p>Creates certificate test data to facilitate automated testing + of applications using X509-certificates often through + SSL/TLS. The test data can be used when you have control + over both the client and the server in a test scenario. + </p> + + <p> The <marker id="cert_opt"/> cert_opt() type consists of the following options: </p> + <taglist> + <tag> {digest, digest_type()}</tag> + <item><p>Hash algorithm to be used for + signing the certificate together with the key option. Defaults to sha that is sha1. + </p></item> + <tag> {key, key_params() | private_key()}</tag> + <item><p>Parameters to be used to call public_key:generate_key/1, to generate a key, or an existing + key. Defaults to generating an ECDSA key. Note this could fail if Erlang/OTP is compiled with a very old + cryptolib.</p></item> + <tag> {validity, {From::erlang:timestamp(), To::erlang:timestamp()}} </tag> + <item><p>The validity period of the certificate.</p></item> + <tag> {extensions, [#'Extension'{}]}</tag> + <item><p> Extensions to include in the certificate.</p> + + <p>Default extensions included in CA certificates if not + otherwise specified are: </p> + <code>[#'Extension'{extnID = ?'id-ce-keyUsage', + extnValue = [keyCertSign, cRLSign], + critical = false}, +#'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA = true}, + critical = true}] + </code> + + <p>Default extensions included in the server peer cert if not + otherwise specified are: </p> + <code>[#'Extension'{extnID = ?'id-ce-keyUsage', + extnValue = [digitalSignature, keyAgreement], + critical = false}, +#'Extension'{extnID = ?'id-ce-subjectAltName', + extnValue = [{dNSName, Hostname}], + critical = false}] + </code> + <p>Hostname is the result of calling net_adm:localhost() in the Erlang node + where this funcion is called. + </p></item> + + </taglist> + + <note><p> + Note that the generated certificates and keys does not provide a formally correct PKIX-trust-chain + and they can not be used to achieve real security. This function is provided for testing purposes only. +</p></note> + </desc> + </func> + <func> <name>pkix_verify(Cert, Key) -> boolean()</name> <fsummary>Verifies PKIX x.509 certificate signature.</fsummary> diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl index f45f2c2e9a..13833830a7 100644 --- a/lib/public_key/src/pubkey_cert.erl +++ b/lib/public_key/src/pubkey_cert.erl @@ -32,12 +32,25 @@ is_issuer/2, issuer_id/2, distribution_points/1, is_fixed_dh_cert/1, verify_data/1, verify_fun/4, select_extension/2, match_name/3, - extensions_list/1, cert_auth_key_id/1, time_str_2_gregorian_sec/1]). + extensions_list/1, cert_auth_key_id/1, time_str_2_gregorian_sec/1, + gen_test_certs/1]). -define(NULL, 0). - + +-export_type([chain_opts/0, test_config/0]). + +-type cert_opt() :: {digest, public_key:digest_type()} | + {key, public_key:key_params() | public_key:private_key()} | + {validity, {From::erlang:timestamp(), To::erlang:timestamp()}} | + {extensions, [#'Extension'{}]}. +-type chain_end() :: root | peer. +-type chain_opts() :: #{chain_end() := [cert_opt()], intermediates => [[cert_opt()]]}. +-type conf_opt() :: {cert, public_key:der_encoded()} | + {key, public_key:der_encoded()} | + {cacerts, [public_key:der_encoded()]}. +-type test_config() :: #{server_config := [conf_opt()], client_config := [conf_opt()]}. %%==================================================================== -%% Internal application API +%% Internal application APIu %%==================================================================== %%-------------------------------------------------------------------- @@ -417,6 +430,31 @@ match_name(Fun, Name, PermittedName, [Head | Tail]) -> false -> match_name(Fun, Name, Head, Tail) end. +%%% +-spec gen_test_certs(#{server_chain:= chain_opts(), client_chain:= chain_opts()}) -> test_config(). + +%% Generates server and and client configuration for testing +%% purposes. All certificate options have default values +gen_test_certs(#{client_chain := #{root := ClientRootConf, + intermediates := ClientCAs, + peer := ClientPeer}, + server_chain := + #{root := ServerRootConf, + intermediates := ServerCAs, + peer := ServerPeer}}) -> + SRootKey = gen_key(proplists:get_value(key, ServerRootConf, default_key_gen())), + CRootKey = gen_key(proplists:get_value(key, ClientRootConf, default_key_gen())), + ServerRoot = root_cert("server", SRootKey, ClientRootConf), + ClientRoot = root_cert("client", CRootKey, ServerRootConf), + + [{ServerDERCert, ServerDERKey} | ServerCAsKeys] = config(server, ServerRoot, + SRootKey, lists:reverse([ServerPeer | lists:reverse(ServerCAs)])), + [{ClientDERCert, ClientDERKey} | ClientCAsKeys] = config(client, ClientRoot, + CRootKey, lists:reverse([ClientPeer | lists:reverse(ClientCAs)])), + ServerDERCA = ca_config(ClientRoot, ServerCAsKeys), + ClientDERCA = ca_config(ServerRoot, ClientCAsKeys), + #{server_config => [{cert, ServerDERCert}, {key, ServerDERKey}, {cacerts, ServerDERCA}], + client_config => [{cert, ClientDERCert}, {key, ClientDERKey}, {cacerts, ClientDERCA}]}. %%-------------------------------------------------------------------- %%% Internal functions @@ -1064,3 +1102,212 @@ missing_basic_constraints(OtpCert, SelfSigned, ValidationState, VerifyFun, UserS Len - 1}, UserState} end. + + gen_key(KeyGen) -> + case is_key(KeyGen) of + true -> + KeyGen; + false -> + public_key:generate_key(KeyGen) + end. + +is_key(#'DSAPrivateKey'{}) -> + true; +is_key(#'RSAPrivateKey'{}) -> + true; +is_key(#'ECPrivateKey'{}) -> + true; +is_key(_) -> + false. + +root_cert(Role, PrivKey, Opts) -> + TBS = cert_template(), + Issuer = issuer("root", Role, " ROOT CA"), + OTPTBS = TBS#'OTPTBSCertificate'{ + signature = sign_algorithm(PrivKey, Opts), + issuer = Issuer, + validity = validity(Opts), + subject = Issuer, + subjectPublicKeyInfo = public_key(PrivKey), + extensions = extensions(Role, ca, Opts) + }, + public_key:pkix_sign(OTPTBS, PrivKey). + +cert_template() -> + #'OTPTBSCertificate'{ + version = v3, + serialNumber = trunc(rand:uniform()*100000000)*10000 + 1, + issuerUniqueID = asn1_NOVALUE, + subjectUniqueID = asn1_NOVALUE + }. +issuer(Contact, Role, Name) -> + subject(Contact, Role ++ Name). + +subject(Contact, Name) -> + Opts = [{email, Contact ++ "@erlang.org"}, + {name, Name}, + {city, "Stockholm"}, + {country, "SE"}, + {org, "erlang"}, + {org_unit, "automated testing"}], + subject(Opts). + +subject(SubjectOpts) when is_list(SubjectOpts) -> + Encode = fun(Opt) -> + {Type,Value} = subject_enc(Opt), + [#'AttributeTypeAndValue'{type=Type, value=Value}] + end, + {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}. + +subject_enc({name, Name}) -> + {?'id-at-commonName', {printableString, Name}}; +subject_enc({email, Email}) -> + {?'id-emailAddress', Email}; +subject_enc({city, City}) -> + {?'id-at-localityName', {printableString, City}}; +subject_enc({org, Org}) -> + {?'id-at-organizationName', {printableString, Org}}; +subject_enc({org_unit, OrgUnit}) -> + {?'id-at-organizationalUnitName', {printableString, OrgUnit}}; +subject_enc({country, Country}) -> + {?'id-at-countryName', Country}. + +validity(Opts) -> + DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), + DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7), + {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}), + Format = fun({Y,M,D}) -> + lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) + end, + #'Validity'{notBefore={generalTime, Format(DefFrom)}, + notAfter ={generalTime, Format(DefTo)}}. + +sign_algorithm(#'RSAPrivateKey'{}, Opts) -> + Type = rsa_digest_oid(proplists:get_value(digest, Opts, sha1)), + #'SignatureAlgorithm'{algorithm = Type, + parameters = 'NULL'}; +sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> + #'SignatureAlgorithm'{algorithm = ?'id-dsa-with-sha1', + parameters = {params,#'Dss-Parms'{p=P, q=Q, g=G}}}; +sign_algorithm(#'ECPrivateKey'{parameters = Parms}, Opts) -> + Type = ecdsa_digest_oid(proplists:get_value(digest, Opts, sha1)), + #'SignatureAlgorithm'{algorithm = Type, + parameters = Parms}. +rsa_digest_oid(sha1) -> + ?'sha1WithRSAEncryption'; +rsa_digest_oid(sha512) -> + ?'sha512WithRSAEncryption'; +rsa_digest_oid(sha384) -> + ?'sha384WithRSAEncryption'; +rsa_digest_oid(sha256) -> + ?'sha256WithRSAEncryption'; +rsa_digest_oid(md5) -> + ?'md5WithRSAEncryption'. + +ecdsa_digest_oid(sha1) -> + ?'ecdsa-with-SHA1'; +ecdsa_digest_oid(sha512) -> + ?'ecdsa-with-SHA512'; +ecdsa_digest_oid(sha384) -> + ?'ecdsa-with-SHA384'; +ecdsa_digest_oid(sha256) -> + ?'ecdsa-with-SHA256'. + +config(Role, Root, Key, Opts) -> + cert_chain(Role, Root, Key, Opts). + +cert_chain(Role, Root, RootKey, Opts) -> + cert_chain(Role, Root, RootKey, Opts, 0, []). + +cert_chain(Role, IssuerCert, IssuerKey, [PeerOpts], _, Acc) -> + Key = gen_key(proplists:get_value(key, PeerOpts, default_key_gen())), + Cert = cert(Role, public_key:pkix_decode_cert(IssuerCert, otp), + IssuerKey, Key, "admin", " Peer cert", PeerOpts, peer), + [{Cert, Key}, {IssuerCert, IssuerKey} | Acc]; +cert_chain(Role, IssuerCert, IssuerKey, [CAOpts | Rest], N, Acc) -> + Key = gen_key(proplists:get_value(key, CAOpts, default_key_gen())), + Cert = cert(Role, public_key:pkix_decode_cert(IssuerCert, otp), IssuerKey, Key, "webadmin", + " Intermidiate CA " ++ integer_to_list(N), CAOpts, ca), + cert_chain(Role, Cert, Key, Rest, N+1, [{IssuerCert, IssuerKey} | Acc]). + +cert(Role, #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = Issuer}}, + PrivKey, Key, Contact, Name, Opts, Type) -> + TBS = cert_template(), + OTPTBS = TBS#'OTPTBSCertificate'{ + signature = sign_algorithm(PrivKey, Opts), + issuer = Issuer, + validity = validity(Opts), + subject = subject(Contact, atom_to_list(Role) ++ Name), + subjectPublicKeyInfo = public_key(Key), + extensions = extensions(Role, Type, Opts) + + }, + public_key:pkix_sign(OTPTBS, PrivKey). + +ca_config(Root, CAsKeys) -> + [Root | [CA || {CA, _} <- CAsKeys]]. + +default_key_gen() -> + case crypto:ec_curves() of + [] -> + {rsa, 2048, 17}; + [Curve |_] -> + Oid = pubkey_cert_records:namedCurves(Curve), + {namedCurve, Oid} + end. + +public_key(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> + Public = #'RSAPublicKey'{modulus=N, publicExponent=E}, + Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = Public}; +public_key(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', + parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}; +public_key(#'ECPrivateKey'{version = _Version, + privateKey = _PrivKey, + parameters = Params, + publicKey = PubKey}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = #'ECPoint'{point = PubKey}}. + +extensions(Role, Type, Opts) -> + Exts = proplists:get_value(extensions, Opts, []), + add_default_extensions(Role, Type, Exts). + +add_default_extensions(_, ca, Exts) -> + Default = [#'Extension'{extnID = ?'id-ce-keyUsage', + extnValue = [keyCertSign, cRLSign], + critical = false}, + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA = true}, + critical = true}], + add_default_extensions(Default, Exts); + +add_default_extensions(server, peer, Exts) -> + Hostname = net_adm:localhost(), + Default = [#'Extension'{extnID = ?'id-ce-keyUsage', + extnValue = [digitalSignature, keyAgreement], + critical = false}, + #'Extension'{extnID = ?'id-ce-subjectAltName', + extnValue = [{dNSName, Hostname}], + critical = false} + ], + add_default_extensions(Default, Exts); + +add_default_extensions(_, peer, Exts) -> + Exts. + +add_default_extensions(Defaults0, Exts) -> + Defaults = lists:filtermap(fun(#'Extension'{extnID = ID} = Ext) -> + case lists:keymember(ID, 2, Exts) of + true -> + false; + false -> + {true, Ext} + end + end, Defaults0), + Exts ++ Defaults. + diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 9a61184f8a..cc01b61433 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -58,11 +58,13 @@ pkix_match_dist_point/2, pkix_crl_verify/2, pkix_crl_issuer/1, - short_name_hash/1 + short_name_hash/1, + pkix_test_data/1 ]). -export_type([public_key/0, private_key/0, pem_entry/0, - pki_asn1_type/0, asn1_type/0, ssh_file/0, der_encoded/0]). + pki_asn1_type/0, asn1_type/0, ssh_file/0, der_encoded/0, + key_params/0, digest_type/0]). -type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key(). -type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key(). @@ -75,6 +77,8 @@ -type ecpk_parameters_api() :: ecpk_parameters() | #'ECParameters'{} | {namedCurve, Name::atom()}. -type ec_public_key() :: {#'ECPoint'{}, ecpk_parameters_api()}. -type ec_private_key() :: #'ECPrivateKey'{}. +-type key_params() :: #'DHParameter'{} | {namedCurve, oid()} | #'ECParameters'{} | + {rsa, Size::integer(), PubExp::integer()}. -type der_encoded() :: binary(). -type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey' | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' @@ -102,6 +106,7 @@ -type crl_reason() :: unspecified | keyCompromise | cACompromise | affiliationChanged | superseded | cessationOfOperation | certificateHold | privilegeWithdrawn | aACompromise. -type oid() :: tuple(). +-type chain_type() :: server_chain | client_chain. -define(UINT32(X), X:32/unsigned-big-integer). -define(DER_NULL, <<5, 0>>). @@ -1027,6 +1032,22 @@ short_name_hash({rdnSequence, _Attributes} = Name) -> <<HashValue:32/little, _/binary>> = crypto:hash(sha, HashThis), string:to_lower(string:right(integer_to_list(HashValue, 16), 8, $0)). + +%%-------------------------------------------------------------------- +-spec pkix_test_data(#{chain_type() := pubkey_cert:chain_opts()}) -> + pubkey_cert:test_config(). + +%% Description: Generates OpenSSL-style hash of a name. +%%-------------------------------------------------------------------- + +pkix_test_data(#{client_chain := ClientChain0, + server_chain := ServerChain0}) -> + Default = #{intermediates => []}, + ClientChain = maps:merge(Default, ClientChain0), + ServerChain = maps:merge(Default, ServerChain0), + pubkey_cert:gen_test_certs(#{client_chain => ClientChain, + server_chain => ServerChain}). + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 4b1b771613..374fb20375 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -48,6 +48,8 @@ all() -> pkix_verify_hostname_cn, pkix_verify_hostname_subjAltName, pkix_verify_hostname_options, + pkix_test_data_all_default, + pkix_test_data, short_cert_issuer_hash, short_crl_issuer_hash, ssh_hostkey_fingerprint_md5_implicit, ssh_hostkey_fingerprint_md5, @@ -93,6 +95,14 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. %%------------------------------------------------------------------- + +init_per_testcase(pkix_test_data_all_default, Config) -> + case crypto:ec_curves() of + [] -> + {skip, missing_ecc_support}; + _ -> + init_common_per_testcase(Config) + end; init_per_testcase(TestCase, Config) -> case TestCase of ssh_hostkey_fingerprint_md5_implicit -> init_fingerprint_testcase([md5], Config); @@ -1047,6 +1057,84 @@ general_name(Config) when is_list(Config) -> authorityCertSerialNumber = 1}). %%-------------------------------------------------------------------- + +pkix_test_data_all_default() -> + [{doc, "Test API function pkix_test_data/1"}]. + +pkix_test_data_all_default(Config) when is_list(Config) -> + #{server_config := ServerConf0, + client_config := ClientConf0} = public_key:pkix_test_data(#{server_chain => + #{root => [], + intermediates => [[]], + peer => []}, + client_chain => + #{root => [], + intermediates => [[]], + peer => []}}), + check_conf_member(ServerConf0, [key, cert, cacerts]), + check_conf_member(ClientConf0, [key, cert, cacerts]), + + 3 = length(proplists:get_value(cacerts, ServerConf0)), + 3 = length(proplists:get_value(cacerts, ServerConf0)), + + #{server_config := ServerConf1, + client_config := ClientConf1} = public_key:pkix_test_data(#{server_chain => + #{root => [], + peer => []}, + client_chain => + #{root => [], + peer => []}}), + 2 = length(proplists:get_value(cacerts, ServerConf1)), + 2 = length(proplists:get_value(cacerts, ServerConf1)), + + check_conf_member(ServerConf1, [key, cert, cacerts]), + check_conf_member(ClientConf1, [key, cert, cacerts]). + + +pkix_test_data() -> + [{doc, "Test API function pkix_test_data/1"}]. + +pkix_test_data(Config) when is_list(Config) -> + {Year, Month, Day} = date(), + Keygen = + case crypto:ec_curves() of + [] -> + {rsa, 2048, 17}; + [Curve |_] -> + Oid = pubkey_cert_records:namedCurves(Curve), + {namedCurve, Oid} + end, + #{server_config := ServerConf0, + client_config := ClientConf0} = + public_key:pkix_test_data(#{server_chain => + #{root => [], + intermediates => [], + peer => [{key, hardcode_rsa_key()}]}, + client_chain => + #{root => [{validity, {{Year-2, Month, Day}, + {Year-1, Month, Day}}}], + intermediates => + [[{extensions, [#'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true, + pathLenConstraint = 1}, + critical = true}]}]], + peer => [{key, Keygen}, {digest, sha1}]}}), + check_conf_member(ServerConf0, [key, cert, cacerts]), + check_conf_member(ClientConf0, [key, cert, cacerts]). + + + +check_conf_member(_, []) -> + true; +check_conf_member(Conf, [Member | Rest]) -> + case lists:keymember(Member, 1, Conf) of + true -> + check_conf_member(Conf, Rest); + false -> + ct:fail({misssing_conf, Member}) + end. + +%%-------------------------------------------------------------------- short_cert_issuer_hash() -> [{doc, "Test OpenSSL-style hash for certificate issuer"}]. @@ -1168,3 +1256,15 @@ ssh_hostkey(rsa) -> public_key), PKdecoded. +hardcode_rsa_key() -> + #'RSAPrivateKey'{ + version = 'two-prime', + modulus = 23995666614853919027835084074500048897452890537492185072956789802729257783422306095699263934587064480357348855732149402060270996295002843755712064937715826848741191927820899197493902093529581182351132392364214171173881547273475904587683433713767834856230531387991145055273426806331200574039205571401702219159773947658558490957010003143162250693492642996408861265758000254664396313741422909188635443907373976005987612936763564996605457102336549804831742940035613780926178523017685712710473543251580072875247250504243621640157403744718833162626193206685233710319205099867303242759099560438381385658382486042995679707669, + publicExponent = 17, + privateExponent = 11292078406990079542510627799764728892919007311761028269626724613049062486316379339152594792746853873109340637991599718616598115903530750002688030558925094987642913848386305504703012749896273497577003478759630198199473669305165131570674557041773098755873191241407597673069847908861741446606684974777271632545629600685952292605647052193819136445675100211504432575554351515262198132231537860917084269870590492135731720141577986787033006338680118008484613510063003323516659048210893001173583018220214626635609151105287049126443102976056146630518124476470236027123782297108342869049542023328584384300970694412006494684657, + prime1 = 169371138592582642967021557955633494538845517070305333860805485424261447791289944610138334410987654265476540480228705481960508520379619587635662291973699651583489223555422528867090299996446070521801757353675026048850480903160224210802452555900007597342687137394192939372218903554801584969667104937092080815197, + prime2 = 141675062317286527042995673340952251894209529891636708844197799307963834958115010129693036021381525952081167155681637592199810112261679449166276939178032066869788822014115556349519329537177920752776047051833616197615329017439297361972726138285974555338480581117881706656603857310337984049152655480389797687577, + exponent1 = 119556097830058336212015217380447172615655659108450823901745048534772786676204666783627059584226579481512852103690850928442711896738555003036938088452023283470698275450886490965004917644550167427154181661417665446247398284583687678213495921811770068712485038160606780733330990744565824684470897602653233516609, + exponent2 = 41669135975672507953822256864985956439473391144599032012999352737636422046504414744027363535700448809435637398729893409470532385959317485048904982111185902020526124121798693043976273393287623750816484427009887116945685005129205106462566511260580751570141347387612266663707016855981760014456663376585234613993, + coefficient = 76837684977089699359024365285678488693966186052769523357232308621548155587515525857011429902602352279058920284048929101483304120686557782043616693940283344235057989514310975192908256494992960578961614059245280827077951132083993754797053182279229469590276271658395444955906108899267024101096069475145863928441, + otherPrimeInfos = asn1_NOVALUE}. |