Add encryption of the password

This commit is contained in:
Privacy_Dragon 2022-02-16 17:32:14 +01:00
parent c75cf18dd2
commit c6bc914608
9 changed files with 163 additions and 34 deletions

1
.idea/.name generated Normal file
View File

@ -0,0 +1 @@
Bookwyrm

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_2_API_24.avd" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2022-02-14T18:35:03.252054Z" />
</component>
</project>

1
.idea/gradle.xml generated
View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>

View File

@ -1,3 +1,7 @@
# Bookwyrm-android # Bookwyrm-android
A crappy attempt at creating an Android application for Bookwyrm. Basically, it is just bookwyrm put into a 'webview' element. Also, I am not sure whether the credentials are stored in a safe way, so be warned! (If you know how to make it safer, please do a pull request!) A crappy attempt at creating an Android application for Bookwyrm. Basically, it is just bookwyrm put into a 'webview' element.
What it does? It enables you to use BookWyrm on your Android phone without having to use a browser to go to it every time.
Currently, it is not able to open links yet, but maybe I will add that sooner or later.
This application works on: Android 6 and above.

View File

@ -7,7 +7,7 @@ android {
defaultConfig { defaultConfig {
applicationId "nl.privacydragon.bookwyrm" applicationId "nl.privacydragon.bookwyrm"
minSdk 21 minSdk 23
targetSdk 31 targetSdk 31
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"

View File

@ -3,7 +3,7 @@
package="nl.privacydragon.bookwyrm"> package="nl.privacydragon.bookwyrm">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:allowBackup="true" android:allowBackup="false"
android:icon="@mipmap/ic_launcher_wyrm" android:icon="@mipmap/ic_launcher_wyrm"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_wyrm_round" android:roundIcon="@mipmap/ic_launcher_wyrm_round"

View File

@ -4,19 +4,41 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Color; import android.graphics.Color;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.util.Base64;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
//Context context = getActivity(); //Check whether there is something stored. Only if there is already something stored, proceed to BookWyrm.
SharedPreferences sharedPref = MainActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); SharedPreferences sharedPref = MainActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE);
String defaultValue = "none"; String defaultValue = "none";
String server = sharedPref.getString(getString(R.string.server), defaultValue); String server = sharedPref.getString(getString(R.string.server), defaultValue);
@ -25,31 +47,72 @@ public class MainActivity extends AppCompatActivity {
} }
} }
private static String getRandomString() //I just copied this from internet. Yes, I am lazy :). (https://stackoverflow.com/questions/12116092/android-random-string-generator#answer-12116194)
{
String ALLOWED_CHARACTERS ="0123456789qwertyuiopasdfghjklzxcvbnm!@#$%^&*()_+=][{}";
final Random random=new Random();
final StringBuilder sb=new StringBuilder(12);
for(int i = 0; i< 12; ++i)
sb.append(ALLOWED_CHARACTERS.charAt(random.nextInt(ALLOWED_CHARACTERS.length())));
return sb.toString();
}
public void LogIn(View view) { public void LogIn(View view) throws IllegalBlockSizeException, BadPaddingException, KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, UnrecoverableKeyException, NoSuchPaddingException, InvalidKeyException {
//Declaring some things needed. Getting the user input.
EditText serverInput = (EditText) findViewById(R.id.Instance); EditText serverInput = (EditText) findViewById(R.id.Instance);
String server = String.valueOf(serverInput.getText()); String server = String.valueOf(serverInput.getText());
EditText passInput = (EditText) findViewById(R.id.Password); EditText passInput = (EditText) findViewById(R.id.Password);
String pass = String.valueOf(passInput.getText()); String pass = String.valueOf(passInput.getText());
EditText nameInput = (EditText) findViewById(R.id.Username); EditText nameInput = (EditText) findViewById(R.id.Username);
String name = String.valueOf(nameInput.getText()); String name = String.valueOf(nameInput.getText());
//All fields are required, so if one of them is empty, the user should see a warning.
if (server.isEmpty() || pass.isEmpty() || name.isEmpty()) { if (server.isEmpty() || pass.isEmpty() || name.isEmpty()) {
TextView ErrorMessage = (TextView) findViewById(R.id.textView5); TextView ErrorMessage = (TextView) findViewById(R.id.textView5);
ErrorMessage.setTextColor(Color.RED); ErrorMessage.setTextColor(Color.RED);
ErrorMessage.setText("ERROR: All fields are required!"); ErrorMessage.setText("ERROR: All fields are required!");
} else { } else {
//Likely this will be the first time the program is run. So create a new key thing in the android key store happening.
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
if (!keyStore.containsAlias("BookWyrm")) { //Actually, the new key is made here, if it does not exist already.
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(
new KeyGenParameterSpec.Builder("BookWyrm",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build());
keyGenerator.generateKey();
}
//Grab the key and initiate the encryption process stuff. For this, a random fixed IV code is generated.
Key DragonLikeKey = keyStore.getKey("BookWyrm", null);
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
String magicCode = getRandomString();
c.init(Cipher.ENCRYPT_MODE, DragonLikeKey, new GCMParameterSpec(128, magicCode.getBytes()));
//And now do the encryption!
byte[] passBytes = c.doFinal(pass.getBytes());
String passUse = Base64.encodeToString(passBytes, Base64.DEFAULT);
//And then all the things are stored in the shared preferences.
//Therefore, first all the shared preferences objects are loaded.
SharedPreferences sharedPref = MainActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); SharedPreferences sharedPref = MainActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE);
SharedPreferences sharedPrefName = MainActivity.this.getSharedPreferences(getString(R.string.name), Context.MODE_PRIVATE); SharedPreferences sharedPrefName = MainActivity.this.getSharedPreferences(getString(R.string.name), Context.MODE_PRIVATE);
SharedPreferences sharedPrefPass = MainActivity.this.getSharedPreferences(getString(R.string.pw), Context.MODE_PRIVATE); SharedPreferences sharedPrefPass = MainActivity.this.getSharedPreferences(getString(R.string.pw), Context.MODE_PRIVATE);
SharedPreferences sharedPrefMagic = MainActivity.this.getSharedPreferences(getString(R.string.q), Context.MODE_PRIVATE);
//Then the 'edit' stuff are made for them.
SharedPreferences.Editor editorName = sharedPrefName.edit(); SharedPreferences.Editor editorName = sharedPrefName.edit();
SharedPreferences.Editor editorPass = sharedPrefPass.edit(); SharedPreferences.Editor editorPass = sharedPrefPass.edit();
SharedPreferences.Editor editorMagic = sharedPrefMagic.edit();
SharedPreferences.Editor editor = sharedPref.edit(); SharedPreferences.Editor editor = sharedPref.edit();
//And finally, the values are written to them.
editor.putString(getString(R.string.server), server); editor.putString(getString(R.string.server), server);
editor.apply(); editor.apply();
editorName.putString(getString(R.string.name), name); editorName.putString(getString(R.string.name), name);
editorName.apply(); editorName.apply();
editorPass.putString(getString(R.string.pw), pass); editorPass.putString(getString(R.string.pw), passUse);
editorPass.apply(); editorPass.apply();
editorMagic.putString(getString(R.string.q), magicCode);
editorMagic.apply();
//Once all that has been done, Bookwyrm can be opened and such!
startActivity(new Intent(MainActivity.this, nl.privacydragon.bookwyrm.StartActivity.class)); startActivity(new Intent(MainActivity.this, nl.privacydragon.bookwyrm.StartActivity.class));
} }
} }

View File

@ -6,10 +6,28 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.util.Base64;
import android.webkit.WebResourceRequest; import android.webkit.WebResourceRequest;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
public class StartActivity extends AppCompatActivity { public class StartActivity extends AppCompatActivity {
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
@ -19,7 +37,7 @@ public class StartActivity extends AppCompatActivity {
setContentView(R.layout.activity_start); setContentView(R.layout.activity_start);
WebView myWebView = (WebView) findViewById(R.id.webview); WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.getSettings().setJavaScriptEnabled(true); myWebView.getSettings().setJavaScriptEnabled(true);
//Context context = StartActivity.this; //The user credentials are stored in the shared preferences, so first they have to be read from there.
String defaultValue = "none"; String defaultValue = "none";
SharedPreferences sharedPref = StartActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); SharedPreferences sharedPref = StartActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE);
String server = sharedPref.getString(getString(R.string.server), defaultValue); String server = sharedPref.getString(getString(R.string.server), defaultValue);
@ -27,23 +45,81 @@ public class StartActivity extends AppCompatActivity {
String name = sharedPrefName.getString(getString(R.string.name), defaultValue); String name = sharedPrefName.getString(getString(R.string.name), defaultValue);
SharedPreferences sharedPrefPass = StartActivity.this.getSharedPreferences(getString(R.string.pw), Context.MODE_PRIVATE); SharedPreferences sharedPrefPass = StartActivity.this.getSharedPreferences(getString(R.string.pw), Context.MODE_PRIVATE);
String pass = sharedPrefPass.getString(getString(R.string.pw), defaultValue); String pass = sharedPrefPass.getString(getString(R.string.pw), defaultValue);
SharedPreferences sharedPrefMagic = StartActivity.this.getSharedPreferences(getString(R.string.q), Context.MODE_PRIVATE);
String codeMagic = sharedPrefMagic.getString(getString(R.string.q), defaultValue);
//Then all the decryption stuff has to happen. There are a lot of try-catch stuff, because apparently that seems to be needed.
//First get the keystore thing.
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException e) {
e.printStackTrace();
}
//Then, load it. or something. To make sure that it can be used.
try {
keyStore.load(null);
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//Next, retrieve the key to be used for the decryption.
Key DragonLikeKey = null;
try {
DragonLikeKey = keyStore.getKey("BookWyrm", null);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
//Do something with getting the/a cipher or something.
Cipher c = null;
try {
c = Cipher.getInstance("AES/GCM/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
//And then initiating the cipher, so it can be used.
try {
c.init(Cipher.DECRYPT_MODE, DragonLikeKey, new GCMParameterSpec(128, codeMagic.getBytes()));
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
//Decrypt the password!
byte[] truePass = null;
try {
truePass = c.doFinal(Base64.decode(pass, Base64.DEFAULT));
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
//Convert the decrypted password back to a string.
String passw = new String(truePass, StandardCharsets.UTF_8);
//A webviewclient thing is needed for some stuff. To automatically log in, the credentials are put in the form by the javascript that is loaded once the page is fully loaded. Then it is automatically submitted if the current page is the login page.
myWebView.setWebViewClient(new MyWebViewClient(){ myWebView.setWebViewClient(new MyWebViewClient(){
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:(function() { document.getElementById('id_password_confirm').value = '" + pass + "'; ;})()"); view.loadUrl("javascript:(function() { document.getElementById('id_password_confirm').value = '" + passw + "'; ;})()");
view.loadUrl("javascript:(function() { document.getElementById('id_localname_confirm').value = '" + name + "'; ;})()"); view.loadUrl("javascript:(function() { document.getElementById('id_localname_confirm').value = '" + name + "'; ;})()");
view.loadUrl("javascript:(function() { if (window.location.href == 'https://" + server + "/login') { document.getElementsByName(\"login-confirm\")[0].submit();} ;})()"); view.loadUrl("javascript:(function() { if (window.location.href == 'https://" + server + "/login') { document.getElementsByName(\"login-confirm\")[0].submit();} ;})()");
} }
}); });
//String data = "localname="+name+"&password="+pass; //Here, load the login page of the server. That actually does all that is needed.
//String data = "";
//myWebView.loadDataWithBaseURL("https://httpbin.org");
//myWebView.loadDataWithBaseURL("https://httpbin.org/", data, "", "", "");
//myWebView.postUrl("https://"+ server + "/login", EncodingUtils.getBytes(data, "base64"));
myWebView.loadUrl("https://" + server + "/login"); myWebView.loadUrl("https://" + server + "/login");
//myWebView.loadUrl("javascript:document.getElementsById('id_password_confirm').value = \""+pass+"\"");
} }
//Here is code to make sure that links of the bookwyrm server are handled withing the webview client, instead of having it open in the default browser.
//Yes, I used the web for this too.
private class MyWebViewClient extends WebViewClient { private class MyWebViewClient extends WebViewClient {
@Override @Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
@ -51,10 +127,10 @@ public class StartActivity extends AppCompatActivity {
String defaultValue = "none"; String defaultValue = "none";
String server = sharedPref.getString(getString(R.string.server), defaultValue); String server = sharedPref.getString(getString(R.string.server), defaultValue);
if (server.equals(request.getUrl().getHost())) { if (server.equals(request.getUrl().getHost())) {
// This is my website, so do not override; let my WebView load the page //If the server is the same as the bookwyrm, load it in the webview.
return false; return false;
} }
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs // Otherwise, it should go to the default browser instead.
Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl()); Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
startActivity(intent); startActivity(intent);
return true; return true;

View File

@ -3,4 +3,5 @@
<string name="server">bla</string> <string name="server">bla</string>
<string name="name">blup</string> <string name="name">blup</string>
<string name="pw">gloep</string> <string name="pw">gloep</string>
<string name="q">wheeeee</string>
</resources> </resources>