[#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.
This commit is contained in:
Norman Maurer 2016-02-05 10:33:50 +01:00
parent acbf1b9e7e
commit 65b3470456
2 changed files with 69 additions and 5 deletions

View File

@ -126,15 +126,12 @@ public final class Base64 {
int e = 0; int e = 0;
int len2 = len - 2; int len2 = len - 2;
int lineLength = 0; int lineLength = 0;
for (; d < len2; e += 4) { for (; d < len2; d += 3, e += 4) {
encode3to4(src, d + off, 3, dest, e, dialect); encode3to4(src, d + off, 3, dest, e, dialect);
lineLength += 4; lineLength += 4;
d += 3;
if (breakLines && lineLength == MAX_LINE_LENGTH if (breakLines && lineLength == MAX_LINE_LENGTH) {
// Only add NEW_LINE if we not ended directly on the MAX_LINE_LENGTH
&& d < len2) {
dest.setByte(e + 4, NEW_LINE); dest.setByte(e + 4, NEW_LINE);
e ++; e ++;
lineLength = 0; lineLength = 0;
@ -146,6 +143,11 @@ public final class Base64 {
e += 4; e += 4;
} // end if: some padding needed } // 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); return dest.slice(0, e);
} }

View File

@ -16,10 +16,16 @@
package io.netty.handler.codec.base64; package io.netty.handler.codec.base64;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Test; 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 io.netty.buffer.Unpooled.copiedBuffer;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -45,6 +51,62 @@ public class Base64Test {
testEncode(src, expectedEncoded); 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) { private static void testEncode(ByteBuf src, ByteBuf expectedEncoded) {
ByteBuf encoded = Base64.encode(src, true, Base64Dialect.STANDARD); ByteBuf encoded = Base64.encode(src, true, Base64Dialect.STANDARD);
try { try {