Crypto now works with the raw public/private key byte arrays (instead of encoded bytearrays)

This commit is contained in:
nathan 2020-09-19 01:32:32 +02:00
parent 32d3023814
commit 1703bc5449

View File

@ -18,7 +18,6 @@ package dorkbox.network.connection
import dorkbox.netUtil.IP import dorkbox.netUtil.IP
import dorkbox.network.Configuration import dorkbox.network.Configuration
import dorkbox.network.handshake.ClientConnectionInfo import dorkbox.network.handshake.ClientConnectionInfo
import dorkbox.network.other.CryptoEccNative
import dorkbox.network.serialization.AeronInput import dorkbox.network.serialization.AeronInput
import dorkbox.network.serialization.AeronOutput import dorkbox.network.serialization.AeronOutput
import dorkbox.network.storage.SettingsStore import dorkbox.network.storage.SettingsStore
@ -26,19 +25,17 @@ import dorkbox.util.Sys
import dorkbox.util.entropy.Entropy import dorkbox.util.entropy.Entropy
import dorkbox.util.exceptions.SecurityException import dorkbox.util.exceptions.SecurityException
import mu.KLogger import mu.KLogger
import java.math.BigInteger
import java.net.InetAddress import java.net.InetAddress
import java.security.KeyFactory import java.security.KeyFactory
import java.security.KeyPair
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.security.MessageDigest import java.security.MessageDigest
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.SecureRandom import java.security.SecureRandom
import java.security.interfaces.XECPrivateKey
import java.security.interfaces.XECPublicKey
import java.security.spec.NamedParameterSpec import java.security.spec.NamedParameterSpec
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.XECPrivateKeySpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.XECPublicKeySpec
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.KeyAgreement import javax.crypto.KeyAgreement
import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.GCMParameterSpec
@ -48,17 +45,17 @@ import javax.crypto.spec.SecretKeySpec
/** /**
* Management for all of the crypto stuff used * Management for all of the crypto stuff used
*/ */
internal class CryptoManagement(val logger: KLogger, internal class CryptoManagement(val logger: KLogger, private val settingsStore: SettingsStore, type: Class<*>, config: Configuration) {
private val settingsStore: SettingsStore,
type: Class<*>,
config: Configuration) {
private val keyFactory = KeyFactory.getInstance("X25519") private val X25519 = "X25519"
private val X25519KeySpec = NamedParameterSpec(X25519)
private val keyFactory = KeyFactory.getInstance(X25519) // key size is 32 bytes
private val keyAgreement = KeyAgreement.getInstance("XDH") private val keyAgreement = KeyAgreement.getInstance("XDH")
private val aesCipher = Cipher.getInstance("AES/GCM/PKCS5Padding") private val aesCipher = Cipher.getInstance("AES/GCM/PKCS5Padding")
private val hash = MessageDigest.getInstance("SHA-256"); private val hash = MessageDigest.getInstance("SHA-256");
companion object { companion object {
const val curve25519 = "curve25519"
const val AES_KEY_SIZE = 256 const val AES_KEY_SIZE = 256
const val GCM_IV_LENGTH = 12 const val GCM_IV_LENGTH = 12
const val GCM_TAG_LENGTH = 16 const val GCM_TAG_LENGTH = 16
@ -89,13 +86,16 @@ internal class CryptoManagement(val logger: KLogger,
if (privateKeyBytes == null || publicKeyBytes == null) { if (privateKeyBytes == null || publicKeyBytes == null) {
try { try {
// seed our RNG based off of this and create our ECC keys // seed our RNG based off of this and create our ECC keys
val seedBytes = Entropy.get("There are no ECC keys for the " + type.simpleName + " yet") val seedBytes = Entropy.get("There are no ECC keys for the ${type.simpleName} yet")
logger.info("Now generating ECC (" + CryptoEccNative.curve25519 + ") keys. Please wait!") logger.info("Now generating ECC ($curve25519) keys. Please wait!")
secureRandom.nextBytes(seedBytes) secureRandom.nextBytes(seedBytes)
val generateKeyPair = createKeyPair(secureRandom)
privateKeyBytes = generateKeyPair.private.encoded // see: https://stackoverflow.com/questions/60303561/how-do-i-pass-a-44-bytes-x25519-public-key-created-by-openssl-to-cryptokit-which
publicKeyBytes = generateKeyPair.public.encoded val (xdhPublic, xdhPrivate) = createKeyPair(secureRandom)
publicKeyBytes = xdhPublic.u.toByteArray()
privateKeyBytes = xdhPrivate.scalar
// save to properties file // save to properties file
settingsStore.savePrivateKey(privateKeyBytes) settingsStore.savePrivateKey(privateKeyBytes)
@ -109,19 +109,23 @@ internal class CryptoManagement(val logger: KLogger,
logger.info("ECC public key: ${Sys.bytesToHex(publicKeyBytes)}") logger.info("ECC public key: ${Sys.bytesToHex(publicKeyBytes)}")
this.privateKey = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privateKeyBytes)) as XECPrivateKey this.publicKey = keyFactory.generatePublic(XECPublicKeySpec(X25519KeySpec, BigInteger(publicKeyBytes)))
this.publicKey = keyFactory.generatePublic(X509EncodedKeySpec(publicKeyBytes)) as XECPublicKey this.privateKey = keyFactory.generatePrivate(XECPrivateKeySpec(X25519KeySpec, privateKeyBytes))
this.publicKeyBytes = publicKeyBytes!! this.publicKeyBytes = publicKeyBytes!!
} }
private fun createKeyPair(secureRandom: SecureRandom): KeyPair { private fun createKeyPair(secureRandom: SecureRandom): Pair<XECPublicKeySpec, XECPrivateKeySpec> {
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance("XDH") val kpg: KeyPairGenerator = KeyPairGenerator.getInstance("XDH")
kpg.initialize(NamedParameterSpec.X25519, secureRandom) kpg.initialize(NamedParameterSpec.X25519, secureRandom)
return kpg.generateKeyPair() val keyPair = kpg.generateKeyPair()
// get the ACTUAL key spec, so we can get the ACTUAL key byte arrays
val xdhPublic: XECPublicKeySpec = keyFactory.getKeySpec(keyPair.public, XECPublicKeySpec::class.java)
val xdhPrivate: XECPrivateKeySpec = keyFactory.getKeySpec(keyPair.private, XECPrivateKeySpec::class.java)
return Pair(xdhPublic, xdhPrivate)
} }
/** /**
* If the key does not match AND we have disabled remote key validation -- key validation is REQUIRED! * If the key does not match AND we have disabled remote key validation -- key validation is REQUIRED!
* *
@ -136,11 +140,7 @@ internal class CryptoManagement(val logger: KLogger,
try { try {
val savedPublicKey = settingsStore.getRegisteredServerKey(remoteAddress) val savedPublicKey = settingsStore.getRegisteredServerKey(remoteAddress)
if (savedPublicKey == null) { if (savedPublicKey != null) {
logger.info("Adding new signature for ${IP.toString(remoteAddress)} : ${Sys.bytesToHex(publicKey)}")
settingsStore.addRegisteredServerKey(remoteAddress, publicKey)
} else {
// COMPARE! // COMPARE!
if (!publicKey.contentEquals(savedPublicKey)) { if (!publicKey.contentEquals(savedPublicKey)) {
return if (enableRemoteSignatureValidation) { return if (enableRemoteSignatureValidation) {
@ -167,8 +167,7 @@ internal class CryptoManagement(val logger: KLogger,
* Generate the AES key based on ECDH * Generate the AES key based on ECDH
*/ */
private fun generateAesKey(remotePublicKeyBytes: ByteArray, bytesA: ByteArray, bytesB: ByteArray): SecretKeySpec { private fun generateAesKey(remotePublicKeyBytes: ByteArray, bytesA: ByteArray, bytesB: ByteArray): SecretKeySpec {
val clientPublicKey = keyFactory.generatePublic(X509EncodedKeySpec(remotePublicKeyBytes)) as XECPublicKey val clientPublicKey = keyFactory.generatePublic(XECPublicKeySpec(X25519KeySpec, BigInteger(remotePublicKeyBytes)))
keyAgreement.init(privateKey) keyAgreement.init(privateKey)
keyAgreement.doPhase(clientPublicKey, true) keyAgreement.doPhase(clientPublicKey, true)
val sharedSecret = keyAgreement.generateSecret() val sharedSecret = keyAgreement.generateSecret()
@ -216,12 +215,10 @@ internal class CryptoManagement(val logger: KLogger,
val secretKeySpec = generateAesKey(serverPublicKeyBytes, publicKeyBytes, serverPublicKeyBytes) val secretKeySpec = generateAesKey(serverPublicKeyBytes, publicKeyBytes, serverPublicKeyBytes)
// now read the encrypted data // now read the encrypted data
registrationData.copyInto(destination = iv, registrationData.copyInto(destination = iv, endIndex = GCM_IV_LENGTH)
endIndex = GCM_IV_LENGTH)
val secretBytes = ByteArray(registrationData.size - GCM_IV_LENGTH) val secretBytes = ByteArray(registrationData.size - GCM_IV_LENGTH)
registrationData.copyInto(destination = secretBytes, registrationData.copyInto(destination = secretBytes, startIndex = GCM_IV_LENGTH)
startIndex = GCM_IV_LENGTH)
// now decrypt the data // now decrypt the data