From 29ab4bf873697932cb8e1a9e2455af6aad864731 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 5 Feb 2016 10:33:50 +0100 Subject: [PATCH] [#4793] Correctly add newlines when encode base64 Motivation: We not correctly added newlines if the src data needed to be padded. This regression was introduced by '63426fc3ed083513c07a58b45381f5c10dd47061' Modifications: - Correctly handling newlines - Add unit test that proves the fix. Result: No more invalid base64 encoded data. --- .../io/netty/handler/codec/base64/Base64.java | 12 ++-- .../handler/codec/base64/Base64Test.java | 62 +++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64.java index adce88b6262b..dc3f1aff801d 100644 --- a/codec/src/main/java/io/netty/handler/codec/base64/Base64.java +++ b/codec/src/main/java/io/netty/handler/codec/base64/Base64.java @@ -126,15 +126,12 @@ public static ByteBuf encode( int e = 0; int len2 = len - 2; int lineLength = 0; - for (; d < len2; e += 4) { + for (; d < len2; d += 3, e += 4) { encode3to4(src, d + off, 3, dest, e, dialect); lineLength += 4; - d += 3; - if (breakLines && lineLength == MAX_LINE_LENGTH - // Only add NEW_LINE if we not ended directly on the MAX_LINE_LENGTH - && d < len2) { + if (breakLines && lineLength == MAX_LINE_LENGTH) { dest.setByte(e + 4, NEW_LINE); e ++; lineLength = 0; @@ -146,6 +143,11 @@ public static ByteBuf encode( e += 4; } // end if: some padding needed + // Remove last byte if it's a newline + if (e > 1 && dest.getByte(e - 1) == NEW_LINE) { + e--; + } + return dest.slice(0, e); } diff --git a/codec/src/test/java/io/netty/handler/codec/base64/Base64Test.java b/codec/src/test/java/io/netty/handler/codec/base64/Base64Test.java index 8d686c3d8746..68233e6c9c6b 100644 --- a/codec/src/test/java/io/netty/handler/codec/base64/Base64Test.java +++ b/codec/src/test/java/io/netty/handler/codec/base64/Base64Test.java @@ -16,10 +16,16 @@ package io.netty.handler.codec.base64; import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.util.CharsetUtil; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + import static io.netty.buffer.Unpooled.copiedBuffer; import static org.junit.Assert.assertEquals; @@ -45,6 +51,62 @@ public void testAddNewLine() { testEncode(src, expectedEncoded); } + @Test + public void testEncodeEmpty() { + ByteBuf src = Unpooled.EMPTY_BUFFER; + ByteBuf expectedEncoded = Unpooled.EMPTY_BUFFER; + testEncode(src, expectedEncoded); + } + + @Test + public void testPaddingNewline() throws Exception { + String cert = "-----BEGIN CERTIFICATE-----\n" + + "MIICqjCCAjGgAwIBAgICI1YwCQYHKoZIzj0EATAmMSQwIgYDVQQDDBtUcnVzdGVk\n" + + "IFRoaW4gQ2xpZW50IFJvb3QgQ0EwIhcRMTYwMTI0MTU0OTQ1LTA2MDAXDTE2MDQy\n" + + "NTIyNDk0NVowYzEwMC4GA1UEAwwnREMgMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYt\n" + + "YjJmNTI0ZjA2ZGE0MREwDwYDVQQLDAhEQyBJUFNFQzEcMBoGA1UECgwTVHJ1c3Rl\n" + + "ZCBUaGluIENsaWVudDB2MBAGByqGSM49AgEGBSuBBAAiA2IABOB7pZYC24sF5gJm\n" + + "OHXhasxmrNYebdtSAiQRgz0M0pIsogsFeTU/W0HTlTOqwDDckphHESAKHVxa6EBL\n" + + "d+/8HYZ1AaCmXtG73XpaOyaRr3TipJl2IaJzwuehgDHs0L+qcqOB8TCB7jAwBgYr\n" + + "BgEBEAQEJgwkMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYtYjJmNTI0ZjA2ZGE0MCMG\n" + + "CisGAQQBjCHbZwEEFQwTNDkwNzUyMjc1NjM3MTE3Mjg5NjAUBgorBgEEAYwh22cC\n" + + "BAYMBDIwNTkwCwYDVR0PBAQDAgXgMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGWljaKj\n" + + "wiGqW61PgLL/zLxj4iirMB8GA1UdIwQYMBaAFA2FRBtG/dGnl0iXP2uKFwJHmEQI\n" + + "MCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwkwCQYHKoZI\n" + + "zj0EAQNoADBlAjAQFP8rMLUxl36u8610LsSCiRG8pP3gjuLaaJMm3tjbVue/TI4C\n" + + "z3iL8i96YWK0VxcCMQC7pf6Wk3RhUU2Sg6S9e6CiirFLDyzLkaWxuCnXcOwTvuXT\n" + + "HUQSeUCp2Q6ygS5qKyc=\n" + + "-----END CERTIFICATE-----"; + + String expected = "MIICqjCCAjGgAwIBAgICI1YwCQYHKoZIzj0EATAmMSQwIgYDVQQDDBtUcnVzdGVkIFRoaW4gQ2xp\n" + + "ZW50IFJvb3QgQ0EwIhcRMTYwMTI0MTU0OTQ1LTA2MDAXDTE2MDQyNTIyNDk0NVowYzEwMC4GA1UE\n" + + "AwwnREMgMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYtYjJmNTI0ZjA2ZGE0MREwDwYDVQQLDAhEQyBJ\n" + + "UFNFQzEcMBoGA1UECgwTVHJ1c3RlZCBUaGluIENsaWVudDB2MBAGByqGSM49AgEGBSuBBAAiA2IA\n" + + "BOB7pZYC24sF5gJmOHXhasxmrNYebdtSAiQRgz0M0pIsogsFeTU/W0HTlTOqwDDckphHESAKHVxa\n" + + "6EBLd+/8HYZ1AaCmXtG73XpaOyaRr3TipJl2IaJzwuehgDHs0L+qcqOB8TCB7jAwBgYrBgEBEAQE\n" + + "JgwkMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYtYjJmNTI0ZjA2ZGE0MCMGCisGAQQBjCHbZwEEFQwT\n" + + "NDkwNzUyMjc1NjM3MTE3Mjg5NjAUBgorBgEEAYwh22cCBAYMBDIwNTkwCwYDVR0PBAQDAgXgMAkG\n" + + "A1UdEwQCMAAwHQYDVR0OBBYEFGWljaKjwiGqW61PgLL/zLxj4iirMB8GA1UdIwQYMBaAFA2FRBtG\n" + + "/dGnl0iXP2uKFwJHmEQIMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwkw\n" + + "CQYHKoZIzj0EAQNoADBlAjAQFP8rMLUxl36u8610LsSCiRG8pP3gjuLaaJMm3tjbVue/TI4Cz3iL\n" + + "8i96YWK0VxcCMQC7pf6Wk3RhUU2Sg6S9e6CiirFLDyzLkaWxuCnXcOwTvuXTHUQSeUCp2Q6ygS5q\n" + + "Kyc="; + + ByteBuf src = Unpooled.wrappedBuffer(certFromString(cert).getEncoded()); + ByteBuf expectedEncoded = copiedBuffer(expected, CharsetUtil.US_ASCII); + testEncode(src, expectedEncoded); + } + + private static X509Certificate certFromString(String string) throws Exception { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream bin = new ByteArrayInputStream(string.getBytes(CharsetUtil.US_ASCII)); + try { + return (X509Certificate) factory.generateCertificate(bin); + } finally { + bin.close(); + } + } + private static void testEncode(ByteBuf src, ByteBuf expectedEncoded) { ByteBuf encoded = Base64.encode(src, true, Base64Dialect.STANDARD); try {