Mã hóa Kotlin ECC

Nov 11 2020

Có thông tin nào về Mã hóa đường cong Elliptic trong Kotlin không?

Để tạo các cặp khóa và mã hóa, giải mã các thông điệp.

Có rất ít thông tin về chủ đề này.

Tôi muốn triển khai đường cong elliptic ECC P-521 chẳng hạn.

Có thể sử dụng phiên bản Java trong Kotlin không?

Và chúng ta thực hiện điều này như thế nào?

Trả lời

2 Topaco Nov 17 2020 at 23:03

ECC cung cấp ECIES, một sơ đồ mã hóa hỗn hợp kết hợp mã hóa bất đối xứng dựa trên ECC với mã hóa đối xứng. Ở đây, một bí mật được chia sẻ được tạo ra từ đó bắt nguồn một khóa cho mã hóa đối xứng của dữ liệu. MAC được sử dụng để xác thực. ECIES được quy định trong các tiêu chuẩn tiền điện tử khác nhau. Thông tin chi tiết có thể được tìm thấy tại đây .

ECIES sử dụng các thành phần bạn đã liệt kê trong câu hỏi của mình (bí mật được chia sẻ qua ECC, mã hóa đối xứng, MAC để xác thực). Tuy nhiên, các thuật toán cụ thể phụ thuộc vào tiêu chuẩn hoặc cách triển khai được sử dụng, vì vậy bạn không có quyền kiểm soát trực tiếp chúng. Nếu điều này là đủ đối với bạn, ECIES sẽ là một lựa chọn tốt.

ECIES được hỗ trợ, ví dụ như BouncyCastle, thực hiện tiêu chuẩn IEEE P 1363a. Để sử dụng ECIES, trước tiên bạn phải thêm BouncyCastle (ví dụ: đối với Android Studio trong phần phụ thuộc của ứng dụng / gradle), xem thêm tại đây :

implementation 'org.bouncycastle:bcprov-jdk15to18:1.67'

Sau đó, mã Kotlin sau sẽ thực hiện mã hóa / giải mã với ECIES và NIST P-521:

// Add BouncyCastle
Security.removeProvider("BC")
Security.addProvider(BouncyCastleProvider())

// Key Pair Generation
val keyPairGenerator = KeyPairGenerator.getInstance("ECDH")
keyPairGenerator.initialize(ECGenParameterSpec("secp521r1"))
val keyPair = keyPairGenerator.generateKeyPair()

// Encryption
val plaintext = "The quick brown fox jumps over the lazy dog".toByteArray(StandardCharsets.UTF_8)
val cipherEnc = Cipher.getInstance("ECIES")
cipherEnc.init(Cipher.ENCRYPT_MODE, keyPair.public) // In practice, the public key of the recipient side is used
val ciphertext = cipherEnc.doFinal(plaintext)

// Decryption
val cipherDec = Cipher.getInstance("ECIES")
cipherDec.init(Cipher.DECRYPT_MODE, keyPair.private)
val decrypted = cipherDec.doFinal(ciphertext)
println(String(decrypted, StandardCharsets.UTF_8))

được thử nghiệm với API cấp 28 / Android 9 Pie.


Nếu bạn muốn kiểm soát nhiều hơn các thuật toán được sử dụng, các thành phần riêng lẻ có thể được triển khai thủ công, ví dụ:

  • ECDH với NIST P-521 để xác định bí mật được chia sẻ
  • SHA-512 để xác định khóa AES-256 là 32 byte đầu tiên của hàm băm (xem thêm tại đây để sử dụng KDF trong ngữ cảnh của ECIES)
  • AES-256 / GCM để mã hóa đối xứng ( GCM đã là mã hóa được xác thực, vì vậy không cần có MAC rõ ràng)

Sau đó, mã Kotlin sau sẽ thực hiện mã hóa / giải mã với các thành phần sau:

// Generate Keys
val keyPairA = generateKeyPair()
val keyPairB = generateKeyPair()

// Generate shared secrets
val sharedSecretA = getSharedSecret(keyPairA.private, keyPairB.public)
val sharedSecretB = getSharedSecret(keyPairB.private, keyPairA.public)

// Generate AES-keys
val aesKeyA = getAESKey(sharedSecretA)
val aesKeyB = getAESKey(sharedSecretB)

// Encryption (WLOG by A)
val plaintextA = "The quick brown fox jumps over the lazy dog".toByteArray(StandardCharsets.UTF_8)
val ciphertextA = encrypt(aesKeyA, plaintextA)

// Decryption (WLOG by B)
val plaintextB = decrypt(aesKeyB, ciphertextA)
println(String(plaintextB, StandardCharsets.UTF_8))

với:

private fun generateKeyPair(): KeyPair {
    val keyPairGenerator = KeyPairGenerator.getInstance("EC")
    keyPairGenerator.initialize(ECGenParameterSpec("secp521r1"))
    return keyPairGenerator.generateKeyPair()
}

private fun getSharedSecret(privateKey: PrivateKey, publicKey: PublicKey): ByteArray {
    val keyAgreement = KeyAgreement.getInstance("ECDH")
    keyAgreement.init(privateKey)
    keyAgreement.doPhase(publicKey, true)
    return keyAgreement.generateSecret()
}

private fun getAESKey(sharedSecret: ByteArray): ByteArray {
    val digest = MessageDigest.getInstance("SHA-512")
    return digest.digest(sharedSecret).copyOfRange(0, 32)
}

private fun encrypt(aesKey: ByteArray, plaintext: ByteArray): ByteArray {
    val secretKeySpec = SecretKeySpec(aesKey, "AES")
    val iv = ByteArray(12) // Create random IV, 12 bytes for GCM
    SecureRandom().nextBytes(iv)
    val gCMParameterSpec = GCMParameterSpec(128, iv)
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gCMParameterSpec)
    val ciphertext = cipher.doFinal(plaintext)
    val ivCiphertext = ByteArray(iv.size + ciphertext.size) // Concatenate IV and ciphertext (the MAC is implicitly appended to the ciphertext)
    System.arraycopy(iv, 0, ivCiphertext, 0, iv.size)
    System.arraycopy(ciphertext, 0, ivCiphertext, iv.size, ciphertext.size)
    return ivCiphertext
}

private fun decrypt(aesKey: ByteArray, ivCiphertext: ByteArray): ByteArray {
    val secretKeySpec = SecretKeySpec(aesKey, "AES")
    val iv = ivCiphertext.copyOfRange(0, 12) // Separate IV
    val ciphertext = ivCiphertext.copyOfRange(12, ivCiphertext.size) // Separate ciphertext (the MAC is implicitly separated from the ciphertext)
    val gCMParameterSpec = GCMParameterSpec(128, iv)
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gCMParameterSpec)
    return cipher.doFinal(ciphertext)
}

được thử nghiệm lại với API Cấp 28 / Android 9 Pie.