Skip to content

Commit

Permalink
feat: add android decrypt RSA AES
Browse files Browse the repository at this point in the history
  • Loading branch information
riderx committed Dec 2, 2022
1 parent 5b1c414 commit 946354e
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 186 deletions.
255 changes: 122 additions & 133 deletions android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package ee.forgr.capacitor_updater;

import static ee.forgr.capacitor_updater.RSACipher.stringToPrivateKey;

import android.content.SharedPreferences;
import android.os.Build;
import android.util.Base64;
import android.util.Log;

import com.android.volley.BuildConfig;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.getcapacitor.JSObject;
import com.getcapacitor.android.BuildConfig;
import com.getcapacitor.plugin.WebView;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
Expand All @@ -24,8 +24,7 @@
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.util.ArrayList;
Expand All @@ -35,9 +34,8 @@
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

import org.json.JSONException;
import org.json.JSONObject;

Expand Down Expand Up @@ -94,23 +92,23 @@ private boolean isProd() {

private boolean isEmulator() {
return (
(Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) ||
Build.FINGERPRINT.startsWith("generic") ||
Build.FINGERPRINT.startsWith("unknown") ||
Build.HARDWARE.contains("goldfish") ||
Build.HARDWARE.contains("ranchu") ||
Build.MODEL.contains("google_sdk") ||
Build.MODEL.contains("Emulator") ||
Build.MODEL.contains("Android SDK built for x86") ||
Build.MANUFACTURER.contains("Genymotion") ||
Build.PRODUCT.contains("sdk_google") ||
Build.PRODUCT.contains("google_sdk") ||
Build.PRODUCT.contains("sdk") ||
Build.PRODUCT.contains("sdk_x86") ||
Build.PRODUCT.contains("sdk_gphone64_arm64") ||
Build.PRODUCT.contains("vbox86p") ||
Build.PRODUCT.contains("emulator") ||
Build.PRODUCT.contains("simulator")
(Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) ||
Build.FINGERPRINT.startsWith("generic") ||
Build.FINGERPRINT.startsWith("unknown") ||
Build.HARDWARE.contains("goldfish") ||
Build.HARDWARE.contains("ranchu") ||
Build.MODEL.contains("google_sdk") ||
Build.MODEL.contains("Emulator") ||
Build.MODEL.contains("Android SDK built for x86") ||
Build.MANUFACTURER.contains("Genymotion") ||
Build.PRODUCT.contains("sdk_google") ||
Build.PRODUCT.contains("google_sdk") ||
Build.PRODUCT.contains("sdk") ||
Build.PRODUCT.contains("sdk_x86") ||
Build.PRODUCT.contains("sdk_gphone64_arm64") ||
Build.PRODUCT.contains("vbox86p") ||
Build.PRODUCT.contains("emulator") ||
Build.PRODUCT.contains("simulator")
);
}

Expand Down Expand Up @@ -149,7 +147,7 @@ private File unzip(final String id, final File zipFile, final String dest) throw

if (!canonicalPath.startsWith(canonicalDir)) {
throw new FileNotFoundException(
"SecurityException, Failed to ensure directory is the start path : " + canonicalDir + " of " + canonicalPath
"SecurityException, Failed to ensure directory is the start path : " + canonicalDir + " of " + canonicalPath
);
}

Expand Down Expand Up @@ -264,53 +262,44 @@ private String getChecksum(File file) throws IOException {
return enc.toLowerCase();
}

private void decodeFile(File file) throws IOException {
if (this.privateKey.equals("")) {
private void decryptFile(final File file, final String ivSessionKey) throws IOException {
if (this.privateKey.equals("") || ivSessionKey.equals("")) {
return;
}
try {
PrivateKey pKey = RSACipher.stringToPrivateKey(this.privateKey);
FileInputStream fis = new FileInputStream(file.getAbsolutePath());
byte[] buffer = new byte[10];
StringBuilder sb = new StringBuilder();
while (fis.read(buffer) != -1) {
sb.append(new String(buffer));
buffer = new byte[10];
}
fis.close();
String content = sb.toString();
String decrypted = RSACipher.decryptRSA(content, pKey);
String ivB64 = ivSessionKey.split(":")[0];
String sessionKeyB64 = ivSessionKey.split(":")[1];
byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
byte[] sessionKey = Base64.decode(sessionKeyB64.getBytes(), Base64.DEFAULT);
PrivateKey pKey = CryptoCipher.stringToPrivateKey(this.privateKey);
byte[] decryptedSessionKey = CryptoCipher.decryptRSA(sessionKey, pKey);
SecretKey sKey = CryptoCipher.byteToSessionKey(decryptedSessionKey);
byte[] content = new byte[(int) file.length()];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
DataInputStream dis = new DataInputStream(bis);
dis.readFully(content);
dis.close();
byte[] decrypted = CryptoCipher.decryptAES(content, sKey, iv);
// write the decrypted string to the file
FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
fos.write(decrypted.getBytes());
fos.write(decrypted);
fos.close();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
throw new IOException("NoSuchPaddingException");
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
throw new IOException("IllegalBlockSizeException");
} catch (NoSuchAlgorithmException e) {
} catch (GeneralSecurityException e) {
Log.i(TAG, "decryptFile fail");
e.printStackTrace();
throw new IOException("NoSuchAlgorithmException");
} catch (BadPaddingException e) {
e.printStackTrace();
throw new IOException("BadPaddingException");
} catch (InvalidKeyException e) {
e.printStackTrace();
throw new IOException("InvalidKeyException");
throw new IOException("GeneralSecurityException");
}
}

public BundleInfo download(final String url, final String version) throws IOException {
public BundleInfo download(final String url, final String version, final String sessionKey) throws IOException {
final String id = this.randomString(10);
this.saveBundleInfo(id, new BundleInfo(id, version, BundleStatus.DOWNLOADING, new Date(System.currentTimeMillis()), ""));
this.notifyDownload(id, 0);
final String idName = bundleDirectory + "/" + id;
this.notifyDownload(id, 5);
final File downloaded = this.downloadFile(id, url, this.randomString(10));
this.decryptFile(downloaded, sessionKey);
final String checksum = this.getChecksum(downloaded);
this.decodeFile(downloaded);
this.notifyDownload(id, 71);
final File unzipped = this.unzip(id, downloaded, this.randomString(10));
downloaded.delete();
Expand Down Expand Up @@ -457,21 +446,21 @@ public void getLatest(final String updateUrl, final Callback callback) {
Log.i(CapacitorUpdater.TAG, "Auto-update parameters: " + json);
// Building a request
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.POST,
updateUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
callback.callback(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error getting Latest" + error.toString());
Request.Method.POST,
updateUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
callback.callback(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error getting Latest" + error.toString());
}
}
}
);
this.requestQueue.add(request);
} catch (JSONException ex) {
Expand All @@ -491,34 +480,34 @@ public void setChannel(final String channel, final CallbackChannel callback) {

// Building a request
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.POST,
channelUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject res) {
final JSObject ret = new JSObject();
Iterator<String> keys = res.keys();
while (keys.hasNext()) {
String key = keys.next();
if (res.has(key)) {
try {
ret.put(key, res.get(key));
} catch (JSONException e) {
e.printStackTrace();
Request.Method.POST,
channelUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject res) {
final JSObject ret = new JSObject();
Iterator<String> keys = res.keys();
while (keys.hasNext()) {
String key = keys.next();
if (res.has(key)) {
try {
ret.put(key, res.get(key));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Log.i(TAG, "Channel set to \"" + channel);
callback.callback(ret);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error set channel: " + error.toString());
}
Log.i(TAG, "Channel set to \"" + channel);
callback.callback(ret);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error set channel: " + error.toString());
}
}
);
this.requestQueue.add(request);
} catch (JSONException ex) {
Expand All @@ -537,34 +526,34 @@ public void getChannel(final CallbackChannel callback) {

// Building a request
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.PUT,
channelUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject res) {
final JSObject ret = new JSObject();
Iterator<String> keys = res.keys();
while (keys.hasNext()) {
String key = keys.next();
if (res.has(key)) {
try {
ret.put(key, res.get(key));
} catch (JSONException e) {
e.printStackTrace();
Request.Method.PUT,
channelUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject res) {
final JSObject ret = new JSObject();
Iterator<String> keys = res.keys();
while (keys.hasNext()) {
String key = keys.next();
if (res.has(key)) {
try {
ret.put(key, res.get(key));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Log.i(TAG, "Channel get to \"" + ret);
callback.callback(ret);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error get channel: " + error.toString());
}
Log.i(TAG, "Channel get to \"" + ret);
callback.callback(ret);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error get channel: " + error.toString());
}
}
);
this.requestQueue.add(request);
} catch (JSONException ex) {
Expand All @@ -584,21 +573,21 @@ public void sendStats(final String action, final String versionName) {

// Building a request
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.POST,
statsUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i(TAG, "Stats send for \"" + action + "\", version " + versionName);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error sending stats: " + error.toString());
Request.Method.POST,
statsUrl,
json,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i(TAG, "Stats send for \"" + action + "\", version " + versionName);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error sending stats: " + error.toString());
}
}
}
);
this.requestQueue.add(request);
} catch (JSONException ex) {
Expand Down Expand Up @@ -734,4 +723,4 @@ public boolean setNextBundle(final String next) {
this.editor.commit();
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ public void run() {
public void download(final PluginCall call) {
final String url = call.getString("url");
final String version = call.getString("version");
final String sessionKey = call.getString("sessionKey", "");
if (url == null) {
Log.e(CapacitorUpdater.TAG, "Download called without url");
call.reject("Download called without url");
Expand All @@ -267,7 +268,7 @@ public void download(final PluginCall call) {
@Override
public void run() {
try {
final BundleInfo downloaded = CapacitorUpdaterPlugin.this.implementation.download(url, version);
final BundleInfo downloaded = CapacitorUpdaterPlugin.this.implementation.download(url, version, sessionKey);
call.resolve(downloaded.toJSON());
} catch (final IOException e) {
Log.e(CapacitorUpdater.TAG, "Failed to download from: " + url, e);
Expand Down Expand Up @@ -764,8 +765,9 @@ public void run() {
);

final String url = res.getString("url");
final String session_key = res.has("session_key") ? res.getString("session_key") : "";
final BundleInfo next =
CapacitorUpdaterPlugin.this.implementation.download(url, latestVersionName);
CapacitorUpdaterPlugin.this.implementation.download(url, latestVersionName, session_key);
final String checksum = res.has("checksum") ? res.getString("checksum") : "";
if (!checksum.equals("") && !next.getChecksum().equals(checksum)) {
Log.e(
Expand Down
Loading

0 comments on commit 946354e

Please sign in to comment.