1.对方要求我们的私钥是pkcs8格式,但是实际的公钥没有用pkcs8转换之后的私钥完成,所以是可以不是pkcs8的格式的。我们加签跟格式没有关系。  

2.数据格式很重要,to_mpint而非crypto:mpint生成mpint的高精度整型,to_mpint有是自己写的函数,但实际上有很多开源代码里面有,所以要多看开源代码及想到直接调用。


3.RSA加密加签原理:
 (1).加密,可以用私钥加密,公钥解密。
 (2).加密,也可以用公钥加密,私钥解密。
 (3).加签,必须要用私钥加签,公钥验证。
 (4).调用的方法不同。原理如下:
 RSA数据的hash^私钥 mod M = 签名
 签名 ^公钥 mod M = 数据的hash
 数据 ^公钥 mod M = 密文
 密文 ^私钥 mod M = 数据
4.调用erlang的crypto模块加密
5.生成密钥的方法是一样的。
 (1).按装openssl(基础环境中有此软件,无需安装)
 (2).输入openssl
生成私钥: genrsa -out private_key.pem 1024
 (3).转换成pkcs8格式(可无)pkcs8 -topk8 -inform PEM -in private_key.pem -outform PEM -nocrypt -out rsa_private_key.pem
 (4).用私钥生成公钥: rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
6.对于加签,是使用crypto:rsa_sign还是crypto:sign,但是实际上sign是新版本的,我方安装版本跟不上。
7.对于未做过的操作,应该有几步: 
 (1)确认要使用的方法
 (2)对于该方法所要的参数,如果格式不符合,要找到对应的转换方式(可以看开源库)
 但是方向要弄对,要先找到解决方法,再找原因。
====================================加密================================================
加密(用私钥加密,公钥解密)
-module(crypto_api).
-include_lib("public_key/include/public_key.hrl").
-include("ewp.hrl").
-include("backend.hrl").
-export([testencode/1,%%测试加密函数
 testdecode/1 %%测试解密函数
 ]).
%%Params为原始内容,数据类型为binary,_Foo为加密后的密文
case Type of
 RSA ->
 crypttest:testencode(Mobile); crypto_api:testencode()
 _ ->
 sm:
testencode(Params) ->
 {ok, PemBinary} = file:read_file("./public/security/rsa_private_key.pem"),%%读取私钥中的pem二进制码
 [Entry] = public_key:pem_decode(PemBinary),%%读取私钥中的pem二进制码,生产一个实体
 RSAPrivateKey = public_key:pem_entry_decode(Entry),%%实体生产私钥Key
 %%调用erlang的crypto模块加密
 PrivKey = [crypto:mpint(RSAPrivateKey#'RSAPrivateKey'.publicExponent), crypto:mpint(RSAPrivateKey#'RSAPrivateKey'.modulus), crypto:mpint(RSAPrivateKey#'RSAPrivateKey'.privateExponent)] ,
 _Foo = crypto:rsa_private_encrypt(Params,PrivKey,rsa_pkcs1_padding).testdecode(Params) ->
 {ok, PemBinary} = file:read_file("./public/security/rsa_public_key.pem"),
 [Entry] = public_key:pem_decode(PemBinary),
 RSAPublicKey = public_key:pem_entry_decode(Entry),
 PubKey = [crypto:mpint(RSAPublicKey#'RSAPublicKey'.publicExponent), crypto:mpint(RSAPublicKey#'RSAPublicKey'.modulus)],
 _Bar = crypto:rsa_public_decrypt(Params,PubKey,rsa_pkcs1_padding).============================加签,MD5摘要生成=======================================================
-module(....).-include("ewp.hrl").
-include("backend.hrl").
-include_lib("public_key/include/public_key.hrl").
-define(URL,"https://jiaofei........html").-export([
 'SN0001'/3, % 第三方配置化
 proplist_to_andlist/1
 ]).'SN0001'(UserObj, TranID, P) ->
 MOBILE_NO = user_obj:get_field('MOBILE_NO', UserObj), %% 手机号
 USER_CODE = user_obj:get_field('USER_CODE', UserObj), %% 手机银行客户号
 Url = ?URL,
 RequestTime = "20170214142901", %%ewp_time:datetime_str()
 Version = "2.0", %
 App_id = "yfbm70058192e2017021301", %
 Merchant_user_no = "43243", %%商户id之后给 USER_CODE
 Terminal_type = "03",
 Sign_type = "RSA",
 Signkey_index = "0001", %%公钥索引 测试
 SignMD = list_to_possign([{"request_time",RequestTime},{"version",Version},{"app_id",App_id}, {"merchant_user_no",Merchant_user_no},{"terminal_type",Terminal_type}]), %%数字签名
 {ok, Privatekey} = file:read_file("./config/key/private_key.pem"),%%读取私钥中的pem二进制码
 SignRSA = rsa_encode(SignMD,Privatekey),
 Sign = yaws_api:url_encode(SignRSA),
 PostDetail = Sign, %%实际要post给苏宁的参数
 [{url,Url},{post,PostDetail}].rsa_encode(Data,Priv_key) ->
 [Entry] = public_key:pem_decode(Priv_key), %%读取私钥中的pem二进制码,生产一个实体
 RSAPrivateKey = public_key:pem_entry_decode(Entry), %%实体生产私钥Key 私钥加密 加密的时候传参数为二进制的
 PrivKey = [crypto:mpint(RSAPrivateKey#'RSAPrivateKey'.publicExponent), 
 crypto:mpint(RSAPrivateKey#'RSAPrivateKey'.modulus), crypto:mpint(RSAPrivateKey#'RSAPrivateKey'.privateExponent)], 
 Result = crypto:rsa_sign('sha',to_mpint(Data),PrivKey), %% crypto:rsa_sign('sha',NumberMpint,PrivKey) crypto:sign(rsa,md5,Number,PrivKey)
 ResultBase = base64:encode(Result),
 binary_to_list(ResultBase). %% 传回后台使用 变回字符串的类型to_mpint(String) when is_list(String) ->
 to_mpint(list_to_binary(String));
to_mpint(Bin) ->
 Size = size(Bin),
 <<Size:32/integer, Bin:Size/binary>>.%% params: Proplist为[{...},.tuple..,{...}]格式。
%% 返回结果:key=value&key1=value1....
proplist_to_andlist(Proplist)->
 Str1 = lists:foldl(
 fun(X,Acc)->
 {Key1,Value1} = X,
 Key = atom_to_string(Key1),
 Value = atom_to_string(Value1),
 case [Key,Value] of
 [_,""] ->
 Acc; %%如果值为空,不放入该序列
 [_," "] ->
 Acc;
 _ ->
 case Acc of
 [] ->
 Acc ++ Key ++ "=" ++ Value;
 " " ->
 Acc ++ Key ++ "=" ++ Value;
 _ ->
 Acc ++ "&" ++ Key ++ "=" ++ Value
 end
 end
 end,"",Proplist),
 Str1. 
%% params: Proplist为[{...},.tuple..,{...}]格式。
%% 返回结果:数字签名---MD5加密后的大写的16进制(按照微信规则商户支付密钥放置在串最后加密)
list_to_possign(Proplist)->
 SortList = lists:keysort(1,Proplist), %%ASC码从小到大排序
 Str1 = proplist_to_andlist(SortList), %%拼接成 Key1=value1&Key2=value2 无key商户支付密钥的序列
 Res1 = Str1,
 MD1 = erlang:md5(Res1), %%
 <<Int:128>> = MD1, %% MD2 = backend_util:to_hexstr(MD1)
 Str = integer_to_list(Int,16), %%生成大写的16进制
 Str.%% 如果参数A为atom则转换为string,如果本来就是string则还是string
atom_to_string(A)->
 case is_atom(A) of
 true ->
 atom_to_list(A);
 false ->
 A
 end.