kotlin-android-extensions 已被弃用,如何使用 @Parcelize?

第 1 步。更新到最新的 kotlin 版本 -1.4.20并替换

apply plugin: 'kotlin-android-extensions'

to

apply plugin: 'kotlin-parcelize'

或者

plugins {
    ..
    id 'kotlin-parcelize'
}

第 2 步。从 android {} 中删除以下代码

androidExtensions {
    experimental = true
}

第 3 步。最后,替换旧的 import ->

import kotlinx.android.parcel.Parcelize

to

import kotlinx.parcelize.Parcelize

新插件:https : //plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.parcelize

WebView监听HTML的事件

添加addJavascriptInterface

val ws: WebSettings = wv.getSettings()
ws.javaScriptEnabled = true
wv.addJavascriptInterface(object : Any() {
      @JavascriptInterface 
      fun performClick(strl: String) {
           Toast.makeText([email protected], stringVariable, Toast.LENGTH_SHORT).show()
      }
}, "ok")

在onclick中添加对应方法,这里的"ok"是addJavascriptInterface的name参数

<button type="button" onclick="ok.performClick();">OK</button>

Android加密算法之RSA(二)

The RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.

使用RSA时,数据过长会报错,如:

javax.crypto.IllegalBlockSizeException: input must be under 256 bytes

因此,基本上,您将密钥长度除以8 -11(如果有填充)。例如,如果您具有2048位密钥,则可以加密2048/8 = 256字节(如果有填充则为11字节)。因此,可以使用更大的密钥,也可以使用对称密钥加密数据,然后使用rsa加密该密钥(推荐的方法)
参考:https://stackoverflow.com/questions/10007147/getting-a-illegalblocksizeexception-data-must-not-be-longer-than-256-bytes-when

通过分段加密解决

1.生成公钥私钥

@Throws(Exception::class)
    fun genKeyPair(): Map<String?, String> {
        val keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM)
        keyPairGen.initialize(2048)
        val keyPair = keyPairGen.generateKeyPair()
        val publicKey = keyPair.public as RSAPublicKey
        val privateKey = keyPair.private as RSAPrivateKey
        val keyMap: MutableMap<String?, String> = HashMap(2)
        keyMap[PUBLIC_KEY] = Base64.encodeToString(publicKey.encoded, Base64.DEFAULT)
        keyMap[PRIVATE_KEY] = Base64.encodeToString(privateKey.encoded, Base64.DEFAULT)
        return keyMap
    }

2.公钥加密

    @Throws(Exception::class)
    fun encryptByPublicKey(data: ByteArray, publicKey: String?): ByteArray {
        val keyBytes: ByteArray = Base64.decode(publicKey, Base64.DEFAULT)
        val x509KeySpec = X509EncodedKeySpec(keyBytes)
        val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM)
        val publicK: Key = keyFactory.generatePublic(x509KeySpec)
        // 对数据加密
        val cipher = Cipher.getInstance(keyFactory.algorithm)
        cipher.init(Cipher.ENCRYPT_MODE, publicK)
        val inputLen = data.size
        val out = ByteArrayOutputStream()
        var offSet = 0
        var cache: ByteArray
        var i = 0
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            cache = if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK)
            } else {
                cipher.doFinal(data, offSet, inputLen - offSet)
            }
            out.write(cache, 0, cache.size)
            i++
            offSet = i * MAX_ENCRYPT_BLOCK
        }
        val encryptedData = out.toByteArray()
        out.close()
        return encryptedData
    }

3.私钥解密

    @Throws(Exception::class)
    fun decryptByPrivateKey(encryptedData: ByteArray, privateKey: String?): ByteArray {
        val keyBytes: ByteArray = Base64.decode(privateKey, Base64.DEFAULT)
        val pkcs8KeySpec = PKCS8EncodedKeySpec(keyBytes)
        val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM)
        val privateK: Key = keyFactory.generatePrivate(pkcs8KeySpec)
        val cipher = Cipher.getInstance(keyFactory.algorithm)
        cipher.init(Cipher.DECRYPT_MODE, privateK)
        val inputLen = encryptedData.size
        val out = ByteArrayOutputStream()
        var offSet = 0
        var cache: ByteArray
        var i = 0
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            cache = if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK)
            } else {
                cipher.doFinal(encryptedData, offSet, inputLen - offSet)
            }
            out.write(cache, 0, cache.size)
            i++
            offSet = i * MAX_DECRYPT_BLOCK
        }
        val decryptedData = out.toByteArray()
        out.close()
        return decryptedData
    }

4.调用方法:

        val keyMap: Map<String?, String?> = RSACrypt.genKeyPair()
        val publicKey = keyMap[RSACrypt.PUBLIC_KEY]
        val privateKey = keyMap[RSACrypt.PRIVATE_KEY]
        val source =
            "身高一米七左右,标准的九头身,五官立体宛如艺术大师鬼斧神工之作,肌肤赛雪,宛如白玉般似能反射光彩。" +
            "“我和茹雪两情相悦,但对我们而言,你是第三者,所以请你自己主动退出,我愿意给你一笔钱,你开个价码吧!”啪,一声清脆的响声,干净利落地狠狠扇在韩斌的脸上。" +
            "乔智愿意当陶家的女婿,除了偿还父亲欠下的人情债之外,还有一个重要原因,陶茹雪本身出众,是琼金一枝花,知名度高,追求者众多。"
        val data = source.encodeToByteArray()
        val encodedData: ByteArray = RSACrypt.encryptByPublicKey(data, publicKey)
        Log.w("rsa", "加密后文字:${Base64.encodeToString(encodedData, Base64.DEFAULT)}")
        val decodedData: ByteArray = RSACrypt.decryptByPrivateKey(encodedData, privateKey)
        Log.w("rsa", "解密后文字: \r\n${String(decodedData)}")

完整代码

object RSACrypt {
    /**
     * 加密算法RSA
     */
    const val KEY_ALGORITHM = "RSA"

    /**
     * 签名算法
     */
    const val SIGNATURE_ALGORITHM = "MD5withRSA"

    /**
     * 获取公钥的key
     */
    const val PUBLIC_KEY = "RSAPublicKey"

    /**
     * 获取私钥的key
     */
    const val PRIVATE_KEY = "RSAPrivateKey"

    /**
     * RSA最大加密明文大小
     */
    private const val MAX_ENCRYPT_BLOCK = 245

    /**
     * RSA最大解密密文大小
     */
    private const val MAX_DECRYPT_BLOCK = 256

    /**
     * 生成密钥对(公钥和私钥)
     * @return
     * @throws Exception
     */
    @Throws(Exception::class)
    fun genKeyPair(): Map<String?, String> {
        val keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM)
        keyPairGen.initialize(2048)
        val keyPair = keyPairGen.generateKeyPair()
        val publicKey = keyPair.public as RSAPublicKey
        val privateKey = keyPair.private as RSAPrivateKey
        val keyMap: MutableMap<String?, String> = HashMap(2)
        keyMap[PUBLIC_KEY] = Base64.encodeToString(publicKey.encoded, Base64.DEFAULT)
        keyMap[PRIVATE_KEY] = Base64.encodeToString(privateKey.encoded, Base64.DEFAULT)
        return keyMap
    }

    /**
     * 私钥解密
     *
     * @param encryptedData 已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    @Throws(Exception::class)
    fun decryptByPrivateKey(encryptedData: ByteArray, privateKey: String?): ByteArray {
        val keyBytes: ByteArray = Base64.decode(privateKey, Base64.DEFAULT)
        val pkcs8KeySpec = PKCS8EncodedKeySpec(keyBytes)
        val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM)
        val privateK: Key = keyFactory.generatePrivate(pkcs8KeySpec)
        val cipher = Cipher.getInstance(keyFactory.algorithm)
        cipher.init(Cipher.DECRYPT_MODE, privateK)
        val inputLen = encryptedData.size
        val out = ByteArrayOutputStream()
        var offSet = 0
        var cache: ByteArray
        var i = 0
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            cache = if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK)
            } else {
                cipher.doFinal(encryptedData, offSet, inputLen - offSet)
            }
            out.write(cache, 0, cache.size)
            i++
            offSet = i * MAX_DECRYPT_BLOCK
        }
        val decryptedData = out.toByteArray()
        out.close()
        return decryptedData
    }

    /**
     *
     * 公钥加密
     *
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    @Throws(Exception::class)
    fun encryptByPublicKey(data: ByteArray, publicKey: String?): ByteArray {
        val keyBytes: ByteArray = Base64.decode(publicKey, Base64.DEFAULT)
        val x509KeySpec = X509EncodedKeySpec(keyBytes)
        val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM)
        val publicK: Key = keyFactory.generatePublic(x509KeySpec)
        // 对数据加密
        val cipher = Cipher.getInstance(keyFactory.algorithm)
        cipher.init(Cipher.ENCRYPT_MODE, publicK)
        val inputLen = data.size
        val out = ByteArrayOutputStream()
        var offSet = 0
        var cache: ByteArray
        var i = 0
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            cache = if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK)
            } else {
                cipher.doFinal(data, offSet, inputLen - offSet)
            }
            out.write(cache, 0, cache.size)
            i++
            offSet = i * MAX_ENCRYPT_BLOCK
        }
        val encryptedData = out.toByteArray()
        out.close()
        return encryptedData
    }
}

Android加密算法之AES

什么是高级加密标准或AES?

AES加密标准又称为高级加密标准Rijndael加密法,是美国国家标准技术研究所NIST旨在取代DES的21世纪的加密标准。

AES加密如何工作?

AES是一种对称的加密算法,可基于相同的密钥进行加密和解密。AES包含三个分组密码:AES-128,AES-192和AES-256。每个密码分别使用128 位,192位和256位的加密密钥对128 位块中的数据进行加密和解密。代码:

object AESCrypt {
    private const val AES_MODE = "AES/CBC/PKCS7Padding" //"算法/模式/补码方式"
    private const val CHARSET = "UTF-8"
    private const val CIPHER = "AES"
    private const val HASH_ALGORITHM = "SHA-256"
    private val IV_BYTES = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)

    /**
     * Generates SHA256 hash of the password which is used as key
     *
     * @param password used to generated key
     * @return SHA256 of the password
     */
    @Throws(NoSuchAlgorithmException::class, UnsupportedEncodingException::class)
    private fun generateKey(password: String): SecretKeySpec {
        val digest = MessageDigest.getInstance(HASH_ALGORITHM)
        val bytes = password.toByteArray(charset(CHARSET))
        digest.update(bytes, 0, bytes.size)
        val key = digest.digest()

        return SecretKeySpec(key, CIPHER)
    }


    /**
     * Encrypt and encode message using 256-bit AES with key generated from password.
     *
     * @param password used to generated key
     * @param message  the thing you want to encrypt assumed String UTF-8
     * @return Base64 encoded CipherText
     * @throws GeneralSecurityException if problems occur during encryption
     */
    @Throws(GeneralSecurityException::class)
    fun encrypt(password: String, message: String): String {
        try {
            val key = generateKey(password)
            val cipherText = encrypt(key, IV_BYTES, message.toByteArray(charset(CHARSET)))
            //NO_WRAP is important as was getting \n at the end
            return Base64.encodeToString(cipherText, Base64.NO_WRAP)
        } catch (e: UnsupportedEncodingException) {
            throw GeneralSecurityException(e)
        }

    }


    /**
     * More flexible AES encrypt that doesn't encode
     *
     * @param key     AES key typically 128, 192 or 256 bit
     * @param iv      Initiation Vector
     * @param message in bytes (assumed it's already been decoded)
     * @return Encrypted cipher text (not encoded)
     * @throws GeneralSecurityException if something goes wrong during encryption
     */
    @Throws(GeneralSecurityException::class)
    fun encrypt(key: SecretKeySpec, iv: ByteArray, message: ByteArray): ByteArray {
        val cipher = Cipher.getInstance(AES_MODE)
        val ivSpec = IvParameterSpec(iv)
        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec)
        return cipher.doFinal(message)
    }


    /**
     * Decrypt and decode ciphertext using 256-bit AES with key generated from password
     *
     * @param password                used to generated key
     * @param base64EncodedCipherText the encrpyted message encoded with base64
     * @return message in Plain text (String UTF-8)
     * @throws GeneralSecurityException if there's an issue decrypting
     */
    @Throws(GeneralSecurityException::class)
    fun decrypt(password: String, base64EncodedCipherText: String): String {

        try {
            val key = generateKey(password)
            val decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.NO_WRAP)
            val decryptedBytes = decrypt(key, IV_BYTES, decodedCipherText)
            return String(decryptedBytes, charset(CHARSET))
        } catch (e: UnsupportedEncodingException) {
            throw GeneralSecurityException(e)
        }

    }

    /**
     * More flexible AES decrypt that doesn't encode
     *
     * @param key               AES key typically 128, 192 or 256 bit
     * @param iv                Initiation Vector
     * @param decodedCipherText in bytes (assumed it's already been decoded)
     * @return Decrypted message cipher text (not encoded)
     * @throws GeneralSecurityException if something goes wrong during encryption
     */
    @Throws(GeneralSecurityException::class)
    fun decrypt(key: SecretKeySpec, iv: ByteArray, decodedCipherText: ByteArray): ByteArray {
        val cipher = Cipher.getInstance(AES_MODE)
        val ivSpec = IvParameterSpec(iv)
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec)
        return cipher.doFinal(decodedCipherText)
    }
}

添加扩展方法,方便调用

private const val key: String = "Your AES Key"
    fun encrypt(input: String): String {
        return AESCrypt.encrypt(key, input)
    }

    fun decrypt(input: String): String {
        return AESCrypt.decrypt(key, input)
    }

    fun String.aesEncrypt(): String = encrypt(this)
    fun String.aesDecrypt(): String = decrypt(this)

使用方法

"明文".aesEncrypt()
"密文".aesDecrypt()

Android P http网络请求失败

最近更新了android studio,以及到gradle和android sdk版本 ;
手机Pixel 2 xl,系统Android P;
网络请求okhttp3,每次网络请求总是:

java.net.UnknownServiceException:
CLEARTEXT communication ** not permitted by network security policy

官网文档:

Android致力于保护用户,他们的设备和数据安全。我们保证数据安全的方法之一是保护所有进入或离开Android设备的数据在传输中使用传输层安全性(TLS)。正如我们在Android P开发人员预览中所宣布的那样,我们通过阻止针对Android P的应用程序默认允许未加密的连接来进一步改进这些保护。
这是我们多年来为更好地保护Android用户而做出的各种更改。为了防止意外的未加密连接,我们android:usesCleartextTraffic在Android Marshmallow中引入了manifest属性。在Android Nougat中,我们通过创建Network Security Config功能扩展了该属性,该功能允许应用程序指示他们不打算在没有加密的情况下发送网络流量。在Android Nougat和Oreo中,我们仍然允许明文连接。


看来以后都要用https了,在Android P系统的设备上,如果应用使用的是非加密的明文流量的http网络请求,则会导致该应用无法进行网络请求,https则不会受影响,同样地,如果应用嵌套了webview,webview也只能使用https请求。
目前找到的解决办法:
1:改用https
2:targetSdkVersion改为27以下
3:在res的xml目录下,新建一个xml文件(名称自定义,如:network_security_config.xml),
内容如下:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
<application
...
 android:networkSecurityConfig="@xml/network_security_config"
...
    />

更多参考:https://android-developers.googleblog.com/2018/04/protecting-users-with-tls-by-default-in.html

原文:https://www.jianshu.com/p/fb7374b544aa