让android支持https访问银联后台,测试成功

时间:2022-07-22
本文章向大家介绍让android支持https访问银联后台,测试成功,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

根据XX公网前置接入规范,XX后台支持公网接入。但必须走https协议加密传输。

java很强大,在java上调用http接口以及解析json之类的应该很容易,有很多现成的接口和第三方库如httpclinet,okhttp,volley,retrofit等。

但是,XX的这套接口涉及到https加密传输,需加载本地公钥证书,以及定制http协议报文头,并且post的是16进制数据。

网上查资料,大多都是讲如何post提交json的,以及如何上传二进制图片的等内容。没找到如何提交二进制流的。

于是自己摸索了吧,接口通了。在此总结下,非专业andorid出身,代码有点乱。但要点就那几个。

使用的是retrofit.

要实现的协议格式如下:

POST/mjc/webtrans/VPB_lb HTTP/1.1

HOST:xxxxxxxx:xxxxx

User-Agent: Donjin Http0.1

Cache-Control: no-cache

Content-Type:x-XX-XXXX/x-XX

Accept: */*

Content-Length: xx

00 XX XX 00 0X 00 00 XX XX 00 XX XX XX XX00 00 20 00 00 00 c0 00 16 00 00 01 31 30 30 30 30

要点1,自定义http报文头,这个很简单了,举例如下:

@Headers({"User-Agent: Donjin Http 0.1","Cache-Control: no-cache","Content-Type: x-xxx","Accept-Encoding: *","Content-Length: xx"})//需要添加头
   // @Multipart
    @POST("/")
    Call<LoginResult> postData(@Body  RequestBody bodyhex);

需要注意的是,content-lenth这个别按上面的写固定了,这个是根据要提交的16进制数据来的。他不是一个固定值。

提交的数据内容直接传参 @Body RequestBody bodyhex

传的参数怎么处理,让他能够以16进制post呢?

如下,重新overrite RequestBody中的writeto方法和contenlenth放法,注意,必须得有contenlenth方法,否则会出错,不知道你post的数据有多少。

示例如下:

class DataBean {

    public byte[] data;
    public String DA = "00000000000000c0006300011000000010030002953657175656e6365204343131390003303120";//要发送的16进制数据的字符串形式
    public RequestBody bodyhex;
    public DataBean()
    {
        data = hexStringToBytes(DA);//转为hex 16进制
        bodyhex =  new RequestBody() {
            @Override
            public MediaType contentType() {
                return null;
            }
            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                sink.write(data);
            }
            @Override
            public long contentLength() {
                return data.length;
            }
        };
    };
}

还有一个比较重要的地方就是加载https的本地公钥证书啦

/**
 * 通过okhttpClient来设置证书
 * @param clientBuilder OKhttpClient.builder
 * @param certificates 读取证书的InputStream
 */
public static void setCertificates(OkHttpClient.Builder clientBuilder, InputStream... certificates) {
    try {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);
        int index = 0;
        for (InputStream certificate : certificates) {
            String certificateAlias = Integer.toString(index++);
            keyStore.setCertificateEntry(certificateAlias, certificateFactory
                    .generateCertificate(certificate));
            try {
                if (certificate != null)
                    certificate.close();
            } catch (IOException e) {
            }
        }
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:"
                    + Arrays.toString(trustManagers));
        }
        X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        clientBuilder.sslSocketFactory(sslSocketFactory,trustManager);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public  static OkHttpClient getOkHttpSingletonInstance( Context ct) {
    mContext = ct;
    if (okHttpClient == null) {
        synchronized (OkHttpClient.class) {
            if (okHttpClient == null) {
                okHttpClient = new OkHttpClient();
                //设置合理的超时
                OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder()
                        .readTimeout(3, TimeUnit.SECONDS)
                        .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) //设置连接超时 30秒
                        .writeTimeout(3, TimeUnit.MINUTES)
                        //.addInterceptor(new LoggingInterceptor())//添加请求拦截
                        .addNetworkInterceptor(
                                new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
                        .hostnameVerifier(new HostnameVerifier() {
                            @Override
                            public boolean verify(String hostname, SSLSession session) {
                                return true;
                            }
                        })
                        //.sslSocketFactory(sslParams.sSLSocketFactory,sslParams.trustManager)
                        .retryOnConnectionFailure(true);
                //InputStream in = new InputStream()
                try {
                    //AssetManager assetManager =  AssetManager.class.newInstance();
                    InputStream is = MyAppcation.getInstance().getAssets().open("UP.pem");
                    OkHttp3Utils.setCertificates( httpBuilder,is);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                okHttpClient = httpBuilder.build();

            }
        }
    }
    return okHttpClient;
}

完成以上这些,就够了。能够实现一开始的要求了。最后完善下接口,在自定义一个

ConverterFactory

对应答的报文解析就完整了。

测试结果如下:

06-15 03:37:45.183 4061-4099/com.example.yang.myapplication D/OkHttp: Connection: Keep-Alive
    --> END POST
06-15 03:37:45.585 4061-4099/com.example.yang.myapplication D/OkHttp: <-- 200 OK https://xx.xx.1xx.xx:xxx0/ (400ms)
06-15 03:37:45.589 4061-4099/com.example.yang.myapplication D/OkHttp: Allow: POST, PUT
06-15 03:37:45.590 4061-4099/com.example.yang.myapplication D/OkHttp: Content-Type: x-ISO-xxx/x-xx
06-15 03:37:45.591 4061-4099/com.example.yang.myapplication D/OkHttp: Date: Fri, 15 Jun 2018 03:39:44 GMT
06-15 03:37:45.592 4061-4099/com.example.yang.myapplication D/OkHttp: Content-Length: 112
    Server: Access-Guard-1000-Software/1.0
06-15 03:37:45.603 4061-4099/com.example.yang.myapplication D/OkHttp: Connection: close
06-15 03:37:45.604 4061-4099/com.example.yang.myapplication D/OkHttp: <-- END HTTP

06-15 03:37:45.633 4061-4099/com.example.yang.myapplication 
D/respond:: 00XXXXXXXXXXXXXXXXXXXXXXXXXXX600000001003000296f3136333135305358582d3443333034313139

06-15 03:37:45.704 4061-4061/com.example.yang.myapplication D/AA: 成功

Response{protocol=http/1.1, code=200, message=OK, url=https://XXXXXXXX;XXX/}
D/OkHttp: --> POST https://1xx.xx.xx.xx:xxxx/ http/1.1
          Content-Type: x-ISO-TPDU/x-auth
D/OkHttp: Content-Length: 89
          User-Agent: Donjin Http 0.1
          Cache-Control: no-cache
          Accept-Encoding: *
D/OkHttp: Host: xxx.xxx.xxx.xx:xxxx
          Connection: Keep-Alive
          --> END POST
D/OkHttp: <-- 200 OK https://xxx.xxx.xxx.xx:xxxx/ (421ms)
D/OkHttp: Allow: POST, PUT
          Content-Type: x-ISO-TPDU/x-auth
          Date: Sat, 30 Jun 2018 09:58:41 GMT
D/OkHttp: Content-Length: 123
D/OkHttp: Server: Access-Guard-1000-Software/1.0
          Connection: close
          <-- END HTTP
D/AA: 成功
      Response{protocol=http/1.1, code=200, message=OK, url=https://xxxxxxxx:xxxxx/}
D/respondAA:: 007960000005016131003111080810003800010ac0001400000117563506300800094900313735363335353837303233303037333738323231343839383431313334313331303031340011000007500030004050bc9eb4774a92544c29dad2c764150bb93eba92d9f11a222efa9c2300000000000000002b580802
I/System.out: 开始解析...
I/System.out: ->ok 解析成功!
I/System.out: pinkey:b1a7ab3cb49c9757390f39a19ce71ae7
I/System.out: <-Er PIK错误