Skip to content

Commit

Permalink
Merge pull request #358 from silk-framework/feature/improvedPasswordE…
Browse files Browse the repository at this point in the history
…ncryption

Improved password encryption
  • Loading branch information
andreas-schultz authored Mar 30, 2020
2 parents 45d5cb4 + 3c9c804 commit f08e10b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package org.silkframework.runtime.plugin
import java.lang.reflect.{ParameterizedType, Type}
import java.net.{URLDecoder, URLEncoder}
import java.security.InvalidKeyException
import java.util.logging.Logger
import java.security.spec.InvalidKeySpecException
import java.util.logging.{Level, Logger}

import javax.crypto.SecretKey
import org.silkframework.config.{DefaultConfig, Prefixes, ProjectReference, TaskReference}
import org.silkframework.dataset.rdf.SparqlEndpointDatasetParameter
import org.silkframework.execution.AbortExecutionException
Expand All @@ -14,7 +16,7 @@ import org.silkframework.util.{AesCrypto, Identifier, Uri}

import scala.language.existentials
import scala.reflect.ClassTag
import scala.util.Try
import scala.util.{Failure, Success, Try}

/**
* Represents a plugin parameter type and provides serialization.
Expand Down Expand Up @@ -361,10 +363,26 @@ object ParameterType {

override def description: String = "A password string."

lazy val key: String = {
Try(DefaultConfig.instance().getString(CONFIG_KEY)).getOrElse {
log.warning(s"No valid value set for $CONFIG_KEY, using insecure default key!")
"1234567890123456"
/**
* The configured key. Failure, if no key has been configured or the key is invalid.
*/
lazy val configuredKey: Try[SecretKey] = {
for {
password <- Try(DefaultConfig.instance().getString(CONFIG_KEY))
checkedPassword <- checkPassword(password)
key <- Try(AesCrypto.generateKey(checkedPassword))
} yield key
}

/**
* The key used for encryption. A default key will be used, if no key has been configured.
*/
lazy val key: SecretKey = {
configuredKey match {
case Success(k) => k
case Failure(ex) =>
log.log(Level.WARNING, s"No valid key set for $CONFIG_KEY, using insecure default key!", ex)
AesCrypto.generateKey("1234567890123456")
}
}

Expand All @@ -374,16 +392,24 @@ object ParameterType {
} else if (str.startsWith(PREAMBLE)) {
str.stripPrefix(PREAMBLE)
} else {
try {
AesCrypto.encrypt(key, str)
} catch {
case ex: InvalidKeyException =>
throw new RuntimeException(s"The password parameter encryption key is invalid. Value for " +
s"${PasswordParameterType.CONFIG_KEY} needs to be a character string of length 16.", ex)
}
AesCrypto.encrypt(key, str)
}
PasswordParameter(encryptedPassword)
}

/**
* Makes sure that the password has been set and is longer than 16 characters.
*/
private def checkPassword(password: String): Try[String] = {
if(password == "changemechangeme") {
Failure(new InvalidKeySpecException("Default key is not overridden"))
} else if(password.length < 16) {
Failure(new InvalidKeySpecException("Key must be at least 16 characters long"))
} else {
Success(password)
}
}

}

object SparqlEndpointDatasetParameterType extends ParameterType[SparqlEndpointDatasetParameter] {
Expand Down
34 changes: 25 additions & 9 deletions silk-core/src/main/scala/org/silkframework/util/AesCrypto.scala
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
package org.silkframework.util

import java.util.Base64

import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.SecretKey
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
import java.security.spec.InvalidKeySpecException
import javax.crypto.spec.SecretKeySpec

object AesCrypto {
final val RANDOM_INIT_VECTOR = "FKJrJQWZ9DEW6KOv"

def encrypt(key: String, value: String): String = {
val iv = new IvParameterSpec(RANDOM_INIT_VECTOR.getBytes("UTF-8"))
val skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES")
private final val INIT_VECTOR = new IvParameterSpec("FKJrJQWZ9DEW6KOv".getBytes("UTF-8"))

private final val SALT = Array[Byte](9, -119, -42, 5, -63, 102, -11, -104, 66, -17, 112, 55, 44, 73, 32, -12, -103, 88, 14, -44, 18, -46, 30, -6, -55, 28, -54, 12, 39, 110, 63, 125)

/**
* Generates a secret key from a password.
*
* @throws InvalidKeySpecException If no key could be generated from the password.
*/
def generateKey(password: String): SecretKey = {
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val spec = new PBEKeySpec(password.toCharArray, SALT, 65536, 256)
val secret = factory.generateSecret(spec)
new SecretKeySpec(secret.getEncoded, "AES")
}

def encrypt(key: SecretKey, value: String): String = {
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv)
cipher.init(Cipher.ENCRYPT_MODE, key, INIT_VECTOR)
val encrypted = cipher.doFinal(value.getBytes)
Base64.getEncoder.encodeToString(encrypted)
}

def decrypt(key: String, encrypted: String): String = {
val iv = new IvParameterSpec(RANDOM_INIT_VECTOR.getBytes("UTF-8"))
val skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES")
def decrypt(key: SecretKey, encrypted: String): String = {
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv)
cipher.init(Cipher.DECRYPT_MODE, key, INIT_VECTOR)
val original = cipher.doFinal(Base64.getDecoder.decode(encrypted))
new String(original)
}
Expand Down

0 comments on commit f08e10b

Please sign in to comment.