fedilab-Android-App/app/src/main/java/app/fedilab/android/helper/ECDH.java

184 lines
5.7 KiB
Java

package app.fedilab.android.helper;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import android.util.Log;
import androidx.preference.PreferenceManager;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.KeyAgreement;
import app.fedilab.android.client.Entities.Account;
// https://github.com/nelenkov/ecdh-kx/blob/master/src/org/nick/ecdhkx/Crypto.java
public class ECDH {
private static final String kp_public = "kp_public";
private static final String kp_private = "kp_private";
private static final String peer_public = "peer_public";
private static final String TAG = ECDH.class.getSimpleName();
private static final String PROVIDER = "SC";
private static final String KEGEN_ALG = "ECDH";
private static ECDH instance;
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
private final KeyFactory kf;
private final KeyPairGenerator kpg;
public ECDH() {
try {
kf = KeyFactory.getInstance(KEGEN_ALG, PROVIDER);
kpg = KeyPairGenerator.getInstance(KEGEN_ALG, PROVIDER);
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
public static synchronized ECDH getInstance() {
if (instance == null) {
instance = new ECDH();
}
return instance;
}
static String base64Encode(byte[] b) {
return Base64.encodeToString(b, Base64.DEFAULT);
}
static byte[] base64Decode(String str) {
return Base64.decode(str, Base64.DEFAULT);
}
synchronized KeyPair generateKeyPair()
throws Exception {
ECGenParameterSpec ecParamSpec = new ECGenParameterSpec("secp256k1");
kpg.initialize(ecParamSpec);
return kpg.generateKeyPair();
}
byte[] generateSecret(PrivateKey myPrivKey, PublicKey otherPubKey) throws Exception {
ECPublicKey ecPubKey = (ECPublicKey) otherPubKey;
Log.d(TAG, "public key Wx: "
+ ecPubKey.getW().getAffineX().toString(16));
Log.d(TAG, "public key Wy: "
+ ecPubKey.getW().getAffineY().toString(16));
KeyAgreement keyAgreement = KeyAgreement.getInstance(KEGEN_ALG, PROVIDER);
keyAgreement.init(myPrivKey);
keyAgreement.doPhase(otherPubKey, true);
return keyAgreement.generateSecret();
}
synchronized PublicKey readPublicKey(String keyStr) throws Exception {
X509EncodedKeySpec x509ks = new X509EncodedKeySpec(
base64Decode(keyStr));
return kf.generatePublic(x509ks);
}
synchronized PrivateKey readPrivateKey(String keyStr) throws Exception {
PKCS8EncodedKeySpec p8ks = new PKCS8EncodedKeySpec(
base64Decode(keyStr));
return kf.generatePrivate(p8ks);
}
synchronized KeyPair readKeyPair(String pubKeyStr, String privKeyStr)
throws Exception {
return new KeyPair(readPublicKey(pubKeyStr), readPrivateKey(privKeyStr));
}
KeyPair newPair(Context context, Account account) {
SharedPreferences.Editor prefsEditor = PreferenceManager
.getDefaultSharedPreferences(context).edit();
KeyPair kp = null;
try {
kp = generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
return null;
}
prefsEditor.putString(kp_public + account.getId() + account.getInstance(), base64Encode(kp.getPublic().getEncoded()));
prefsEditor.putString(kp_private + account.getId() + account.getInstance(), base64Encode(kp.getPrivate().getEncoded()));
prefsEditor.commit();
return kp;
}
synchronized KeyPair getPair(Context context, Account account) {
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
String strPub = prefs.getString(kp_public + account.getId() + account.getInstance(), "");
String strPriv = prefs.getString(kp_private + account.getId() + account.getInstance(), "");
if (strPub.isEmpty() || strPriv.isEmpty()) {
return newPair(context, account);
}
try {
return readKeyPair(strPub, strPriv);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String getPublicKey(Context context, Account account) {
return base64Encode(getPair(context, account).getPublic().getEncoded());
}
void saveServerKey(Context context, String strPeerPublic) {
SharedPreferences.Editor prefsEditor = PreferenceManager
.getDefaultSharedPreferences(context).edit();
prefsEditor.putString(peer_public, strPeerPublic);
prefsEditor.commit();
}
PublicKey getServerKey(Context context) throws Exception {
return readPublicKey(
PreferenceManager.getDefaultSharedPreferences(context)
.getString(peer_public, "")
);
}
byte[] getSecret(Context context, Account account) {
try {
return generateSecret(getPair(context, account).getPrivate(), getServerKey(context));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}