Longitud no válida para una matriz de caracteres Base-64

91

Como dice el título, obtengo:

Longitud no válida para una matriz de caracteres Base-64.

He leído sobre este problema aquí y parece que la sugerencia es almacenar ViewState en SQL si es grande. Estoy usando un asistente con una gran cantidad de recopilación de datos, por lo que es probable que mi ViewState sea grande. Pero, antes de pasar a la solución "almacenar en base de datos", ¿quizás alguien pueda echar un vistazo y decirme si tengo otras opciones?

Construyo el correo electrónico para la entrega utilizando el siguiente método:

public void SendEmailAddressVerificationEmail(string userName, string to)
{
    string msg = "Please click on the link below or paste it into a browser to verify your email account.<BR><BR>" +
                    "<a href=\"" + _configuration.RootURL + "Accounts/VerifyEmail.aspx?a=" +
                    userName.Encrypt("verify") + "\">" +
                    _configuration.RootURL + "Accounts/VerifyEmail.aspx?a=" +
                    userName.Encrypt("verify") + "</a>";

    SendEmail(to, "", "", "Account created! Email verification required.", msg);
}

El método Encrypt se ve así:

public static string Encrypt(string clearText, string Password)
{

    byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);

    PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });


    byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));

    return Convert.ToBase64String(encryptedData);
}

Así es como se ve el HTML en hotmail:

Haga clic en el enlace a continuación o péguelo en un navegador para verificar su cuenta de correo electrónico.

http: // localhost: 1563 / Cuentas / VerifyEmail.aspx? a = YOHY57xYRENEOu3H + FGq1Rf09AZAI56EPjfwuK8XWKg =

En el extremo receptor, la página VerifyEmail.aspx.cs tiene la línea:

 string username = Cryptography.Decrypt(_webContext.UserNameToVerify, "verify");

Aquí está el captador de UserNameToVerify:

public string UserNameToVerify
{
    get
    {
        return GetQueryStringValue("a").ToString();
    }
}

Y aquí está el método GetQueryStringValue:

private static string GetQueryStringValue(string key)
{
    return HttpContext.Current.Request.QueryString.Get(key);
}

Y el método de descifrado se ve así:

public static string Decrypt(string cipherText, string password)
{

    **// THE ERROR IS THROWN HERE!!**
    byte[] cipherBytes = Convert.FromBase64String(cipherText);

¿Se puede solucionar este error con una corrección de código o debo almacenar ViewState en la base de datos?

Pedro
fuente

Respuestas:

205

La longitud de una cadena codificada en base64 es siempre un múltiplo de 4. Si no es un múltiplo de 4, =se añaden caracteres hasta que lo sea. Una cadena de consulta del formulario ?name=valuetiene problemas cuando valuecontiene caracteres =(algunos de ellos se eliminarán, no recuerdo el comportamiento exacto). Es posible que pueda salirse con la suya agregando el número correcto de =caracteres antes de realizar la decodificación base64.

Editar 1

Puede encontrar que el valor de UserNameToVerifyha tenido "+"'s ha cambiado a " "' s, por lo que es posible que deba hacer algo así:

a = a.Replace(" ", "+");

Esto debería tener la longitud correcta;

int mod4 = a.Length % 4;
if (mod4 > 0 )
{
    a += new string('=', 4 - mod4);
}

Por supuesto, llamar UrlEncode(como en la respuesta de LukeH) debería hacer que todo esto sea discutible.

Vadim Ovchinnikov
fuente
9
Gracias Brad. En realidad, fue este pequeño código el que hizo el trabajo: a = a.Replace ("", "+");
Peter
1
@Code Sherpa: si ese es el caso, su mejor opción es urlencode antes de enviar la cadena y urldecode al recibirlo. De lo contrario, si otro carácter significativo de la URL entra en su cadena, tendrá que agregar otra Replacedeclaración. La codificación es un mono que te protege independientemente.
Matt Ellen
5
No debe UrlDecode su cadena al recibirla, ya que los parámetros de solicitud ya están UrlDecoded por ASP.Net. Sin embargo, debe UrlEncode al enviar.
bleeeah
O si desea una versión en línea: a = a + new string('=', (4 - a.Length % 4) % 4). Ejemplo para decodificar RFC 4648 Base64 segura para URL :public string base64urlDecode(string encoded) { return System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(encoded.Replace("_","/").Replace("-","+") + new string('=', (4 - encoded.Length % 4) % 4))); }
gregmac
1
"No debe utilizar UrlDecode" - ¡ESTO! Al revisar mi código, pude ver que el parámetro ya estaba decodificado, el problema era que lo ejecutaba y UrlDecodeque estaba eliminando caracteres. Gracias @MattEllen
GJKH
30

Supongo que simplemente necesita codificar en URL su cadena Base64 cuando la incluye en la cadena de consulta.

La codificación Base64 utiliza algunos caracteres que deben ser codificados si son parte de una cadena de consulta (es decir, +e /, y tal vez =también). Si la cadena no está codificada correctamente, no podrá decodificarla correctamente en el otro extremo, de ahí los errores.

Puede usar el HttpUtility.UrlEncodemétodo para codificar su cadena Base64:

string msg = "Please click on the link below or paste it into a browser "
             + "to verify your email account.<br /><br /><a href=\""
             + _configuration.RootURL + "Accounts/VerifyEmail.aspx?a="
             + HttpUtility.UrlEncode(userName.Encrypt("verify")) + "\">"
             + _configuration.RootURL + "Accounts/VerifyEmail.aspx?a="
             + HttpUtility.UrlEncode(userName.Encrypt("verify")) + "</a>";
LukeH
fuente
Gracias. Intenté tu sugerencia, Luke, pero no funcionó :(.
Peter
@Sherpa: sigue trabajando, el problema es casi seguro con los =caracteres finales .
Luke: tengo la sensación de que tienes razón. Probaré esto en casa. Gracias un paquete. Para su información, agregué cómo se ve la cadena en mi bandeja de entrada de Hotmail en mi publicación original.
Peter
el tío brad tiene razón, tuve el mismo problema la semana pasada y el problema era un carácter "=" final ._.
Marcote
10

No soy lo suficientemente reputado como para votar o comentar todavía, pero la respuesta de LukeH fue acertada para mí.

Como el cifrado AES es el estándar para usar ahora, produce una cadena base64 (al menos todas las implementaciones de cifrado / descifrado que he visto). Esta cadena tiene una longitud en múltiplos de 4 (string.length% 4 = 0)

Las cadenas que estaba obteniendo contenían + y = al principio o al final, y cuando las concatena en la cadena de consulta de una URL, se verá bien (por ejemplo, en un correo electrónico que genere), pero cuando se siga el enlace y el La página .NET lo recibe y lo coloca en this.Page.Request.QueryString, esos caracteres especiales desaparecerán y la longitud de su cadena no será múltiplo de 4.

Como hay caracteres especiales al FRENTE de la cadena (por ejemplo: +), así como = al final, no puede simplemente agregar algunos = para compensar la diferencia, ya que está alterando el texto cifrado de una manera que no lo hace. no coincide con lo que estaba realmente en la cadena de consulta original.

Por lo tanto, envolver el texto cifrado con HttpUtility.URLEncode (no HtmlEncode) transforma los caracteres no alfanuméricos de una manera que garantiza que .NET los vuelva a analizar en su estado original cuando se interprete en la colección de cadenas de consulta.

Lo bueno es que solo necesitamos hacer el código URLEncode al generar la cadena de consulta para la URL. En el lado entrante, se traduce automáticamente al valor de cadena original.

Aquí hay un código de ejemplo

string cryptostring = MyAESEncrypt(MySecretString);
string URL = WebFunctions.ToAbsoluteUrl("~/ResetPassword.aspx?RPC=" + HttpUtility.UrlEncode(cryptostring));
Ken Forslund
fuente
6

Mi suposición inicial sin conocer los datos sería que UserNameToVerify no es un múltiplo de 4 de longitud. Consulte FromBase64String en msdn.

// Ok
byte[] b1 = Convert.FromBase64String("CoolDude");
// Exception
byte[] b2 = Convert.FromBase64String("MyMan");
SwDevMan81
fuente
Gracias SwDevMan81. Acabo de salir del trabajo, pero lo intentaré más tarde esta noche. Gracias por tu ayuda.
Peter
No hay problema, la solución sería rellenar con un carácter para obtener una cadena que sea un múltiplo de 4.
SwDevMan81
Gracias de nuevo SwDevMan81. Echaré un vistazo a eso. Publiqué UserNameToVeryify en mi publicación original (FYI). OK ... ahora realmente necesito irme o me voy a meter en problemas con el verdadero jefe :)
Peter
Parece que esta publicación también podría ayudar: stackoverflow.com/questions/1392970/…
SwDevMan81
1

La cadena cifrada tenía dos caracteres especiales +y =.

El signo '+' estaba dando el error, por lo que la siguiente solución funcionó bien:

//replace + sign

encryted_string = encryted_string.Replace("+", "%2b");

//`%2b` is HTTP encoded string for **+** sign

O

//encode special charactes 

encryted_string = HttpUtility.UrlEncode(encryted_string);

//then pass it to the decryption process
...
Vikrant
fuente
0
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 
Código
fuente