Retrofit請(qǐng)求加密承桥,響應(yīng)解密并生成類對(duì)象(自定義ConverterFactory)

1.自定義JsonConverterFactory類

public class JsonConverterFactory extends Converter.Factory {
    private static final String TAG = "JsonConverterFactory";
    private final Gson gson;

    public static JsonConverterFactory create() {
        return create(new Gson());
    }

    public static JsonConverterFactory create(Gson gson) {
        return new JsonConverterFactory(gson);

    }

    private JsonConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }


    @Nullable
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new JsonRequestBodyConverter<>(gson, adapter); //請(qǐng)求
    }

    @Nullable
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new JsonResponseBodyConverter<>(gson, adapter); //響應(yīng)
    }

    /**
     * JsonRequestBodyConverter<T>
     * @param <T>
     */
    public static class JsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
        private final Gson gson;
        private final TypeAdapter<T> adapter;

        /**
         * 構(gòu)造器
         */
        public JsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }

        @Override
        public RequestBody convert(T value) throws IOException {

            //這里需要,特別注意的是,request是將T轉(zhuǎn)換成json數(shù)據(jù)颖榜。
            //你要在T轉(zhuǎn)換成json之后再做加密惊科。
            //再將數(shù)據(jù)post給服務(wù)器拍摇,同時(shí)要注意,你的T到底指的那個(gè)對(duì)象

            //加密操作馆截,返回字節(jié)數(shù)組
            byte[] encrypt = AESUtils.encrypt(value.toString());

            Log.i("xiaozhang", "request中傳遞的json數(shù)據(jù):" + value.toString()); //打映浠睢:加密前的json字符串
            Log.i("xiaozhang", "加密后的字節(jié)數(shù)組:" + encrypt.toString());//打印:字節(jié)數(shù)組

            //傳入字節(jié)數(shù)組蜡娶,創(chuàng)建RequestBody 對(duì)象
            return RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),encrypt);
        }
    }

    /**
     * JsonResponseBodyConverter<T>
     * @param <T>
     */
    public class JsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
        private final Gson mGson;//gson對(duì)象
        private final TypeAdapter<T> adapter;

        /**
         * 構(gòu)造器
         */
        public JsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.mGson = gson;
            this.adapter = adapter;
        }

        /**
         * 轉(zhuǎn)換
         *
         * @param responseBody
         * @return
         * @throws IOException
         */
        @Override
        public T convert(ResponseBody responseBody) throws IOException {

            byte[] bytes = responseBody.bytes();

            //對(duì)字節(jié)數(shù)組進(jìn)行解密操作
            String decryptString = AESUtils.decrypt(bytes);

            //對(duì)解密的字符串進(jìn)行處理
            int position = decryptString.lastIndexOf("}");
            String jsonString = decryptString.substring(0,position+1);

            Log.i(TAG, "需要解密的服務(wù)器數(shù)據(jù)字節(jié)數(shù)組:" + bytes.toString());
            Log.i(TAG, "解密后的服務(wù)器數(shù)據(jù)字符串:" + decryptString);
            Log.i(TAG, "解密后的服務(wù)器數(shù)據(jù)字符串處理為json:" + jsonString);

            //這部分代碼參考GsonConverterFactory中GsonResponseBodyConverter<T>的源碼對(duì)json的處理
            Reader reader = StringToReader(jsonString);
            JsonReader jsonReader = gson.newJsonReader(reader);
            try {
                return adapter.read(jsonReader);
            } finally {
                reader.close();
                jsonReader.close();
            }
        }

        /**
         * String轉(zhuǎn)Reader
         * @param json
         * @return
         */
        private Reader StringToReader(String json){
            Reader reader  = new StringReader(json);
            return reader;
        }
    }
}

注:為了可以像GsonConverterFactory將Json字符串轉(zhuǎn)化為Java類對(duì)象混卵,我參看了GsonConverterFactory的源碼,在處理完解密操作之后得到了Json字符串窖张,然后我加上GsonConverterFactory的源碼部分來處理Json字符串淮菠。這樣其實(shí)就是在GsonConverterFactory的基礎(chǔ)上添加了加密和解密操作。

參考

1. Retrofit 2 之自定義Converter實(shí)現(xiàn)加密解密

其他參考
retrofit 自定義請(qǐng)求參數(shù)加密 和自定義響應(yīng)解密 帶你走出那些坑
Retrofit 進(jìn)階篇 自定義轉(zhuǎn)換器
Retrofit:打造自己的Converter之byte[]

2.AES/CBC/NoPadding加密解密

找到了一篇關(guān)于PHP和Java的AES互通兼容加密文章荤堪,看完之后發(fā)現(xiàn)了原來PHP的AES加密填充只有ZeroPadding(補(bǔ)零 - 因?yàn)閿?shù)據(jù)長(zhǎng)度不是16的整數(shù)倍就需要填充)合陵,而Java是沒有這種填充模式,杯具的只能自己寫一個(gè)了澄阳,那Java的填充模式就用NoPadding(不填充內(nèi)容)拥知;

public class AESUtils {

    //初始向量(偏移)
    public static final String iv= "7983B5439EF75A69";   //AES 為16bytes. DES 為8bytes

    //編碼方式
    //public static final String bm = "utf-8";

    //私鑰  (密鑰)
    private static final String key="7983b5439ef75a69";   //AES固定格式為128/192/256 bits.即:16/24/32bytes。DES固定格式為128bits碎赢,即8bytes低剔。

    /**
     * 加密
     * @param data 加密前的字符串
     * @return 加密后的字節(jié)數(shù)組
     */
    public static byte[] encrypt(String data){
        try {

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();

            //判斷待加密的字節(jié)數(shù)組的長(zhǎng)度,在此長(zhǎng)度基礎(chǔ)上擴(kuò)展一個(gè)字節(jié)數(shù)組的長(zhǎng)度為16的倍數(shù)
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            //創(chuàng)建需新的待加密的字節(jié)數(shù)組肮塞,將上面的字節(jié)數(shù)組復(fù)制進(jìn)來襟齿,多余的補(bǔ)0
            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

            //加密后的字節(jié)數(shù)組
            byte[] encrypted = cipher.doFinal(plaintext);

            return encrypted;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密
     * @param encrypted1 解密前的字節(jié)數(shù)組
     * @return 解密后的字符串
     */
    public static String decrypt(byte[] encrypted1) {
        try
        {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
            
            //解密后的字節(jié)數(shù)組
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

可以看到aes加密的中間結(jié)果是byte[]類型,直接new String(byte[])會(huì)看不到有意義的中間結(jié)果枕赵,可以在這里用的是base64猜欺,是因?yàn)楦鱾€(gè)語(yǔ)言都有這樣的支持。在同個(gè)語(yǔ)言內(nèi)拷窜,也有bytesToHexString這樣的方式开皿。

跨語(yǔ)言加解密的要求是:AES/CBC/ZeroPadding 128位模式涧黄,key和iv一樣,編碼統(tǒng)一用utf-8赋荆。不支持ZeroPadding的就用NoPadding.

參考

1. AES加密CBC模式兼容互通四種編程語(yǔ)言平臺(tái)【PHP笋妥、Javascript、Java窄潭、C#】(看Java的AES加密解密)

2.C#, Java, PHP, Python和Javascript幾種語(yǔ)言的AES加密解密實(shí)現(xiàn)【多種語(yǔ)言AES/CBC/PKCS5Padding通用加解密數(shù)據(jù)】(看最后的總結(jié))

3.AES對(duì)稱加密算法掃盲(看AES加密的方式)

4.在線AES等加密解密驗(yàn)證工具(驗(yàn)證加解密)

其他參考
java加密算法之AES小記
JAVA實(shí)現(xiàn)AES加密
java使用Hex編碼解碼實(shí)現(xiàn)Aes加密解密功能示例
Java加密算法 AES

3.Retrofit+RxJava

(1)定義HttpService接口

public interface HttpService {
 /**
     * 管理員登錄
     * @return
     */
    @Headers({"Content-Type: application/json","Accept: application/json"})//需要添加頭
    @POST("ictweb.cgi")
    Observable<Result<MasterLoginInfo>> masterLoginEncrypt(@Body String parmasJson);
}

注:這里的參數(shù)是@Body String

(2)// 獲取retrofit的實(shí)例

retrofit = new Retrofit
          .Builder()
          .baseUrl(UrlHelper.BASE_URL)  //自己配置
          .client(client)
          .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
       // .addConverterFactory(GsonConverterFactory.create())
          .addConverterFactory(JsonConverterFactory.create())
          .build();

(3)獲取HttpService代理對(duì)象

HttpService httpService = retrofit.create(HttpService.class);

(4)構(gòu)建請(qǐng)求的json字符串

String jsonString= "{\n" +
            "\t\"cmd\":\"10\",\n" +
            "\t\"content\":{\n" +
            "\t\t\"LoginName\":\"admin\",\t\t\n" +
            "\t\t\"Password\":\"admin\",\t\t\n" +
            "\"ClientType\":\"1\"\t\t\t\n" +
            "}\n" +
            "}";

(5)請(qǐng)求數(shù)據(jù)

httpService.masterLoginEncrypt(jsonString)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Result<MasterLoginInfo>>() {
                    @Override
                    public void onCompleted() {
                        Log.i(TAG, "onCompleted: ");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i(TAG, "error: ");
                    }

                    @Override
                    public void onNext(Result<MasterLoginInfo> masterLoginInfoResult0) {
                        Log.i(TAG, "onNext: result="+masterLoginInfoResult0.getContent().getResult());
                    }
                });

綜上春宣,使用Rertofit+RxJava進(jìn)行網(wǎng)絡(luò)操作,通過自定義繼承Converter.Factory類可以實(shí)現(xiàn)嫉你,在客戶端數(shù)據(jù)請(qǐng)求時(shí)進(jìn)行AES/CBC/NoPadding加密月帝,在服務(wù)器數(shù)據(jù)響應(yīng)時(shí)進(jìn)行AES/CBC/NoPadding解密。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末均抽,一起剝皮案震驚了整個(gè)濱河市嫁赏,隨后出現(xiàn)的幾起案子其掂,更是在濱河造成了極大的恐慌油挥,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件款熬,死亡現(xiàn)場(chǎng)離奇詭異深寥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)贤牛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門惋鹅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人殉簸,你說我怎么就攤上這事闰集。” “怎么了般卑?”我有些...
    開封第一講書人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵武鲁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我蝠检,道長(zhǎng)沐鼠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任叹谁,我火速辦了婚禮饲梭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘焰檩。我一直安慰自己憔涉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開白布析苫。 她就那樣靜靜地躺著监氢,像睡著了一般布蔗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浪腐,一...
    開封第一講書人閱讀 52,821評(píng)論 1 314
  • 那天纵揍,我揣著相機(jī)與錄音,去河邊找鬼议街。 笑死泽谨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的特漩。 我是一名探鬼主播吧雹,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼涂身!你這毒婦竟也來了雄卷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤蛤售,失蹤者是張志新(化名)和其女友劉穎丁鹉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悴能,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揣钦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漠酿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冯凹。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炒嘲,靈堂內(nèi)的尸體忽然破棺而出宇姚,到底是詐尸還是另有隱情,我是刑警寧澤夫凸,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布浑劳,位于F島的核電站,受9級(jí)特大地震影響寸痢,放射性物質(zhì)發(fā)生泄漏呀洲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一啼止、第九天 我趴在偏房一處隱蔽的房頂上張望道逗。 院中可真熱鬧,春花似錦献烦、人聲如沸滓窍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吏夯。三九已至此蜈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間噪生,已是汗流浹背裆赵。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跺嗽,地道東北人战授。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桨嫁,于是被迫代替她去往敵國(guó)和親植兰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,343評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理璃吧,服務(wù)發(fā)現(xiàn)楣导,斷路器,智...
    卡卡羅2017閱讀 134,720評(píng)論 18 139
  • 看有一期的《天天向上》膝晾,汪涵說起節(jié)日家里的男人給女人送花栓始,岳父給岳母獻(xiàn)花务冕,說的不是“我愛你”,而是“我依你”幻赚。很簡(jiǎn)...
    學(xué)徒賢芳閱讀 2,693評(píng)論 0 3
  • 玫瑰嬌嫩美艷禀忆,因此花枝上長(zhǎng)滿了刺用以防御;荷花生于澤地落恼,因此需要荷葉的托舉箩退。可我總也無法想象玉蘭的枝究竟是有怎樣的...
    Zzz宣兒閱讀 446評(píng)論 0 2