import org.libsodium.jni.NaCl
import org.libsodium.jni.SodiumException
import org.libsodium.jni.crypto SecretBox
import org.libsodium.jni.keys.KeyPair
import java.util.*
fun generateCurve25519KeyPair(): Pair<ByteArray, ByteArray> {
// 初始化 libsodium 库
NaCl.init()
// 创建一个新的KeyPair实例
val keyPair = KeyPair()
// 生成Curve25519密钥对
try {
keyPair.generateKeyPair()
} catch (e: SodiumException) {
throw RuntimeException("Failed to generate Curve25519 key pair.", e)
}
// 提取公钥和私钥
val publicKey = keyPair.publicKey
val privateKey = keyPair.secretKey
// 返回密钥对
return Pair(publicKey.copyOf(), privateKey.copyOf())
}
// 使用函数生成密钥对
val (publicKey, privateKey) = generateCurve25519KeyPair()
// 打印密钥(实际操作中应妥善保存,不要直接打印)
println("Public Key: ${publicKey.toHexString()}")
println("Private Key: ${privateKey.toHexString()}")
// 辅助函数,将字节数组转为十六进制字符串
fun ByteArray.toHexString() = joinToString(separator = "") { "%02x".format(it) }
import android.util.Base64;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
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.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyAgreement;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class ECDH {
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
public static KeyPair generateECDH() {
try {
ECGenParameterSpec ecParamSpec = new ECGenParameterSpec("secp256k1");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "SC");
keyPairGenerator.initialize(ecParamSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
| NoSuchProviderException e) {
e.printStackTrace();
return null;
}
}
public static String getPublicKey(KeyPair keyPair) {
return Base64.encodeToString(keyPair.getPublic().getEncoded(), Base64.DEFAULT).replaceAll("\n", "");
}
public static PublicKey stringToPublicKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
byte[] keyBytes = Base64.decode(key.getBytes("utf-8"), Base64.DEFAULT);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("ECDH", "SC");
PublicKey publicKey = keyFactory.generatePublic(spec);
return publicKey;
}
public static SecretKey computeECDHSecret(PrivateKey privateKey, String publicKey) {
try {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "SC");
keyAgreement.init(privateKey);
keyAgreement.doPhase(stringToPublicKey(publicKey), true);
SecretKey key = keyAgreement.generateSecret("AES");
return key;
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchProviderException | UnsupportedEncodingException
| InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
}
public static String encryptAuthIV(SecretKey key, String plainText) {
try {
byte[] iv = new SecureRandom().generateSeed(16);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SC");
byte[] plainTextBytes = plainText.getBytes("UTF-8");
byte[] cipherText;
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
cipherText = new byte[cipher.getOutputSize(plainTextBytes.length)];
int encryptLength = cipher.update(plainTextBytes, 0,
plainTextBytes.length, cipherText, 0);
encryptLength += cipher.doFinal(cipherText, encryptLength);
byte[] concatenatedBytes = new byte[iv.length + cipherText.length];
System.arraycopy(iv, 0, concatenatedBytes, 0, iv.length);
System.arraycopy(cipherText, 0, concatenatedBytes, iv.length, cipherText.length);
return Base64.encodeToString(concatenatedBytes, Base64.DEFAULT).replaceAll("\n", "");
} catch (NoSuchAlgorithmException | NoSuchProviderException
| NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException
| UnsupportedEncodingException | ShortBufferException
| IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
return null;
}
}
public static String decryptAuthIV(SecretKey key, String cipherText) {
try {
Key decryptionKey = new SecretKeySpec(key.getEncoded(), key.getAlgorithm());
byte[] iv = new byte[16];
byte[] cipherBytes = Base64.decode(cipherText.getBytes("utf-8"), Base64.DEFAULT);
System.arraycopy(cipherBytes, 0, iv, 0, iv.length);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SC");
byte[] plainText;
byte[] cipherTextBytes = new byte[cipherBytes.length - iv.length];
System.arraycopy(cipherBytes, iv.length, cipherTextBytes, 0, cipherBytes.length - iv.length);
cipher.init(Cipher.DECRYPT_MODE, decryptionKey, ivSpec);
plainText = new byte[cipher.getOutputSize(cipherTextBytes.length)];
int decryptLength = cipher.update(cipherTextBytes, 0,
cipherTextBytes.length, plainText, 0);
decryptLength += cipher.doFinal(plainText, decryptLength);
return new String(plainText, "UTF-8");
} catch (NoSuchAlgorithmException | NoSuchProviderException
| NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException | BadPaddingException
| ShortBufferException | UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
public static String bytesToHex(byte[] data, int length) {
String digits = "0123456789ABCDEF";
StringBuffer buffer = new StringBuffer();
for (int i = 0; i != length; i++) {
int v = data[i] & 0xff;
buffer.append(digits.charAt(v >> 4));
buffer.append(digits.charAt(v & 0xf));
}
return buffer.toString();
}
public static String bytesToHex(byte[] data) {
return bytesToHex(data, data.length);
}
public static byte[] hexToBytes(String string) {
int length = string.length();
byte[] data = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
data[i / 2] = (byte) ((Character.digit(string.charAt(i), 16) << 4) + Character
.digit(string.charAt(i + 1), 16));
}
return data;
}
}
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.ecdhsample.ui.theme.ECDHSampleTheme
import com.google.crypto.tink.aead.AeadConfig
import com.google.crypto.tink.subtle.AesGcmJce
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PrivateKey
import java.security.PublicKey
import javax.crypto.KeyAgreement
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ECDHSampleTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
}
val aliceKpg: KeyPairGenerator = KeyPairGenerator.getInstance("EC")
aliceKpg.initialize(256) // Key size in bits
val aliceKeyPair: KeyPair = aliceKpg.generateKeyPair()
// Generate key pair for Bob
val bobKpg: KeyPairGenerator = KeyPairGenerator.getInstance("EC")
bobKpg.initialize(256) // Key size in bits
val bobKeyPair: KeyPair = bobKpg.generateKeyPair()
// Alice's private key and Bob's public key
val alicePrivateKey: PrivateKey = aliceKeyPair.private
val bobPublicKey: PublicKey = bobKeyPair.public
// Alice performs the key agreement
// Alice performs the key agreement
val aliceKeyAgreement: KeyAgreement = KeyAgreement.getInstance("ECDH")
aliceKeyAgreement.init(alicePrivateKey)
aliceKeyAgreement.doPhase(bobPublicKey, true)
// Generate shared secret
val sharedSecretAlice: ByteArray = aliceKeyAgreement.generateSecret()
// Bob performs the key agreement
val bobPrivateKey: PrivateKey = bobKeyPair.private
val alicePublicKey: PublicKey = aliceKeyPair.public
val bobKeyAgreement: KeyAgreement = KeyAgreement.getInstance("ECDH")
bobKeyAgreement.init(bobPrivateKey)
bobKeyAgreement.doPhase(alicePublicKey, true)
// Generate shared secret
val sharedSecretBob: ByteArray = bobKeyAgreement.generateSecret()
// Compare the shared secrets
Log.d("Alice's shared secret: ", byteArrayToHexString(sharedSecretAlice)!!)
Log.d("Bob's shared secret: ", byteArrayToHexString(sharedSecretBob)!!)
AeadConfig.register()
// 1. Divide by two the shared secret to have a 128 bit key
val keyset128 = sharedSecretBob.take(16)
val aead = AesGcmJce(keyset128.toTypedArray().toByteArray())
// 3. Use the primitive to encrypt a plaintext
val plainText = "This is a random message"
val ciphertext = aead.encrypt(plainText.toByteArray(), null)
Log.d("encrypted message = ", String(ciphertext))
// ... or to decrypt a ciphertext.
val decrypted = aead.decrypt(ciphertext, null)
Log.d("decrypted message = ", String(decrypted))
}
private fun byteArrayToHexString(array: ByteArray): String? {
val sb = StringBuilder()
for (b in array) {
sb.append(String.format("%02X", b))
}
return sb.toString()
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ECDHSampleTheme {
1. **依赖项添加**:
在你的Android项目的`build.gradle(Module: app)`文件中添加Tink库依赖:
```groovy
dependencies {
implementation 'com.google.crypto.tink:tink-android:1.6.1'
}
```kotlin
import com.google.crypto.tink.subtle.EcdhKem
import com.google.crypto.tink.subtle.EllipticCurves
import java.security.KeyPairGenerator
import java.security.spec.ECGenParameterSpec
import javax.crypto.SecretKey
// 生成客户端ECDH密钥对
val generator: KeyPairGenerator = KeyPairGenerator.getInstance("EC")
val ecSpec: ECGenParameterSpec = ECGenParameterSpec("secp256r1") // 或者其他曲线名称
generator.initialize(ecSpec)
val keyPair = generator.generateKeyPair()
val clientPrivateKey = keyPair.private as java.security.interfaces.ECPrivateKey
// 假设后端公钥已经以某种方式获取并且解析成了ECPublicKey类型
val backendPublicKey: java.security.interfaces.ECPublicKey = ... // 解析过程
// 使用Tink计算共享密钥
val tinkPublicKey = EllipticCurves.convertToPublicKey(backendPublicKey)
val kem = EcdhKem.newInstance(clientPrivateKey.parameters)
val sharedSecretBytes: ByteArray = kem.deriveKey(tinkPublicKey)
// sharedSecretBytes现在包含的是双方共享的密钥,可以进一步处理成AES密钥等
val sharedSecretKey = SecretKeySpec(sharedSecretBytes, "AES") //