1 package com.foxinmy.weixin4j.http.weixin;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.security.*;
7 import java.security.cert.CertificateFactory;
8 import java.security.interfaces.RSAPrivateKey;
9 import java.security.spec.InvalidKeySpecException;
10 import java.security.spec.PKCS8EncodedKeySpec;
11 import java.util.Arrays;
12
13 import javax.net.ssl.KeyManagerFactory;
14 import javax.net.ssl.SSLContext;
15 import java.security.cert.CertificateException;
16 import java.security.cert.X509Certificate;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
19 import javax.xml.bind.DatatypeConverter;
20
21 import com.foxinmy.weixin4j.exception.WeixinException;
22 import com.foxinmy.weixin4j.http.HttpClient;
23 import com.foxinmy.weixin4j.http.HttpClientException;
24 import com.foxinmy.weixin4j.http.HttpMethod;
25 import com.foxinmy.weixin4j.http.HttpParams;
26 import com.foxinmy.weixin4j.http.HttpRequest;
27 import com.foxinmy.weixin4j.http.HttpResponse;
28 import com.foxinmy.weixin4j.http.MimeType;
29 import com.foxinmy.weixin4j.http.URLParameter;
30 import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
31 import com.foxinmy.weixin4j.http.apache.mime.HttpMultipartMode;
32 import com.foxinmy.weixin4j.http.apache.mime.MultipartEntityBuilder;
33 import com.foxinmy.weixin4j.http.entity.FormUrlEntity;
34 import com.foxinmy.weixin4j.http.entity.HttpEntity;
35 import com.foxinmy.weixin4j.http.entity.StringEntity;
36 import com.foxinmy.weixin4j.http.factory.HttpClientFactory;
37 import com.foxinmy.weixin4j.http.message.XmlMessageConverter;
38 import com.foxinmy.weixin4j.logging.InternalLogLevel;
39 import com.foxinmy.weixin4j.logging.InternalLogger;
40 import com.foxinmy.weixin4j.logging.InternalLoggerFactory;
41 import com.foxinmy.weixin4j.util.Consts;
42 import com.foxinmy.weixin4j.util.StringUtil;
43 import org.bouncycastle.jce.provider.BouncyCastleProvider;
44 import static java.util.regex.Pattern.CASE_INSENSITIVE;
45
46
47
48
49
50
51
52
53
54
55 public class WeixinRequestExecutor {
56
57 protected final InternalLogger logger = InternalLoggerFactory
58 .getInstance(getClass());
59
60 private static final String SUCCESS_CODE = ",0,success,";
61
62 private final HttpClient httpClient;
63
64 public WeixinRequestExecutor() {
65 this.httpClient = HttpClientFactory.getInstance();
66 }
67
68 public WeixinRequestExecutor(HttpParams params) {
69 this.httpClient = HttpClientFactory.getInstance(params);
70 }
71
72 private static final Pattern CERT_PATTERN = Pattern.compile(
73 "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" +
74 "([a-z0-9+/=\\r\\n]+)" +
75 "-+END\\s+.*CERTIFICATE[^-]*-+",
76 CASE_INSENSITIVE);
77
78 private static final Pattern KEY_PATTERN = Pattern.compile(
79 "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" +
80 "([a-z0-9+/=\\r\\n]+)" +
81 "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+",
82 CASE_INSENSITIVE);
83
84
85
86
87
88
89
90
91
92
93
94 public WeixinResponse post(String url, String body) throws WeixinException {
95 HttpEntity entity = new StringEntity(body);
96 HttpRequest request = new HttpRequest(HttpMethod.POST, url);
97 request.setEntity(entity);
98 return doRequest(request);
99 }
100
101
102
103
104
105
106
107
108
109
110
111 public WeixinResponse post(String url, FormBodyPart... bodyParts)
112 throws WeixinException {
113 MultipartEntityBuilder builder = MultipartEntityBuilder.create();
114 for (FormBodyPart bodyPart : bodyParts) {
115 builder.addPart(bodyPart);
116 }
117 HttpRequest request = new HttpRequest(HttpMethod.POST, url);
118 request.setEntity(builder.setMode(HttpMultipartMode.RFC6532)
119 .buildEntity());
120 return doRequest(request);
121 }
122
123
124
125
126
127
128
129
130
131
132
133 public WeixinResponse get(String url, URLParameter... parameters)
134 throws WeixinException {
135
136 StringBuilder buf = new StringBuilder(url);
137 if (parameters != null && parameters.length > 0) {
138 buf.append("&").append(
139 FormUrlEntity.formatParameters(Arrays.asList(parameters)));
140 }
141 HttpRequest request = new HttpRequest(HttpMethod.GET, buf.toString());
142 return doRequest(request);
143 }
144
145
146
147
148
149
150
151
152
153 public WeixinResponse doRequest(HttpRequest request) throws WeixinException {
154 try {
155 if (logger.isEnabled(InternalLogLevel.DEBUG)) {
156 logger.debug("weixin request >> "
157 + request.getMethod()
158 + " "
159 + request.getURI().toString()
160 + (request.getEntity() instanceof StringEntity ? " >> "
161 + ((StringEntity) request.getEntity())
162 .getContentString() : ""));
163 }
164 HttpResponse httpResponse = httpClient.execute(request);
165 WeixinResponse response = new WeixinResponse(httpResponse);
166 handleResponse(response);
167 return response;
168 } catch (HttpClientException e) {
169 throw new WeixinException(e);
170 }
171 }
172
173
174
175
176
177
178
179
180 private boolean hasStreamMimeType(WeixinResponse response) {
181 MimeType responseMimeType = MimeType.valueOf(response.getHeaders()
182 .getContentType());
183 for (MimeType streamMimeType : MimeType.STREAM_MIMETYPES) {
184 if (streamMimeType.includes(responseMimeType)) {
185 return true;
186 }
187 }
188 return false;
189 }
190
191
192
193
194
195
196
197
198 protected void handleResponse(WeixinResponse response)
199 throws WeixinException {
200 boolean hasStreamMimeType = hasStreamMimeType(response);
201 if (logger.isEnabled(InternalLogLevel.DEBUG)) {
202 logger.debug("weixin response << "
203 + response.getProtocol()
204 + response.getStatus()
205 + " << "
206 + (hasStreamMimeType ? response.getHeaders()
207 .getContentType() : response.getAsString()));
208 }
209 if (hasStreamMimeType) {
210 return;
211 }
212 ApiResult result = response.getAsResult();
213 if (!SUCCESS_CODE.contains(String.format(",%s,", result.getReturnCode()
214 .toLowerCase()))) {
215 throw new WeixinException(result.getReturnCode(),
216 result.getReturnMsg());
217 }
218 if (XmlMessageConverter.GLOBAL.canConvert(XmlResult.class, response)) {
219 try {
220 XmlResult xmlResult = XmlMessageConverter.GLOBAL.convert(
221 XmlResult.class, response);
222
223 if(StringUtil.isNotBlank(xmlResult.getResultCode())) {
224 if (!SUCCESS_CODE.contains(String.format(",%s,", xmlResult
225 .getResultCode().toLowerCase()))) {
226 throw new WeixinException(xmlResult.getErrCode(),
227 xmlResult.getErrCodeDes());
228 }
229 }
230 } catch (IOException e) {
231 ;
232 }
233 }
234 }
235
236 public HttpClient getExecuteClient() {
237 return httpClient;
238 }
239
240
241
242
243
244
245
246
247
248
249
250 public WeixinRequestExecutor createSSLRequestExecutor(String password,
251 InputStream inputStream) throws WeixinException {
252 try {
253 KeyStore keyStore = KeyStore.getInstance(Consts.PKCS12);
254 keyStore.load(inputStream, password.toCharArray());
255 KeyManagerFactory kmf = KeyManagerFactory
256 .getInstance(Consts.SunX509);
257 kmf.init(keyStore, password.toCharArray());
258 SSLContext sslContext = SSLContext.getInstance("TLS");
259 sslContext.init(kmf.getKeyManagers(), null,
260 new java.security.SecureRandom());
261 return createSSLRequestExecutor(sslContext);
262 } catch (Exception e) {
263 if (inputStream != null) {
264 try {
265 inputStream.close();
266 } catch (IOException ignore) {
267 }
268 }
269 throw new WeixinException("Key load error", e);
270 }
271 }
272
273 public WeixinRequestExecutor createSSLRequestExecutor(SSLContext sslContext) {
274 if (sslContext == null) {
275 throw new IllegalArgumentException("sslContext must not be empty");
276 }
277 HttpParams params = new HttpParams();
278 params.setSSLContext(sslContext);
279 return new WeixinRequestExecutor(params);
280 }
281
282
283
284
285
286
287
288
289
290
291 public WeixinRequestExecutor createSSLRequestExecutor(String password, String pemCertificate, String pemPrivateKey) throws WeixinException{
292 Security.addProvider(new BouncyCastleProvider());
293
294 try {
295 byte[] certBytes = parseDERFromPEM(pemCertificate);
296 byte[] keyBytes = parseDERFromPEM(pemPrivateKey);
297
298 char[] passwordChars = password.toCharArray();
299 X509Certificate cert = generateCertificateFromDER(certBytes);
300 RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
301
302 KeyStore keystore = KeyStore.getInstance("JKS");
303 keystore.load(null);
304 keystore.setCertificateEntry("cert-alias", cert);
305 keystore.setKeyEntry("key-alias", key, passwordChars, new X509Certificate[] {cert});
306
307 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
308 kmf.init(keystore, passwordChars);
309
310 SSLContext context = SSLContext.getInstance("TLS");
311 context.init(kmf.getKeyManagers(), null, new java.security.SecureRandom());
312
313 return createSSLRequestExecutor(context);
314 } catch (Exception e) {
315 throw new WeixinException("Certificate load error", e);
316 }
317
318 }
319
320 private static byte[] parseDERFromPEM(String data) throws KeyStoreException {
321 Matcher matcher = CERT_PATTERN.matcher(data);
322 String content = "";
323 if(!matcher.find()){
324 matcher = KEY_PATTERN.matcher(data);
325 if(!matcher.find()){
326 throw new KeyStoreException("found no private key or certificate from content:"+ data);
327 }
328 }
329 content = matcher.group(1);
330 return DatatypeConverter.parseBase64Binary(content);
331 }
332
333 private static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
334 PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
335
336 KeyFactory factory = KeyFactory.getInstance("RSA");
337
338 return (RSAPrivateKey)factory.generatePrivate(spec);
339 }
340
341 protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
342 CertificateFactory factory = CertificateFactory.getInstance("X.509");
343
344 return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));
345 }
346 }