diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/PersistentLoginManager.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/PersistentLoginManager.java index 70c947bca258b..7127ec41e25d5 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/PersistentLoginManager.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/PersistentLoginManager.java @@ -1,12 +1,15 @@ package io.quarkus.vertx.http.runtime.security; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.security.SecureRandom; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.jboss.logging.Logger; @@ -25,10 +28,13 @@ public class PersistentLoginManager { private static final Logger log = Logger.getLogger(PersistentLoginManager.class); + private static final String ENC_ALGORITHM = "AES/GCM/NoPadding"; + private static final int ENC_TAG_LENGTH = 128; private final SecretKey secretKey; private final String cookieName; private final long timeoutMillis; + private final SecureRandom secureRandom = new SecureRandom(); public PersistentLoginManager(String encryptionKey, String cookieName, long timeoutMillis) { try { @@ -56,9 +62,15 @@ public RestoreResult restore(RoutingContext context) { } String val = existing.getValue(); try { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, secretKey); - String result = new String(cipher.doFinal(Base64.getDecoder().decode(val)), StandardCharsets.UTF_8); + Cipher cipher = Cipher.getInstance(ENC_ALGORITHM); + ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.getDecoder().decode(val.getBytes(StandardCharsets.UTF_8))); + int ivLength = byteBuffer.get(); + byte[] iv = new byte[ivLength]; + byteBuffer.get(iv); + byte[] encrypted = new byte[byteBuffer.remaining()]; + byteBuffer.get(encrypted); + cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(ENC_TAG_LENGTH, iv)); + String result = new String(cipher.doFinal(encrypted)); int sep = result.indexOf(":"); if (sep == -1) { return null; @@ -81,18 +93,21 @@ public void save(SecurityIdentity identity, RoutingContext context, RestoreResul } } try { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - + Cipher cipher = Cipher.getInstance(ENC_ALGORITHM); + byte[] iv = new byte[12]; + secureRandom.nextBytes(iv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(ENC_TAG_LENGTH, iv)); StringBuilder contents = new StringBuilder(); - //TODO: do we need random padding? long timeout = System.currentTimeMillis() + timeoutMillis; contents.append(timeout); contents.append(":"); contents.append(identity.getPrincipal().getName()); - - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - String cookieValue = Base64.getEncoder() - .encodeToString(cipher.doFinal(contents.toString().getBytes(StandardCharsets.UTF_8))); + byte[] encrypted = cipher.doFinal(contents.toString().getBytes(StandardCharsets.UTF_8)); + ByteBuffer message = ByteBuffer.allocate(1 + iv.length + encrypted.length); + message.put((byte) iv.length); + message.put(iv); + message.put(encrypted); + String cookieValue = Base64.getEncoder().encodeToString(message.array()); context.addCookie(Cookie.cookie(cookieName, cookieValue).setPath("/")); } catch (Exception e) { throw new RuntimeException(e);