encrypt cookies

only applies to okhttp requests for now, but cookies are stored and sent and on-disk they are encrypted

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/284/head
androidacy-user 1 year ago
parent 08e78d9577
commit 9e7a38ed0a

@ -30,7 +30,10 @@ import com.google.android.material.materialswitch.MaterialSwitch;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.Objects;
@ -336,14 +339,37 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
});
try {
String cookieFileName = "cookies";
File cookieFile = new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName);
String initialCookie = "is_foxmmm=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=\" + chain.request().url().host() + \"; SameSite=None; Secure;|foxmmm_version=" + BuildConfig.VERSION_CODE + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=\" + chain.request().url().host() + \"; SameSite=None; Secure;";
Context context = getApplicationContext();
MasterKey mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build();
MasterKey mainKeyAlias;
mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build();
EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName), mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build();
encryptedFile.openFileOutput().write(initialCookie.getBytes());
encryptedFile.openFileOutput().flush();
encryptedFile.openFileOutput().close();
InputStream inputStream;
try {
inputStream = encryptedFile.openFileInput();
} catch (
FileNotFoundException e) {
Timber.d("Cookie file not found, creating new file");
OutputStream outputStream = encryptedFile.openFileOutput();
outputStream.write(initialCookie.getBytes());
outputStream.close();
outputStream.flush();
inputStream = encryptedFile.openFileInput();
}
byte[] buffer = new byte[1024];
int bytesRead;
StringBuilder outputString = new StringBuilder();
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputString.append(new String(buffer, 0, bytesRead));
}
inputStream.close();
if (outputString.toString().isEmpty()) {
Timber.d("Cookie file is empty, writing initial cookie");
OutputStream outputStream = encryptedFile.openFileOutput();
outputStream.write(initialCookie.getBytes());
outputStream.close();
outputStream.flush();
}
} catch (GeneralSecurityException |
IOException e) {
Timber.e(e);

@ -1,9 +1,5 @@
package com.fox2code.mmm.utils.io;
// Original written by tsuharesu
// Adapted to create a "drop it in and watch it work" approach by Nikhil Jha.
// Just add your package statement and drop it in the folder with all your other classes.
import android.content.Context;
import androidx.annotation.NonNull;
@ -12,7 +8,6 @@ import androidx.security.crypto.MasterKey;
import com.fox2code.mmm.MainApplication;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -24,8 +19,6 @@ import timber.log.Timber;
public class AddCookiesInterceptor implements Interceptor {
// We're storing our stuff in a database made just for cookies called PREF_COOKIES.
// I reccomend you do this, and don't change this default value.
private final Context context;
public AddCookiesInterceptor(Context context) {
@ -36,44 +29,37 @@ public class AddCookiesInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
MasterKey mainKeyAlias;
// Cookies are stored in an encrypted file in the files directory in our app data
// so we need to decrypt the file before using it
// first, get our decryption key from MasterKey using the AES_256_GCM encryption scheme
// then, create an EncryptedFile object using the key and the file name
// finally, open the file and read the contents into a string
// the string is then split into an array of cookies
// the cookies are then added to the request builder
String cookieFileName = "cookies";
byte[] plaintext;
String[] cookies = new String[0];
MasterKey mainKeyAlias;
try {
// create cookie file if it doesn't exist
mainKeyAlias = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build();
EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName), mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build();
InputStream inputStream;
inputStream = encryptedFile.openFileInput();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int nextByte = inputStream.read();
while (nextByte != -1) {
byteArrayOutputStream.write(nextByte);
nextByte = inputStream.read();
InputStream inputStream = encryptedFile.openFileInput();
byte[] buffer = new byte[1024];
int bytesRead;
StringBuilder outputString = new StringBuilder();
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputString.append(new String(buffer, 0, bytesRead));
}
plaintext = byteArrayOutputStream.toByteArray();
cookies = outputString.toString().split("\\|");
inputStream.close();
} catch (
Exception e) {
Timber.e(e, "Error while reading cookies");
plaintext = new byte[0];
} catch (Exception e) {
Timber.e(e, "Error reading cookies from file");
}
String[] preferences = new String(plaintext).split("\\|");
// Use the following if you need everything in one line.
// Some APIs die if you do it differently.
StringBuilder cookiestring = new StringBuilder();
for (String cookie : preferences) {
// if cookie doesn't end in a semicolon, add one.
if (!cookie.endsWith(";")) {
cookie = cookie + ";";
}
cookiestring.append(cookie).append(" ");
for (String cookie : cookies) {
builder.addHeader("Cookie", cookie);
}
Timber.d("Sending cookies: %s", cookiestring.toString());
builder.addHeader("Cookie", cookiestring.toString());
return chain.proceed(builder.build());
}

@ -1,9 +1,5 @@
package com.fox2code.mmm.utils.io;
// Original written by tsuharesu
// Adapted to create a "drop it in and watch it work" approach by Nikhil Jha.
// Just add your package statement and drop it in the folder with all your other classes.
import android.annotation.SuppressLint;
import android.content.Context;
@ -11,14 +7,11 @@ import androidx.annotation.NonNull;
import androidx.security.crypto.EncryptedFile;
import androidx.security.crypto.MasterKey;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.MainApplication;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.HashSet;
@ -40,48 +33,74 @@ public class ReceivedCookiesInterceptor implements Interceptor {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
MasterKey mainKeyAlias;
StringBuilder cookieBuffer = new StringBuilder();
for (String header : originalResponse.headers("Set-Cookie")) {
cookieBuffer.append(header).append("|");
} // for
int lastPipe = cookieBuffer.lastIndexOf("|");
if (lastPipe > 0) {
cookieBuffer.deleteCharAt(lastPipe);
}
// Cookies are stored in an encrypted file in the files directory in our app data
// so we need to decrypt the file before using it
// first, get our decryption key from MasterKey using the AES_256_GCM encryption scheme
// then, create an EncryptedFile object using the key and the file name
// finally, open the file and read the contents into a string
// the string is then split into an array of cookies
String cookieFileName = "cookies";
byte[] plaintext;
String[] cookies = new String[0];
MasterKey mainKeyAlias;
try {
mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build();
EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName), mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build();
InputStream inputStream = encryptedFile.openFileInput();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int nextByte = inputStream.read();
while (nextByte != -1) {
byteArrayOutputStream.write(nextByte);
nextByte = inputStream.read();
byte[] buffer = new byte[1024];
int bytesRead;
StringBuilder outputString = new StringBuilder();
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputString.append(new String(buffer, 0, bytesRead));
}
plaintext = byteArrayOutputStream.toByteArray();
cookies = outputString.toString().split("\\|");
inputStream.close();
} catch (
Exception e) {
e.printStackTrace();
plaintext = new byte[0];
} catch (Exception e) {
Timber.e(e, "Error reading cookies from file");
}
HashSet<String> cookies = new HashSet<>(Arrays.asList(new String(plaintext).split("\\|")));
HashSet<String> cookieSet = new HashSet<>(originalResponse.headers("Set-Cookie"));
if (BuildConfig.DEBUG_HTTP) {
Timber.d("Received cookies: %s", cookieSet);
// Logic to merge our cookies with received cookies
// We need to check if the cookie we received is already in our file
// If it is, we need to replace it with the new one
// If it isn't, we need to add it to the end of the file
HashSet<String> cookieSet = new HashSet<>(Arrays.asList(cookies));
String[] newCookies = cookieBuffer.toString().split("\\|");
for (String cookie : newCookies) {
cookieSet.remove(cookie);
cookieSet.add(cookie);
}
// convert the set back into a string
StringBuilder newCookieBuffer = new StringBuilder();
for (String cookie : cookieSet) {
newCookieBuffer.append(cookie).append("|");
}
// if we already have the cooki in cookies, remove the one in cookies
cookies.removeIf(cookie -> cookieSet.toString().contains(cookie.split(";")[0]));
// add the new cookies to the cookies
cookies.addAll(cookieSet);
// write the cookies to the file
// remove the last pipe
lastPipe = newCookieBuffer.lastIndexOf("|");
if (lastPipe > 0) {
newCookieBuffer.deleteCharAt(lastPipe);
}
// write the new cookies to the file
try {
mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build();
EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName), mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build();
encryptedFile.openFileOutput().write(String.join("|", cookies).getBytes());
encryptedFile.openFileOutput().flush();
encryptedFile.openFileOutput().close();
Timber.d("Storing encrypted cookies: %s", String.join("|", cookies));
} catch (
GeneralSecurityException e) {
throw new IllegalStateException("Unable to get master key", e);
encryptedFile.openFileOutput().write(newCookieBuffer.toString().getBytes());
} catch (Exception e) {
Timber.e(e, "Error writing cookies to file");
}
}
return originalResponse;

Loading…
Cancel
Save