Crypto now works with the raw public/private key byte arrays (instead of encoded bytearrays)
This commit is contained in:
parent
32d3023814
commit
1703bc5449
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user