本次总结主要以技术为主,到了6月30号了,转眼间,收银台项目已经建立起来有两个多月了,
从刚开始的我负责,到两个人负责,现在是4个人负责,建立起来了支付组。后边还得继续
招小伙伴,很累,但也收获很多,现在收银台包含支付网关、券系统、账户系统、交易系统、
收银台入口等多个模块,说白了,只要涉及到钱的功能,都得过我们这一边。废话不多说,
技术撸上:
首先是安全性,收银台跟钱相关的,所以我们把安全性作为项目第一位考虑的,数据对内部
要防篡改,对外部数据传输要加密,为什么安全性排第一位呢?就是数据传输过程中,黑客把数据
拦截了下来,然后对数据进行一番改造,假设充值时,把0.1元字段改成10000元,黑客实际支付了
0.1元,然后在提现时就可能提出10000元来,当然这是简单的假设举例,而实际上提现更为复杂,
经过多重校验和多重审核。我们实现的是防篡改和防破解的思想是这样的:先用工具生成非对称性的
公私钥对,私钥加密,公钥解密,数据在网络传输前先用私钥加密,到达服务端时用公钥解密,而防
破解私钥,我们在数据的传输前加入一个随机数字段,这样保证每一次密文都是不一样的。但我们如
果供对内部的系统调用则主要是防篡改,干货来了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249package com.carlife.common.sign.utils;
import com.carlife.cashier.vo.CashierPrepayReq;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.lang.reflect.Field;
import java.security.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 签名验签算法:DSA
* 1、生成密钥对
* 2、签名
* 3、验签
*/
public class DSASignaureUtil {
public static final String Algorithm_DSA="DSA";
//默认密钥字节数
private static final int key_size=1024;
//默认种子
public static final String default_seed="0f22507a10bbddd07d8a3082122966e3";
public static final String public_key = "DSAPublicKey";
public static final String private_key = "DSAPrivateKey";
/***
* 生成密钥种子
* @param seed
* @return
* @throws Exception
*/
public static Map initKey(String seed) throws Exception{
KeyPairGenerator keygen = KeyPairGenerator.getInstance(Algorithm_DSA);
// 初始化随机产生器
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(seed.getBytes());
keygen.initialize(key_size,secureRandom);
KeyPair keys = keygen.genKeyPair();
DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();
Map map = new HashMap(2);
map.put(public_key,publicKey);
map.put(private_key,privateKey);
return map;
}
/**
* description: 加签名方法 <p>
* param: [data, private_key] <p>
* return: java.lang.String <p>
* author: shicong yang<p>
* date: 2018/6/28 <p>
*/
public static String sign(Object data,String private_key)throws Exception{
return sign(getData(data),private_key);
}
/***
* 用私钥对信息生成数字签名
* @param data 加密数据
* @param privateKey 私钥
* @return
* @throws Exception
*/
public static String sign(byte[] data,String privateKey)throws Exception{
// 解密由base64编码的私钥
byte[] keyBytes = decryptBASE64(privateKey);
// 构造PKCS8EncodedKeySpec对象
//PKCS#8:描述私有密钥信息格式,该信息包括公开密钥算法的私有密钥以及可选的属性集等[27]。
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(Algorithm_DSA);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
//用私钥对信息生成数字签名
Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
signature.initSign(priKey);
signature.update(data);
return encryptBASE64(signature.sign());
}
public static boolean verify(Object data,String publicKey,String sign) throws Exception {
return verify(getData(data),publicKey,sign);
}
/***
* 校验数字签名
* @param data 加密数据
* @param publicKey 公钥
* @param sign 数据签名
* @return 校验成功返回true 失败返回false
* @throws Exception
*/
public static boolean verify(byte[] data,String publicKey,String sign) throws Exception {
// 解密由base64编码的公钥
byte[] keyBytes = decryptBASE64(publicKey);
// 构造X509EncodedKeySpec对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(Algorithm_DSA);
// 取公钥匙对象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
signature.initVerify(pubKey);
signature.update(data);
// 验证签名是否正常
return signature.verify(decryptBASE64(sign));
}
/***
* 默认生成密钥
* @return
* @throws Exception
*/
public static Map initKey() throws Exception {
return initKey(default_seed);
}
/***
* 取得私钥
* @param keyMap
* @return
* @throws Exception
*/
public static String getPrivateKey(Map keyMap)throws Exception {
Key key = (Key) keyMap.get(private_key);
return encryptBASE64(key.getEncoded());
}
/***
* 取得公钥
* @param keyMap
* @return
* @throws Exception
*/
public static String getPublicKey(Map keyMap) throws Exception {
Key key = (Key) keyMap.get(public_key);
return encryptBASE64(key.getEncoded());
}
/**
* BASE64解密
*
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
/**
* BASE64 加密
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}
private static byte[] getData(Object obj) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
//遍历本类
Class cls = obj.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(obj) != null && f.get(obj) != "" && !f.getName().equals("sign") && !f.getName().equals("caller")) {
list.add(f.getName() + "=" + f.get(obj) + "&");
}
}
//遍历超类
Class superclass = cls.getSuperclass();
Field[] superclassfields = superclass.getDeclaredFields();
for (Field f : superclassfields) {
f.setAccessible(true);
if (f.get(obj) != null && f.get(obj) != "" && !f.getName().equals("sign") && !f.getName().equals("caller")) {
list.add(f.getName() + "=" + f.get(obj) + "&");
}
}
int size = list.size();
String[] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result = result.substring(0,result.length() -1);
System.err.println(result);
return result.getBytes();
}
public static void main(String[] args) throws Exception{
CashierPrepayReq obj = new CashierPrepayReq();
obj.setOrderNo("111111");
obj.setPayChannel(2);
obj.setProductLine("auto");
obj.setOrderBody("测试");
obj.setSubject("测试");
obj.setOrderOwnerUserId("999");
obj.setTotalFee(1L);
System.out.println("入参实体类:"+obj.toString());
byte[] data = getData(obj);
String inputStr = "Hello,你好啊!";
//byte[] data = inputStr.getBytes();
// 构建密钥
Map<String, Object> keyMap = DSASignaureUtil.initKey();
// 获得密钥
String publicKey = DSASignaureUtil.getPublicKey(keyMap);
String privateKey = DSASignaureUtil.getPrivateKey(keyMap);
System.err.println("公钥:\r" + publicKey);
System.err.println("私钥:\r" + privateKey);
// 产生签名
String sign = DSASignaureUtil.sign(data, privateKey);
System.err.println("签名:\r" + sign);
// 验证签名
boolean status = DSASignaureUtil.verify(data, publicKey, sign);
System.err.println("状态:\r" + status);
if(status){
System.out.println("原文:"+new String(data));
}
}
}
以后,还要讨论收银台的稳定性(服务器宕机就没办法收钱?)、扩展性(需求如何快速加入?)、
统一性(收银台几个子系统操作步骤如何规划统一?)、高效性(用户体验??)等等。