whyoleg / cryptography-kotlin

Kotlin Multiplatform cryptography / crypto library
https://whyoleg.github.io/cryptography-kotlin/
Apache License 2.0
343 stars 20 forks source link

Can I set my PublicKey in "provider.get(RSA.PSS)" ? #23

Closed thanhhoai162963 closed 8 months ago

thanhhoai162963 commented 8 months ago

val ecdsa = provider.get(ECDSA) val keyPairGenerator = ecdsa.keyPairGenerator(EC.Curve.P521) val keyPair: ECDSA.KeyPair = keyPairGenerator.generateKey()

I wasn't found setPublicKey()

whyoleg commented 8 months ago

Hey, Im not sure what do you mean by setPublicKey. What use case do you have? What are you trying to do?

thanhhoai162963 commented 8 months ago

I use asymmetric encryption which connect resful api. I get rsaPublicKey from api server (not need encryption). After I need set publickKey with rsaPublicKey and key random from local to encrypt(k) call api.

Example: fun encryptData(dataDes: String): String? { return try { val c = Cipher.getInstance(Algorithm.RSA_OAEP) c.init(Cipher.ENCRYPT_MODE, Key.rsaPublicKey()) // rsaPublicKey I get from api val encryptOut = c.doFinal(dataDes.toByteArray()) // dataDes random char() org.bouncycastle.util.encoders.Base64.toBase64String(encryptOut) // result "k" to call api asymmetric encryption } catch (e: Exception) {

    }
}
whyoleg commented 8 months ago

As far as I understand you need to decode key from binary representation and then use it. In this case you will need something like: ECDSA:

val ecdsa = provider.get(ECDSA)
val publicKey = ecdsa.publicKeyDecoder(EC.Curve.P521).decodeFrom(EC.PublicKey.Format.RAW, keyByteArray) // or other format
// and then do anything with key verify or encrypt

Similar for RSA-OAEP:

val rsa = provider.get(RSA.OAEP)
val publicKey = rsa.publicKeyDecoder(SHA256).decodeFrom(RSA.PublicKey.Format.DER) // or other format
// and then do anything with key verify or encrypt

Does it helps?

thanhhoai162963 commented 8 months ago

val publicKey = rsa.publicKeyDecoder(SHA256).decodeFrom(RSA.PublicKey.Format.DER, myKey) //Follow I understand, Can I set publicKey = "myKey" here?

I mean i can init val rsa = provider.get(RSA.OAEP) with myKey, after I encrypt data combine (RSA.OAEP)contain mykey as

c.init(Cipher.ENCRYPT_MODE, myKey) // rsaPublicKey I get from api val encryptOut = c.doFinal(data) // dataDes random char()

whyoleg commented 8 months ago

Follow I understand, Can I set publicKey = "myKey" here?

Yeah, though, myKey should be a ByteArray, not a String. DER format is the same format which is used in JDK APIs by default for RSA keys.

thanhhoai162963 commented 8 months ago
 fun encryptData(dataRandom: String): String? {
        return try {
            val c = Cipher.getInstance("RSA/ECB/OAEPPadding")
            c.init(Cipher.ENCRYPT_MODE, loadPublicKey())
            val encryptOut = c.doFinal(dataRandom.toByteArray())
            org.bouncycastle.util.encoders.Base64.toBase64String(encryptOut)
        } catch (e: Exception) {
            Log.d("error:", "error")
            ""
        }
    }

      @Throws(GeneralSecurityException::class, IOException::class)
    private fun loadPublicKey(): PublicKey? {
        val rsaPublicKey = runBlocking {
            LizAiDataStore.instance.getRsaPublicKey()?.publicKey
        }
        val data = Base64.decode(rsaPublicKey)
        val spec = X509EncodedKeySpec(data)
        val fact = KeyFactory.getInstance(Algorithm.RSA)
        return fact.generatePublic(spec)
    }
 Thanks you very much recommend you @whyoleg  , after 3 days thinking, learn about
from website, ,convery swift xcode - to kotlin mutiilplatform, I still can't convert this code. Looking forward to your suggestions
whyoleg commented 8 months ago

Looks like I really need to create a guide for JDK APIs to cryptography-kotlin conversion, as you are not the only one who is struggling with it :)

Here is a one way to do this:

fun encryptData(data: String): String? {
  val key = loadPublicKey() ?: return null
  val encryptOut = key.encryptor().encrypt(data.encodeToByteArray())
  return Base64.encode(encryptOut)
}

private fun loadPublicKey(): RSA.OAEP.PublicKey? {
  val rsaPublicKey = runBlocking { LizAiDataStore.instance.getRsaPublicKey()?.publicKey } ?: return null
  val encodedKey: ByteArray = Base64.decode(rsaPublicKey)
  return CryptographyProvider.Default.get(RSA.OAEP)
               .publicKeyDecoder(SHA256)
               .decode(RSA.PublicKey.Format.DER, encodedKey)
}

Notes:

thanhhoai162963 commented 8 months ago

Thank you @whyoleg. I generated encryptOut. But my server not decrypt . My server use dot Net rsa oeap follow: https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsacryptoserviceprovider?view=netcore-3.1 keySize: 2048

whyoleg commented 8 months ago

Looks like .NET is using RSA OAEP with SHA1 - hint on SO: https://stackoverflow.com/a/67793433 So you need to try publicKeyDecoder(SHA1) instead

thanhhoai162963 commented 8 months ago

Thanks you very much @whyoleg , it working. Special thanks. And Can I use triple des algorithm as android ?

mySecretKeyFactory = SecretKeyFactory.getInstance("DESede") cipher = Cipher.getInstance("DESede") key = mySecretKeyFactory?.generateSecret("DESede")

whyoleg commented 8 months ago

Cool! I'm closing an issue! JDK to cryptography-kotlin guide will be tracked in #24