From 69d4a9c1e61846ef74670b25f8c13fb9527bf87f Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Sat, 24 Feb 2024 22:46:52 +0100 Subject: [PATCH] Wip --- api-webapp/pom.xml | 7 +-- .../org/jeudego/pairgoth/api/TokenHandler.kt | 26 ++++++--- .../jeudego/pairgoth/server/AESCryptograph.kt | 57 ------------------- .../jeudego/pairgoth/server/Cryptograph.kt | 29 ---------- pairgoth-common/pom.xml | 5 ++ .../org/jeudego/pairgoth/util/Cryptograph.kt | 7 +++ view-webapp/pom.xml | 7 +-- .../org/jeudego/pairgoth/oauth/OAuthHelper.kt | 24 +++----- .../org/jeudego/pairgoth/web/AuthFilter.kt | 2 +- 9 files changed, 45 insertions(+), 119 deletions(-) delete mode 100644 api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/AESCryptograph.kt delete mode 100644 api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Cryptograph.kt diff --git a/api-webapp/pom.xml b/api-webapp/pom.xml index 4ddf8bc..4e54e7a 100644 --- a/api-webapp/pom.xml +++ b/api-webapp/pom.xml @@ -16,7 +16,6 @@ PairGoth pairing system TODO - 5.7.1 package @@ -166,9 +165,9 @@ - org.pac4j - pac4j-oauth - ${pac4j.version} + commons-codec + commons-codec + 1.16.1 diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt index 654edb9..a4d6e94 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TokenHandler.kt @@ -1,6 +1,8 @@ package org.jeudego.pairgoth.api import com.republicate.kson.Json +import org.jeudego.pairgoth.util.AESCryptograph +import org.jeudego.pairgoth.util.Cryptograph import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse import javax.servlet.http.HttpSession @@ -9,11 +11,14 @@ class TokenHandler: ApiHandler { companion object { const val AUTH_KEY = "pairgoth-auth" const val CHALLENGE_KEY = "pairgoth-challenge" + private val cryptograph: Cryptograph = AESCryptograph().apply { + init("78659783ed8ccc0e") + } } override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? { val auth = request.session.getAttribute(AUTH_KEY) as String? if (auth == null) { - failed(request.session, response) + failed(request, response) return null } else { return Json.Object( @@ -25,19 +30,26 @@ class TokenHandler: ApiHandler { override fun post(request: HttpServletRequest, response: HttpServletResponse): Json { val auth = getObjectPayload(request) val answer = auth.getString("answer") - if (answer == null) { - failed(request.session, response) + val challenge = request.session.getAttribute(CHALLENGE_KEY) as AuthChallenge? + if (answer == null || challenge == null) { + failed(request, response) } else { - - + val parts = cryptograph.webDecrypt(answer).split(":") + if (parts.size != 2) } } - private fun failed(session: HttpSession, response: HttpServletResponse) { + override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json { + request.session.removeAttribute(AUTH_KEY) + return Json.Object("success" to true) + } + + private fun failed(request: HttpServletRequest, response: HttpServletResponse) { + val session = request.session val challenge = AuthChallenge() session.setAttribute(CHALLENGE_KEY, challenge) response.addHeader("WWW-Authenticate", challenge.value) response.status = HttpServletResponse.SC_UNAUTHORIZED - response.writer.println(Json.Object("status" to "failed")) + response.writer.println(Json.Object("status" to "failed", "message" to "unauthorized")) } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/AESCryptograph.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/AESCryptograph.kt deleted file mode 100644 index d202a2e..0000000 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/AESCryptograph.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.jeudego.pairgoth.server - -import java.nio.charset.Charset -import javax.crypto.Cipher -import javax.crypto.Cipher.DECRYPT_MODE -import javax.crypto.Cipher.ENCRYPT_MODE -import javax.crypto.SecretKey -import javax.crypto.spec.SecretKeySpec - - -/** - * Basic AES encryption. Please note that it uses the ECB block mode, which has the advantage - * to not require random bytes, thus providing some *persistence* for the encrypted data, but - * at the expense of some security weaknesses. The purpose here is just to encrypt temporary - * session ids in URLs, not to protect state secrets. - */ -class AESCryptograph : Cryptograph { - - override fun init(key: String) { - val bytes = key.toByteArray(Charset.defaultCharset()) - if (bytes.size < 16) { - throw Error("not enough secret bytes") - } - val secret: SecretKey = SecretKeySpec(bytes, 0, 16, ALGORITHM) - try { - encrypt.init(ENCRYPT_MODE, secret) - decrypt.init(DECRYPT_MODE, secret) - } catch (e: Exception) { - throw RuntimeException("cyptograph initialization failed", e) - } - } - - override fun encrypt(str: String): ByteArray { - return try { - encrypt.doFinal(str.toByteArray(Charset.defaultCharset())) - } catch (e: Exception) { - throw RuntimeException("encryption failed failed", e) - } - } - - override fun decrypt(bytes: ByteArray): String { - return try { - String(decrypt.doFinal(bytes), Charset.defaultCharset()) - } catch (e: Exception) { - throw RuntimeException("encryption failed failed", e) - } - } - - private var encrypt = Cipher.getInstance(CIPHER) - private var decrypt = Cipher.getInstance(CIPHER) - - companion object { - private val CIPHER = "AES/ECB/PKCS5Padding" - private val ALGORITHM = "AES" - - } -} diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Cryptograph.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Cryptograph.kt deleted file mode 100644 index b3a34ed..0000000 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Cryptograph.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jeudego.pairgoth.server - -import java.io.Serializable - -/** - * Cryptograph - used to encrypt and decrypt strings. - * - */ -interface Cryptograph : Serializable { - /** - * init. - * @param random random string - */ - fun init(random: String) - - /** - * encrypt. - * @param str string to encrypt - * @return encrypted string - */ - fun encrypt(str: String): ByteArray - - /** - * decrypt. - * @param bytes to decrypt - * @return decrypted string - */ - fun decrypt(bytes: ByteArray): String -} diff --git a/pairgoth-common/pom.xml b/pairgoth-common/pom.xml index 4b545b7..7f668b3 100644 --- a/pairgoth-common/pom.xml +++ b/pairgoth-common/pom.xml @@ -76,6 +76,11 @@ kotlinx-datetime-jvm 0.4.0 + + commons-codec + commons-codec + 1.16.1 + jakarta.servlet diff --git a/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt b/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt index 2a221d0..b9d0f34 100644 --- a/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt +++ b/pairgoth-common/src/main/kotlin/org/jeudego/pairgoth/util/Cryptograph.kt @@ -1,5 +1,6 @@ package org.jeudego.pairgoth.util +import org.apache.commons.codec.binary.Base64 import java.io.Serializable /** @@ -26,4 +27,10 @@ interface Cryptograph : Serializable { * @return decrypted string */ fun decrypt(bytes: ByteArray): String + + + fun webEncrypt(str: String) = Base64.encodeBase64URLSafeString(encrypt(str)) + + fun webDecrypt(str: String) = decrypt(Base64.decodeBase64(str)) + } diff --git a/view-webapp/pom.xml b/view-webapp/pom.xml index a96c5a8..af1f18d 100644 --- a/view-webapp/pom.xml +++ b/view-webapp/pom.xml @@ -16,7 +16,6 @@ PairGoth pairing system TODO - 5.7.1 9.9.0 @@ -184,9 +183,9 @@ - org.pac4j - pac4j-oauth - ${pac4j.version} + commons-codec + commons-codec + 1.16.1 diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt index 7cdf5fa..27665cc 100644 --- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/oauth/OAuthHelper.kt @@ -6,7 +6,6 @@ import com.republicate.kson.Json import org.jeudego.pairgoth.web.WebappManager //import com.republicate.modality.util.AESCryptograph //import com.republicate.modality.util.Cryptograph -import org.apache.commons.codec.binary.Base64 import org.apache.http.NameValuePair import org.jeudego.pairgoth.util.AESCryptograph import org.jeudego.pairgoth.util.ApiClient.JsonApiClient @@ -14,8 +13,6 @@ import org.jeudego.pairgoth.util.Cryptograph import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException -import java.io.UnsupportedEncodingException -import java.net.URLEncoder abstract class OAuthHelper { abstract val name: String @@ -28,20 +25,22 @@ abstract class OAuthHelper { get() = WebappManager.getMandatoryProperty("webapp.external.url").removeSuffix("/") + "/oauth/${name}" protected fun getState(sessionId: String): String { - return name + ":" + encrypt(sessionId) + return name + ":" + cryptograph.webEncrypt(sessionId) } fun checkState(state: String, expectedSessionId: String): Boolean { - val foundSessionId = decrypt(state) + val foundSessionId = cryptograph.webDecrypt(state) return expectedSessionId == foundSessionId } protected abstract fun getAccessTokenURL(code: String): Pair> @Throws(IOException::class) - fun getAccessToken(code: String): String { + fun getAccessToken(sessionID: String, code: String): String { val (url, params) = getAccessTokenURL(code) val json = JsonApiClient.post(url, null, *params.toTypedArray()).asObject() + val state = json.getString("state") ?: throw IOException("could not get state") + if (!checkState(state, sessionID)) throw IOException("invalid state") return json.getString("access_token") ?: throw IOException("could not get access token") } @@ -55,17 +54,8 @@ abstract class OAuthHelper { companion object { protected var logger: Logger = LoggerFactory.getLogger("oauth") - private const val salt = "0efd28fb53cbac42" - private val sessionIdCrypto: Cryptograph = AESCryptograph().apply { - init(salt) - } - - private fun encrypt(input: String): String { - return Base64.encodeBase64URLSafeString(sessionIdCrypto.encrypt(input)) - } - - private fun decrypt(input: String): String { - return sessionIdCrypto.decrypt(Base64.decodeBase64(input)) + private val cryptograph: Cryptograph = AESCryptograph().apply { + init("0efd28fb53cbac42") } } } \ No newline at end of file diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt index f08922b..3fbbfb6 100644 --- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/web/AuthFilter.kt @@ -34,7 +34,7 @@ class AuthFilter: Filter { if (auth == "oauth" && uri.startsWith("/oauth/")) { val provider = uri.substring("/oauth/".length) val helper = OauthHelperFactory.getHelper(provider) - val accessToken = helper.getAccessToken(request.getParameter("code") ?: "") + val accessToken = helper.getAccessToken(request.session.id, request.getParameter("code") ?: "") val user = helper.getUserInfos(accessToken) request.session.setAttribute("logged", user) response.sendRedirect("/index")