diff --git a/app/build.gradle b/app/build.gradle index 997a85f7a..47c462ded 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -173,10 +173,9 @@ dependencies { //debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2' implementation 'com.huangyz0918:androidwm-light:0.1.2' + implementation "com.madgag.spongycastle:bctls-jdk15on:1.58.0.0" - implementation "com.madgag.spongycastle:prov:1.58.0.0" - implementation "com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0" - implementation "com.madgag.spongycastle:bcpg-jdk15on:1.58.0.0" + //implementation 'org.bouncycastle:bcprov-jdk15on:1.64' implementation 'com.github.UnifiedPush:android-connector:1.0.0' //Flavors diff --git a/app/src/main/java/app/fedilab/android/asynctasks/PostSubscriptionAsyncTask.java b/app/src/main/java/app/fedilab/android/asynctasks/PostSubscriptionAsyncTask.java index 1e183cc25..1dbd6c446 100644 --- a/app/src/main/java/app/fedilab/android/asynctasks/PostSubscriptionAsyncTask.java +++ b/app/src/main/java/app/fedilab/android/asynctasks/PostSubscriptionAsyncTask.java @@ -48,11 +48,11 @@ public class PostSubscriptionAsyncTask { protected void doInBackground() { new Thread(() -> { apiResponse = new API(contextReference.get(), account.getInstance(), account.getToken()).getPushSubscription(); - if (apiResponse == null || apiResponse.getPushSubscription() == null) { + if (apiResponse == null || apiResponse.getPushSubscription() == null || endpoint.compareTo(apiResponse.getPushSubscription().getEndpoint()) != 0) { apiResponse = new API(contextReference.get(), account.getInstance(), account.getToken()).pushSubscription(endpoint, account); - } else if (apiResponse != null && endpoint.compareTo(apiResponse.getPushSubscription().getEndpoint()) != 0) { - apiResponse = new API(contextReference.get(), account.getInstance(), account.getToken()).updatePushSubscription(endpoint); } + //TODO: remove this line + apiResponse = new API(contextReference.get(), account.getInstance(), account.getToken()).pushSubscription(endpoint, account); List accountList = new ArrayList<>(); accountList.add(account); apiResponse.setAccounts(accountList); diff --git a/app/src/main/java/app/fedilab/android/client/API.java b/app/src/main/java/app/fedilab/android/client/API.java index d99111eb3..a360a9ca3 100644 --- a/app/src/main/java/app/fedilab/android/client/API.java +++ b/app/src/main/java/app/fedilab/android/client/API.java @@ -27,7 +27,7 @@ import android.util.Log; import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import com.google.gson.Gson; + import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonObject; @@ -5790,8 +5790,8 @@ public class API { PushSubscription pushSubscription = null; try { String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/push/subscription"), 10, null, prefKeyOauthTokenT); + Log.v(Helper.TAG, "response GET: " + response); pushSubscription = parsePushNotifications(new JSONObject(response)); - Log.v(Helper.TAG, "response2: " + response); } catch (HttpsConnection.HttpsConnectionException e) { setError(e.getStatusCode(), e); e.printStackTrace(); @@ -5818,14 +5818,21 @@ public class API { boolean notif_status = sharedpreferences.getBoolean(Helper.SET_NOTIF_STATUS, true); boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true); + HashMap params = new HashMap<>(); + try { + endpoint = URLEncoder.encode(endpoint, "UTF-8"); + } catch (UnsupportedEncodingException ignored) { + } + params.put("subscription[endpoint]", endpoint); params.put("data[alerts][follow]", String.valueOf(notif_follow)); params.put("data[alerts][mention]", String.valueOf(notif_mention)); params.put("data[alerts][favourite]", String.valueOf(notif_add)); params.put("data[alerts][reblog]", String.valueOf(notif_share)); params.put("data[alerts][poll]", String.valueOf(notif_poll)); - params.put("subscription[endpoint]", endpoint); + try { + String response = new HttpsConnection(context, this.instance).put(getAbsoluteUrl("/push/subscription"), 10, params, prefKeyOauthTokenT); pushSubscription = parsePushNotifications(new JSONObject(response)); Log.v(Helper.TAG, "response3: " + response); @@ -5855,23 +5862,56 @@ public class API { boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true); HashMap params = new HashMap<>(); - params.put("data[alerts][follow]", String.valueOf(notif_follow)); - params.put("data[alerts][mention]", String.valueOf(notif_mention)); - params.put("data[alerts][favourite]", String.valueOf(notif_add)); - params.put("data[alerts][reblog]", String.valueOf(notif_share)); - params.put("data[alerts][poll]", String.valueOf(notif_poll)); - params.put("subscription[endpoint]", endpoint); + /*try { + endpoint = URLEncoder.encode(endpoint, "UTF-8"); + } catch (UnsupportedEncodingException ignored) { + }*/ + ECDH ecdh = ECDH.getInstance(); String pubKey = ecdh.getPublicKey(context); byte[] randBytes = new byte[16]; new Random().nextBytes(randBytes); - String auth = Base64.encodeToString(randBytes, Base64.DEFAULT); - params.put("subscription[keys][p256dh]", pubKey); - params.put("subscription[keys][auth]", auth); + String auth = ECDH.base64Encode(randBytes); + + /*try { + pubKey = URLEncoder.encode(pubKey.replaceAll("\n",""), "UTF-8"); + } catch (UnsupportedEncodingException ignored) { + } try { - String response = new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/push/subscription"), 10, params, prefKeyOauthTokenT); + auth = URLEncoder.encode(auth.replaceAll("\n",""), "UTF-8"); + } catch (UnsupportedEncodingException ignored) { + }*/ + + JSONObject jsonObject = new JSONObject(); + + + try { + JSONObject jsonObjectSub = new JSONObject(); + + jsonObjectSub.put("endpoint", endpoint); + JSONObject jsonObjectkey = new JSONObject(); + jsonObjectkey.put("p256dh", pubKey); + jsonObjectkey.put("auth", auth); + jsonObjectSub.put("keys", jsonObjectkey); + jsonObject.put("subscription", jsonObjectSub); + + JSONObject jsonObjectdata = new JSONObject(); + JSONObject jsonObjectarlerts = new JSONObject(); + jsonObjectarlerts.put("follow", notif_follow); + jsonObjectarlerts.put("mention", notif_mention); + jsonObjectarlerts.put("favourite", notif_add); + jsonObjectarlerts.put("reblog", notif_share); + jsonObjectarlerts.put("poll", notif_poll); + jsonObjectdata.put("alerts", jsonObjectarlerts); + jsonObject.put("data", jsonObjectdata); + } catch (JSONException e) { + e.printStackTrace(); + } + Log.v(Helper.TAG, "jsonObject: " + jsonObject); + try { + String response = new HttpsConnection(context, this.instance).postJson(getAbsoluteUrl("/push/subscription"), 10, jsonObject, prefKeyOauthTokenT); pushSubscription = parsePushNotifications(new JSONObject(response)); Log.v(Helper.TAG, "response: " + response); ecdh.saveServerKey(context, account, pushSubscription.getServer_key()); diff --git a/app/src/main/java/app/fedilab/android/client/HttpsConnection.java b/app/src/main/java/app/fedilab/android/client/HttpsConnection.java index 0b71fc085..a82e76cd3 100644 --- a/app/src/main/java/app/fedilab/android/client/HttpsConnection.java +++ b/app/src/main/java/app/fedilab/android/client/HttpsConnection.java @@ -19,6 +19,7 @@ import android.content.SharedPreferences; import android.os.Build; import android.text.Html; import android.text.SpannableString; +import android.util.Log; import com.google.gson.JsonObject; @@ -368,6 +369,7 @@ public class HttpsConnection { postData.append('='); postData.append(param.getValue()); } + Log.v(Helper.TAG, "postData: " + postData); byte[] postDataBytes = postData.toString().getBytes(StandardCharsets.UTF_8); if (proxy != null) httpURLConnection = (HttpsURLConnection) url.openConnection(proxy); diff --git a/app/src/main/java/app/fedilab/android/helper/ECDH.java b/app/src/main/java/app/fedilab/android/helper/ECDH.java index 784bd3f37..f6917b6b6 100644 --- a/app/src/main/java/app/fedilab/android/helper/ECDH.java +++ b/app/src/main/java/app/fedilab/android/helper/ECDH.java @@ -8,7 +8,15 @@ import android.util.Log; import androidx.preference.PreferenceManager; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.x9.ECNamedCurveTable; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.params.ECNamedDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; +import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -37,14 +45,15 @@ public class ECDH { private static final String TAG = ECDH.class.getSimpleName(); - private static final String PROVIDER = "SC"; + private static final String PROVIDER = org.spongycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME; private static final String KEGEN_ALG = "ECDH"; private static ECDH instance; static { - Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); + // Security.removeProvider("BC"); + Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider()); } private final KeyFactory kf; @@ -68,8 +77,11 @@ public class ECDH { return instance; } - static String base64Encode(byte[] b) { - return Base64.encodeToString(b, Base64.DEFAULT); + public static String base64Encode(byte[] b) { + + byte[] encoded = Base64.encode( + b, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); + return new String(encoded); } static byte[] base64Decode(String str) { @@ -78,7 +90,7 @@ public class ECDH { synchronized KeyPair generateKeyPair() throws Exception { - ECGenParameterSpec ecParamSpec = new ECGenParameterSpec("secp256k1"); + ECGenParameterSpec ecParamSpec = new ECGenParameterSpec("prime256v1"); kpg.initialize(ecParamSpec); return kpg.generateKeyPair(); @@ -119,17 +131,29 @@ public class ECDH { KeyPair newPair(Context context) { SharedPreferences.Editor prefsEditor = PreferenceManager .getDefaultSharedPreferences(context).edit(); - - KeyPair kp = null; - + KeyPair kp; try { kp = generateKeyPair(); } catch (Exception e) { e.printStackTrace(); return null; } + ECPublicKey key = (ECPublicKey) kp.getPublic(); + byte[] x = key.getW().getAffineX().toByteArray(); + byte[] y = key.getW().getAffineY().toByteArray(); + BigInteger xbi = new BigInteger(1, x); + BigInteger ybi = new BigInteger(1, y); + X9ECParameters x9 = ECNamedCurveTable.getByName("prime256v1"); + ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID("prime256v1"); + ECCurve curve = x9.getCurve(); + ECPoint point = curve.createPoint(xbi, ybi); + ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid, + x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed()); + ECPublicKeyParameters pubKey = new ECPublicKeyParameters(point, dParams); - prefsEditor.putString(kp_public, base64Encode(kp.getPublic().getEncoded())); + String keyString = base64Encode(pubKey.getQ().getEncoded(false)); + + prefsEditor.putString(kp_public, keyString); prefsEditor.putString(kp_private, base64Encode(kp.getPrivate().getEncoded())); prefsEditor.commit(); return kp; @@ -142,7 +166,7 @@ public class ECDH { String strPub = prefs.getString(kp_public, ""); String strPriv = prefs.getString(kp_private, ""); - if (strPub.isEmpty() || strPriv.isEmpty()) { + if (strPub.isEmpty() || strPriv.isEmpty() || 1 == 1) { return newPair(context); } try { @@ -154,7 +178,11 @@ public class ECDH { } public String getPublicKey(Context context) { - return base64Encode(getPair(context).getPublic().getEncoded()); + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + + String strPub = prefs.getString(kp_public, ""); + return strPub; } public void saveServerKey(Context context, Account account, String strPeerPublic) {