import decrypt.ArchiveDecrypt
import decrypt.Encrypt
import decrypt.FileArchiveDecrypt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import progress.Progress
import progress.ProgressFileHandler
import java.io.File
import java.util.concurrent.atomic.AtomicReference
import java.util.logging.Logger
import javax.swing.JOptionPane
import kotlin.time.measureTime

object BruteForce {
    private val archiveDecrypt: ArchiveDecrypt = FileArchiveDecrypt
    private val progress: ProgressFileHandler = Progress
    private val passwordFoundFlow = MutableSharedFlow<String>()
    private var passwordFound = AtomicReference<String?>(null)

    /**
     * Tenta descriptografar o arquivo fornecido utilizando a técnica de força bruta.
     *
     * @param file o arquivo a ser descriptografado
     * @return a senha utilizada para descriptografar o arquivo
     * @throws IllegalArgumentException se o arquivo não for criptografado
     */
    suspend fun force(file: File): String {
        if (!Encrypt().isEncrypted(file)) {
            throw IllegalArgumentException("Arquivo não criptografado!")
        }
        progress.loadProgress(file.name)

        val duration = measureTime {
            val consumers = List(Runtime.getRuntime().availableProcessors()) {
                CoroutineScope(Dispatchers.IO).launch { tryPasswords(file) }
            }
            passwordFound = passwordFoundFlow.first()
            consumers.forEach { it.cancel() }
        }
        val passwordsGenerated = progress.passwordIterator.totalPasswordsGenerated.toInt()

        JOptionPane.showMessageDialog(
            null, "\n--Senha encontrada--\n$passwordFound\nTempo de execução: $duration\nSenhas testadas: " + "$passwordsGenerated\nSenhas por segundo: ${
                if (duration.inWholeSeconds > 0) {
                    passwordsGenerated / duration.inWholeSeconds
                } else {
                    passwordsGenerated / duration.inWholeMilliseconds
                }
            }", "Senha encontrada!", JOptionPane.INFORMATION_MESSAGE
        )
        Logger.getLogger(this.javaClass.name).info(
            "\n--Senha encontrada--\n$passwordFound\nTempo de execução: $duration\nSenhas testadas: " + "$passwordsGenerated\nSenhas por segundo: ${
                if (duration.inWholeSeconds > 0) {
                    passwordsGenerated / duration.inWholeSeconds
                } else {
                    passwordsGenerated / duration.inWholeMilliseconds
                }
            }"
        )

        return passwordFound.get()!!.also {
            passwordFound = AtomicReference<String?>(null)
            progress.passwordIterator.reset()
        }
    }

    private suspend fun tryPasswords(archiveFile: File) {
        while (passwordFound.get() == null) {
            if (progress.passwordIterator.totalPasswordsGenerated.toInt() % 5000 == 0) {
                progress.saveProgress(archiveFile.name)
            }
            val result = archiveDecrypt.tryPassword(archiveFile, progress.passwordIterator.getNextPassword())
            if (passwordFound.compareAndSet(null, result) && result?.isNotBlank() == true) {
                passwordFoundFlow.emit(result)
                break
            }
        }
    }
}
package decrypt

import com.github.junrar.Archive
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.lingala.zip4j.ZipFile
import org.apache.commons.compress.archivers.sevenz.SevenZFile
import java.io.File
import java.io.IOException

/**
 * Implementation of [ArchiveDecrypt] that uses the 7-zip library to decrypt 7z files, and the Apache Commons Compress
 * library to decrypt zip files.
 */
object FileArchiveDecrypt : ArchiveDecrypt {
    override suspend fun tryPassword(archiveFile: File, password: String): String? {
        return withContext(Dispatchers.IO) {
            try {
                when (archiveFile.extension) {
                    "7z" -> SevenZFile.builder().setFile(archiveFile).setPassword(password.toCharArray()).get()
                        .use { sevenZFile ->
                            sevenZFile.entries.forEach { sevenEntry ->
                                sevenZFile.getInputStream(sevenEntry).readBytes()
                            }
                        }

                    "zip" -> ZipFile(archiveFile, password.toCharArray()).use { zipFile ->
                        zipFile.fileHeaders.forEach { fileHeader ->
                            zipFile.getInputStream(fileHeader).readBytes()
                        }
                    }

                    "rar" -> Archive(archiveFile, password).use { archive ->
                        archive.fileHeaders.forEach { fileHeader ->
                            archive.getInputStream(fileHeader).readBytes()
                        }
                    }

                    else -> throw IllegalArgumentException(
                        "Extensão de arquivo não suportado!\n${archiveFile.extension}"
                    )
                }
                password
            } catch (_: IOException) {
                null
            }
        }
    }
}
package progress

import password.PasswordIterator
import password.PasswordSequenceIterator
import java.io.File

object Progress : ProgressFileHandler {
    override var passwordIterator: PasswordSequenceIterator = PasswordIterator

    override fun saveProgress(fileName: String) {
        File("${fileName}progress.Progress.txt").writeText(passwordIterator.getCurrent())
    }

    override fun loadProgress(fileName: String) {
        val progressFile = File("${fileName}progress.Progress.txt")
        if (progressFile.exists()) {
            val password = progressFile.readText().trim()
            passwordIterator.setCurrent(password)
        }
    }
}
package password

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.concurrent.atomic.AtomicInteger
import java.util.logging.Logger

object PasswordIterator : PasswordSequenceIterator {
    override val passwordCharacters: List<Char> = ('0'..'9') + ('a'..'z') + ('A'..'Z') + "-_!@#$%^&*(). ".toList()
    override var currentPassword: CharArray = charArrayOf(passwordCharacters[0])
    override var totalPasswordsGenerated: AtomicInteger = AtomicInteger()
    override val mutex: Mutex = Mutex()
    private val charToIndexMap = passwordCharacters.withIndex().associate { it.value to it.index }

    override suspend fun getNextPassword(): String {
        return mutex.withLock {
            next().also { Logger.getLogger(this.javaClass.name).info(it) }
        }
    }

    override fun reset() {
        currentPassword = charArrayOf(passwordCharacters[0])
        totalPasswordsGenerated.set(0)
    }

    override fun getCurrent(): String {
        return currentPassword.concatToString()
    }

    override fun setCurrent(newPassword: String) {
        if (newPassword.all { it in passwordCharacters }) {
            currentPassword = newPassword.toCharArray()
            totalPasswordsGenerated.addAndGet(calculatePasswordsGenerated(newPassword))
            return
        } else {
            val invalidChars = newPassword.filterNot { it in passwordCharacters }
            throw IllegalArgumentException("A senha contém caracteres inválidos: $invalidChars")
        }
    }

    private fun next(): String {
        val nextPassword: String = currentPassword.concatToString()
        incrementPassword()
        totalPasswordsGenerated.incrementAndGet()
        return nextPassword
    }

    private fun incrementPassword() {
        for (position in currentPassword.size - 1 downTo 0) {
            if (currentPassword[position] == passwordCharacters.last()) {
                if (position == 0) {
                    currentPassword = CharArray(currentPassword.size + 1) { passwordCharacters[0] }
                } else {
                    currentPassword[position] = passwordCharacters.first()
                }
            } else {
                currentPassword[position] =
                    passwordCharacters[(charToIndexMap[currentPassword[position]] ?: return) + 1]
                break
            }
        }
    }

    private fun calculatePasswordsGenerated(password: String): Int {
        var count = 0
        var multiplier = 1
        for (i in password.indices) {
            val charIndex = charToIndexMap[password[i]]!!
            count += charIndex * multiplier
            multiplier *= passwordCharacters.size
        }
        return count
    }

}
package decrypt

import com.github.junrar.Archive
import com.github.junrar.exception.UnsupportedRarV5Exception
import net.lingala.zip4j.ZipFile
import org.apache.commons.compress.archivers.sevenz.SevenZFile
import java.io.File

class Encrypt {
    /**
     * Verifica se um arquivo de arquivamento está criptografado.
     *
     * @param archiveFile o arquivo de arquivamento a ser verificado
     * @return `true` se o arquivo estiver criptografado, `false` caso contrário
     * @throws IllegalArgumentException se o arquivo não for suportado
     */
    fun isEncrypted(archiveFile: File): Boolean {
        return try {
            when (archiveFile.extension) {
                "7z" -> SevenZFile.builder().setFile(archiveFile).get().use { false }
                "zip" -> ZipFile(archiveFile).fileHeaders.any { it.isEncrypted }
                "rar" -> Archive(archiveFile).fileHeaders.any { it.isEncrypted }
                else -> throw IllegalArgumentException("Arquivo não suportado!")
            }
        } catch (e: Exception) {
            if (e is UnsupportedRarV5Exception) {
                throw e
            } else {
                true
            }
        }
    }
}
package progress

import password.PasswordIterator
import password.PasswordSequenceIterator
import java.io.File

object Progress : ProgressFileHandler {
    override var passwordIterator: PasswordSequenceIterator = PasswordIterator

    override fun saveProgress(fileName: String) {
        File("${fileName}progress.Progress.txt").writeText(passwordIterator.getCurrent())
    }

    override fun loadProgress(fileName: String) {
        val progressFile = File("${fileName}progress.Progress.txt")
        if (progressFile.exists()) {
            val password = progressFile.readText().trim()
            passwordIterator.setCurrent(password)
        }
    }
}
import decrypt.FileArchiveDecrypt
import kotlinx.coroutines.runBlocking
import java.io.File
import javax.swing.JFileChooser
import javax.swing.JOptionPane
import kotlin.system.exitProcess

fun main() {
    try {
        val fileChooser = JFileChooser()
        val returnValue = fileChooser.showOpenDialog(null)

        if (returnValue != JFileChooser.APPROVE_OPTION) {
            throw IllegalArgumentException("Nenhum arquivo selecionado.")
        }
        val file = fileChooser.selectedFile

        if (!listOf("7z", "zip", "rar").contains(file.extension)) {
            throw IllegalArgumentException("Arquivo selecionado não suportado!")
        }
        val passwordFile = File("${file.name}Password.txt")

        if (passwordFile.exists()) {
            val password = passwordFile.readText().trim()
            runBlocking { FileArchiveDecrypt.tryPassword(file, password) }.also {
                JOptionPane.showMessageDialog(null, "Senha já encontrada!\n$it")
            }
        } else {
            runBlocking { BruteForce.force(file) }.also {
                passwordFile.writeText(it)
            }
        }
    } catch (e: Exception) {
        JOptionPane.showMessageDialog(null, e, "Erro ${e.hashCode()}", JOptionPane.ERROR_MESSAGE)
        exitProcess(e.hashCode())
    }
}

melhore esse código me mostrando uma explicação detalhada dos motivos técnicos para tal, bem como cada classe de forma separada