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());
}
});