Tworzenie klucza/pary kluczy ECC na podstawie string'a

0

Hej, ogólnie problem jaki mam, to "jak utworzyć parę kluczy szyfrujących na podstawie tego co użytkownik ma w głowie". Grzebię sobie trochę w ECC i trochę utknąłem na przekształceniu wartości liczbowej w coś co będzie robiło za klucz prywatny. Ma ktoś jakiś prosty sposób nie wymagający pisania własnych implementacji tego algorytmu?

Na ten moment proszę o pominięcie aspektów bezpieczeństwa. Ogólnie problem polega na tym, że użytkownik pamięta sobie "kotek" i to ma być wystarczający składnik do podpisania jakiejś wiadomości, przy założeniu, że nikt inny tego "kotek" nie zna, a jedynie jakieś jego pochodne.

1

Klucze generujesz normalnie, tylko potem szyfrujesz je kluczem symetrycznym wygenerowanym z hasła i gdzieś to przechowujesz

0

No problem właśnie w tym, że nie mam gdzie ich przechowywać, a myślę sobie, ze skoro klucz EC to liczba, mogę sobie tę luczbę wygenerować jako np. SHA-256(password) (mniejsza o jakość tego...), następnie z klucza prywatnego wybiągnąć klucz publiczny, to mogę przechowywać parę kluczy wyłącznie "w głowie" użytkownika, co ma akurat w moim przypadku dość spore zalety.

1

no w sumie wygląda na to że możesz zrobić dokładnie tak jak piszesz
https://stackoverflow.com/questions/57240808/generate-private-key-from-seed
łatwo będzie do tego jednak utworzyć tablice tęczowe

0

Gdzie będziesz trzymał klucz publiczny służący do weryfikacji podpisu?

Istnieją algorytmy Key derivation function, ale te generują klucz symetryczny ze względu na ograniczoną użyteczność w kryptografii klucz publicznego.

0

@Krzemień: Część publiczna będzie po stronie weryfikującej podpis.

Ogólnie problem polega na tym, że na użytkownik posługując się wspólnym urządzeniem musi złożyć podpis pod wiadomością. Urządzenie nie jest trwale podłączone do sieci. Idea jest taka, że:

Użytkownik ustawia sobie hasło bo backendu, backend tworzy pubK(prvK(password)) i zapisuje sobie taki "hash".

Na urządzeniu "offline" użytkownik tworzy dokument, następnie podpisuje swoim hasłem i idzie do domu.
Urządzenie po odzyskaniu połączenia z siecią wysyła podpisany dokument do backendu, który weryfikuje treść i w przypadku pozytywnego wyniku zapisuje go sobie.

1

Ma to jakiś sens. Biblioteka której użyjesz do podpisywania (bouncy castle?) powinna mieć możliwość stworzenia klucza deterministycznie podając d. d można wyznaczyć funkcją kdf, np. argon2, scrypt, żeby utrudnić odgadnięcie hasła.
I jeszcze jedna uwaga, jeśli klient wyśle backendowi hasło albo klucz prywatny, to podpis pod wiadomością może weryfikować tylko backend. Trzecia strona nie będzie w stanie stwierdzić czy podpis pochodzi od klienta, czy backendu.

0

@Krzemień: Możliwość weryfikacji jestem w stanie zapewnić dystrybuując po urządzeniach w sieci te dane, out of scope - to już jest robione. Piszę sobie właśnie jakieś PoC całej zabawy przy użyciu bouncy castle (prościej by było, gdybym coś na ten temat wiedział, a nie tylko hasłowo). Co do generowania samego kodu, myślałem o czymś w stylu:

final static String salt = "asdlkjhaslkdfjasldkfjh"
private String hash = password
for(int i = 0; i < 5000; i++){
  hash = sha256(salt+hash)
}

Odpowiednio długi salt powinien zablokować możliwość ataku przez rainbow tables, a liczba iteracji nieco skomplikować życie atakującym. Oczywiście przykład, czeka mnie trochę czytania jeszcze.

0

Wrzucam jako ciekawostkę:

import org.junit.jupiter.api.Assertions.assertEquals
import kotlin.test.Test
import kotlin.test.assertTrue

internal class MainKtTest {

    @Test
    fun passwordToPrivateKeyTest() {
        val key1 = passwordToPrivateKeyBC("password")
        val key2 = passwordToPrivateKeyBC("password")

        assertEquals(key1, key2)
    }

    @Test
    fun generateSignVerify() {
        val password = "password"
        val privateKey = passwordToPrivateKeyBC(password)
        val message = "This is message to sign and verify"
        val sign = sign(privateKey, message)

        val verificationResult = verify(privateKeyToPublicKey(privateKey), message, sign[0], sign[1])

        assertTrue(verificationResult)
    }
}

i sam kod:

import org.bouncycastle.asn1.sec.ECPrivateKey
import org.bouncycastle.asn1.sec.SECNamedCurves
import org.bouncycastle.crypto.digests.SHA256Digest
import org.bouncycastle.crypto.params.ECDomainParameters
import org.bouncycastle.crypto.params.ECPrivateKeyParameters
import org.bouncycastle.crypto.params.ECPublicKeyParameters
import org.bouncycastle.crypto.signers.ECDSASigner
import org.bouncycastle.crypto.signers.HMacDSAKCalculator
import org.bouncycastle.math.ec.ECPoint
import java.math.BigInteger
import java.security.MessageDigest

fun passwordToPrivateKeyBC(password: String): ECPrivateKey {
    val passwordDigest = MessageDigest.getInstance("SHA-256").digest(password.toByteArray())
    val d = BigInteger(passwordDigest)
    return ECPrivateKey(256, d)
}

private const val CURVE = "secp256k1"

fun privateKeyToPublicKey(privateKey: ECPrivateKey): ECPoint {
    val curve = SECNamedCurves.getByName(CURVE)
    return curve.g.multiply(privateKey.key)
}

fun sign(privateKey: ECPrivateKey, textToSing: String): Array<out BigInteger> {
    val signer = ECDSASigner(HMacDSAKCalculator(SHA256Digest()))
    val privateKeyParams = ECPrivateKeyParameters(privateKey.key, ECDomainParameters(SECNamedCurves.getByName(CURVE)))
    signer.init(true, privateKeyParams)

    return signer.generateSignature(textToSing.toByteArray())
}

fun verify(publicKey: ECPoint, message: String, r: BigInteger, s: BigInteger): Boolean {
    val ecDomainParameters: ECDomainParameters = ECDomainParameters(SECNamedCurves.getByName(CURVE))
    val ecPublicKeyParameters = ECPublicKeyParameters(publicKey, ecDomainParameters)
    val signer = ECDSASigner()
    signer.init(false, ecPublicKeyParameters)
    return signer.verifySignature(message.toByteArray(), r, s)
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1