184 lines
5.7 KiB
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;
|
|
}
|
|
}
|
|
}
|