Пакетное шифрование



Представлен пример пакетного шифрования данных.
Во-первых, напишем сам класс шифрования, RSAencryption:

class RSAencryption(context: Context) {

    companion object {
        private const val ALIAS = "yourCompany"
        private const val KEYSTORE_TYPE = "AndroidKeyStore"
        private const val ALGORITHM = "RSA"
        private const val BLOCK_MODE = "ECB"
        private const val PADDING = "PKCS1Padding"
        const val KEY_SIZE = 512
        const val PADDING_OFFSET = 11
    }

    private val keyStore: KeyStore
    private val context: Context

    init {
        this.context = context
        keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
        keyStore.load(null)
    }

    @Throws(KeyStoreException::class)
    fun isExistsKeyPair(): Boolean {
        return keyStore.containsAlias(ALIAS)
    }

    @Throws(KeyStoreException::class)
    fun deleteEntry(): Boolean {
        if (!isExistsKeyPair()) return false
        keyStore.deleteEntry(ALIAS)
        return true
    }

    @Throws(KeyStoreException::class)
    fun getExpiredDate(): Date? {
        if (!isExistsKeyPair()) return null
        val privateKeyEntry = keyStore.getEntry(ALIAS, null) as KeyStore.PrivateKeyEntry
        val cerificate = privateKeyEntry.certificate as X509Certificate
        return cerificate.notAfter
    }

    fun generateKeyPair(): Single {
        val notBefore = Calendar.getInstance()
        val notAfter = Calendar.getInstance()
        notAfter.add(Calendar.YEAR, 1)

        return Single.create() {
            try {
                val generator: KeyPairGenerator = KeyPairGenerator.getInstance(
                    ALGORITHM,
                    KEYSTORE_TYPE
                )

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                    val spec = KeyGenParameterSpec.Builder(
                        ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
                    )
                        .setCertificateSubject(X500Principal("CN=" + ALIAS))
                        .setDigests(KeyProperties.DIGEST_SHA256)
                        .setCertificateSerialNumber(BigInteger.TEN)
                        .setCertificateNotBefore(notBefore.getTime())
                        .setCertificateNotAfter(notAfter.getTime())
                        .setKeySize(KEY_SIZE)
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                        .build()
                    generator.initialize(spec)
                } else {
                    @Suppress("DEPRECATION")
                    @SuppressLint("WrongConstant")
                    val spec = KeyPairGeneratorSpec.Builder(context)
                        .setAlias(ALIAS)
                        .setKeyType(ALGORITHM)
                        .setKeySize(KEY_SIZE)
                        .setSubject(X500Principal("CN=" + ALIAS))
                        .setSerialNumber(BigInteger.TEN)
                        .setStartDate(notBefore.getTime())
                        .setEndDate(notAfter.getTime())
                        .build()
                    generator.initialize(spec)
                }
                val keyPair = generator.generateKeyPair()
                it.onSuccess(keyPair)
            } catch (e: NoSuchAlgorithmException) {
                it.onError(e)
            } catch (e: InvalidAlgorithmParameterException) {
                it.onError(e)
            } catch (e: Exception) {
                it.onError(e)
            }
        }
    }

    fun encrypt(inBytes: ByteArray): ByteArray {
        if (inBytes.isEmpty()) return ByteArray(0)

        val privateKeyEntry = keyStore.getEntry(ALIAS, null) as KeyStore.PrivateKeyEntry
        val cerificate = privateKeyEntry.certificate
        val publicKey = cerificate.publicKey
        val inCipher = Cipher.getInstance("$ALGORITHM/$BLOCK_MODE/$PADDING")
        inCipher.init(Cipher.ENCRYPT_MODE, publicKey)
        val encodedBytes = inCipher.doFinal(inBytes)
        return encodedBytes
    }

    fun decrypt(encodedBytes: ByteArray): ByteArray {
        if (encodedBytes.isEmpty()) return ByteArray(0)

        val privateKeyEntry = keyStore.getEntry(ALIAS, null) as KeyStore.PrivateKeyEntry
        val privateKey =  privateKeyEntry.privateKey
        val outCipher = Cipher.getInstance("$ALGORITHM/$BLOCK_MODE/$PADDING")
        outCipher.init(Cipher.DECRYPT_MODE, privateKey)
        val decodedBytes = outCipher.doFinal(encodedBytes)
        return decodedBytes
    }
}

Далее создадим класс с исходным текстом для шифрования, MainModel:

object MainModel {
    const val plainText = "Сегодня утром на Брикстон-Роуд, между\n" +
            "трактиром \"Белый олень\" и Холленд-Грув,\n" +
            "найдено золотое кольцо. Обращаться к\n" +
            "доктору Уотсону, Бейкер-Стрит, 221-Б, от\n" +
            "восьми до десяти вечера."
}

Потом напишем класс обработки ViewModel:

class MainViewModel
    : ViewModel() {

    private var disposables: CompositeDisposable
    var plainText: MutableLiveData
    var encodingText: MutableLiveData
    var decodingText: MutableLiveData
    private lateinit var rsaEncryption: RSAencryption

    init {
        disposables = CompositeDisposable()
        plainText = MutableLiveData()
        encodingText = MutableLiveData()
        decodingText = MutableLiveData()
    }

    fun afterInit(appContext: Context) {
        plainText.value = MainModel.plainText

        rsaEncryption = RSAencryption(appContext)

        if (!rsaEncryption.isExistsKeyPair()) {
            rsaEncryption.deleteEntry()

            disposables.add(
                rsaEncryption.generateKeyPair()
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe()
            )
        }
    }

    fun onEncodeClick(s: String) {
        if (!rsaEncryption.isExistsKeyPair()) return
        if (s.isNotEmpty()) {
            val blockSize = RSAencryption.KEY_SIZE / 8 - RSAencryption.PADDING_OFFSET
            val list = splitByteArray(s.toByteArray(), blockSize)

            val output = ByteArrayOutputStream()
            for (item in list) {
                val enc = rsaEncryption.encrypt(item)
                output.write(enc)
            }
            encodingText.value = Base64.encodeToString(output.toByteArray(), Base64.NO_WRAP)
            decodingText.value = ""
            output.close()
        }
    }

    fun onDecodeClick(s: String) {
        if (!rsaEncryption.isExistsKeyPair()) return

        if (s.isNotEmpty()) {
            val blockSize = RSAencryption.KEY_SIZE / 8
            val list = splitByteArray(Base64.decode(s, Base64.NO_WRAP) , blockSize)
            val output = ByteArrayOutputStream()
            for (item in list) {
                val dec = rsaEncryption.decrypt(item)
                output.write(dec)
            }
            decodingText.value = String(output.toByteArray())
            output.close()
        }
    }

    private fun splitByteArray(array: ByteArray, blockSize: Int): MutableList {
        val result = mutableListOf()

        var offset = 0
        while (offset < array.size) {
            val a = Arrays.copyOfRange(array, offset, Math.min(offset + blockSize, array.size))
            result.add(a)
            offset += blockSize
        }

        return result
    }

    override fun onCleared() {
        super.onCleared()
        disposables.clear()
    }
}

Ну, а теперь, сама Activity:

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initViewModel()
    }

    private fun initViewModel() {
        viewModel = ViewModelProviders.of(this@MainActivity).get(MainViewModel::class.java)

        viewModel.plainText.observe(this@MainActivity, Observer { s->
            et_plain_text.setText(s)
        })

        viewModel.encodingText.observe(this@MainActivity, Observer { s->
            tv_encoding_text.setText(s)
        })

        viewModel.decodingText.observe(this@MainActivity, Observer { s->
            tv_decoding_text.setText(s)
        })

        btn_encode.setOnClickListener { viewModel.onEncodeClick(et_plain_text.text.toString()) }

        btn_decode.setOnClickListener { viewModel.onDecodeClick(tv_encoding_text.text.toString()) }

        viewModel.afterInit(this@MainActivity.applicationContext)
    }
}


    Запускаем программу и получаем:


   






























    Автор статьи: Дмитрий Изергин