Comment vérifier la validité d'une chaîne encodée Base64

y a-t-il un moyen en C# de voir si une chaîne de caractères est encodée en base 64 autrement qu'en essayant de la convertir et de voir s'il y a une erreur? J'ai le code suivant:

// Convert base64-encoded hash value into a byte array.
byte[] HashBytes = Convert.FromBase64String(Value);

je veux éviter l'exception" caractère invalide dans une chaîne de base-64 " qui se produit si la valeur n'est pas une chaîne de base 64 valide. Je veux juste vérifier et retourner false au lieu de gérer une exception parce que je m'attends à ce que parfois cette valeur ne va pas être une chaîne de base 64. Est-il une façon de vérifier avant D'utiliser le Convert.Fonction FromBase64String?

Merci!

mise à Jour:

Merci pour toutes vos réponses. Voici une méthode d'extension que vous pouvez Tous utiliser jusqu'à présent, il semble pour s'assurer que votre chaîne passera convertir.FromBase64String sans exception. .NET semble ignorer tous les espaces de fin et de fin lors de la conversion en base 64 donc "1234" est valide et "1234" l'est aussi. "

public static bool IsBase64String(this string s)
{
    s = s.Trim();
    return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9+/]*={0,3}$", RegexOptions.None);

}

pour ceux qui s'interrogent sur la performance de l'essai par rapport à la capture et l'exception, dans la plupart des cas pour cette base 64 chose, il est plus rapide de vérifier que d'attraper l'exception jusqu'à ce que vous atteigniez une certaine longueur. Plus la longueur est petite plus il est plus rapide

dans mon test très peu scientifique: Pour 10000 itérations pour une longueur de caractère de 100 000 à 110000, il était 2,7 fois plus rapide de tester en premier.

pour 1000 itérations pour les caractères Longueur 1-16 caractères pour un total de 16 000 tests, il était 10,9 fois plus rapide.

je suis sûr qu'il y a un point où il devient préférable de tester avec la méthode basée sur les exceptions. Je ne sais pas à quelle point c'est tout.

81
demandé sur abatishchev 2011-06-10 20:31:29

15 réponses

il est assez facile de reconnaître une chaîne Base64, car elle ne sera composée que des caractères 'A'..'Z', 'a'..'z', '0'..'9', '+', '/' et elle est souvent rembourrée à la fin avec jusqu'à deux '=', pour faire la longueur un multiple de 4. Mais au lieu de comparer ces, vous seriez mieux d'ignorer l'exception, si elle se produit.

33
répondu Anirudh Ramanathan 2017-02-05 13:36:25

je sais que vous avez dit que vous ne vouliez pas attraper une exception. Mais, parce que prendre une exception est plus fiable, je vais aller de l'avant et poster cette réponse.

public static bool IsBase64(this string base64String) {
     // Credit: oybek https://stackoverflow.com/users/794764/oybek
     if (base64String== null || base64String.Length == 0 || base64String.Length % 4 != 0
        || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
        return false;

     try{
         Convert.FromBase64String(base64String);
         return true;
     }
     catch(Exception exception){
     // Handle the exception
     }
     return false;
}

mise à Jour: j'ai mis à jour l'état de santé grâce à oybek pour améliorer encore la fiabilité.

25
répondu harsimranb 2017-05-23 12:26:33

je crois que le regex devrait être:

    Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")

ne correspondant qu'à un ou deux signes"=", pas à trois.

s doit être la chaîne qui sera vérifié. Regex fait partie de l'espace de noms System.Text.RegularExpressions .

9
répondu jazzdev 2018-07-24 13:07:00

Pourquoi ne pas simplement intercepter l'exception, et retourne la valeur False?

cela évite des frais généraux supplémentaires dans le cas courant.

6
répondu Tyler Eaves 2011-06-10 16:41:54

juste par souci d'exhaustivité, je veux fournir une mise en œuvre. En général, Regex est une approche coûteuse, surtout si la chaîne est grande (ce qui arrive lors du transfert de gros fichiers). L'approche suivante tente d'abord les moyens de détection les plus rapides.

public static class HelperExtensions {
    // Characters that are used in base64 strings.
    private static Char[] Base64Chars = new[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
    /// <summary>
    /// Extension method to test whether the value is a base64 string
    /// </summary>
    /// <param name="value">Value to test</param>
    /// <returns>Boolean value, true if the string is base64, otherwise false</returns>
    public static Boolean IsBase64String(this String value) {

        // The quickest test. If the value is null or is equal to 0 it is not base64
        // Base64 string's length is always divisible by four, i.e. 8, 16, 20 etc. 
        // If it is not you can return false. Quite effective
        // Further, if it meets the above criterias, then test for spaces.
        // If it contains spaces, it is not base64
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;

        // 98% of all non base64 values are invalidated by this time.
        var index = value.Length - 1;

        // if there is padding step back
        if (value[index] == '=')
            index--;

        // if there are two padding chars step back a second time
        if (value[index] == '=')
            index--;

        // Now traverse over characters
        // You should note that I'm not creating any copy of the existing strings, 
        // assuming that they may be quite large
        for (var i = 0; i <= index; i++) 
            // If any of the character is not from the allowed list
            if (!Base64Chars.Contains(value[i]))
                // return false
                return false;

        // If we got here, then the value is a valid base64 string
        return true;
    }
}

MODIFIER

comme suggéré par Sam , vous pouvez également modifier légèrement le code source. Il fournit un approche plus performante pour la dernière étape des tests. La routine

    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;

        // 1 - 9
        if (intValue >= 48 && intValue <= 57) 
            return false;

        // A - Z
        if (intValue >= 65 && intValue <= 90) 
            return false;

        // a - z
        if (intValue >= 97 && intValue <= 122) 
            return false;

        // + or /
        return intValue != 43 && intValue != 47;
    } 

peut être utilisé pour remplacer if (!Base64Chars.Contains(value[i])) par if (IsInvalid(value[i]))

le code source complet avec les améliorations de Sam ressemblera à ceci (supprimé les commentaires pour plus de clarté)

public static class HelperExtensions {
    public static Boolean IsBase64String(this String value) {
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;
        var index = value.Length - 1;
        if (value[index] == '=')
            index--;
        if (value[index] == '=')
            index--;
        for (var i = 0; i <= index; i++)
            if (IsInvalid(value[i]))
                return false;
        return true;
    }
    // Make it private as there is the name makes no sense for an outside caller
    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;
        if (intValue >= 48 && intValue <= 57)
            return false;
        if (intValue >= 65 && intValue <= 90)
            return false;
        if (intValue >= 97 && intValue <= 122)
            return false;
        return intValue != 43 && intValue != 47;
    }
}
5
répondu Oybek 2017-05-23 12:26:33

La réponse doit dépendre de l'utilisation de la chaîne. Il y a beaucoup de chaînes qui peuvent être "valid base64" selon la syntaxe suggérée par plusieurs posters, mais qui peuvent "correctement" décoder, sans exception, à la camelote. Exemple: la chaîne de caractères Portland est valide Base64. Quel est l'intérêt d'affirmer qu'il s'agit d'une base valide64? Je suppose qu'à un moment donné vous voudriez savoir que cette chaîne devrait ou ne devrait pas être décodée Base64.

dans mon cas, J'ai Oracle les chaînes de connexion qui peuvent être en texte simple comme:

Data source=mydb/DBNAME;User Id=Roland;Password=.....`

ou en base64 comme

VXNlciBJZD1sa.....................................==

je dois juste vérifier la présence d'un point-virgule, Parce que cela prouve qu'il n'est pas base64, qui est bien sûr plus rapide que n'importe quelle méthode ci-dessus.

4
répondu Roland 2014-04-07 14:02:27

Knibb Haute règles du football!

cela devrait être relativement rapide et précis, mais j'admets que je n'ai pas fait un test complet, juste quelques-uns.

il évite les exceptions coûteuses, regex, et évite également la boucle à travers un jeu de caractères, en utilisant plutôt des gammes ascii pour la validation.

public static bool IsBase64String(string s)
    {
        s = s.Trim();
        int mod4 = s.Length % 4;
        if(mod4!=0){
            return false;
        }
        int i=0;
        bool checkPadding = false;
        int paddingCount = 1;//only applies when the first is encountered.
        for(i=0;i<s.Length;i++){
            char c = s[i];
            if (checkPadding)
            {
                if (c != '=')
                {
                    return false;
                }
                paddingCount++;
                if (paddingCount > 3)
                {
                    return false;
                }
                continue;
            }
            if(c>='A' && c<='z' || c>='0' && c<='9'){
                continue;
            }
            switch(c){ 
              case '+':
              case '/':
                 continue;
              case '=': 
                 checkPadding = true;
                 continue;
            }
            return false;
        }
        //if here
        //, length was correct
        //, there were no invalid characters
        //, padding was correct
        return true;
    }
1
répondu Jason K 2014-11-11 18:24:58
public static bool IsBase64String1(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return false;
            }
            try
            {
                Convert.FromBase64String(value);
                if (value.EndsWith("="))
                {
                    value = value.Trim();
                    int mod4 = value.Length % 4;
                    if (mod4 != 0)
                    {
                        return false;
                    }
                    return true;
                }
                else
                {

                    return false;
                }
            }
            catch (FormatException)
            {
                return false;
            }
        }
1
répondu user3181503 2015-04-21 19:13:24

je vais utiliser comme ceci pour que je n'ai pas besoin d'appeler à nouveau la méthode convert

   public static bool IsBase64(this string base64String,out byte[] bytes)
    {
        bytes = null;
        // Credit: oybek http://stackoverflow.com/users/794764/oybek
        if (string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0
           || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
            return false;

        try
        {
             bytes=Convert.FromBase64String(base64String);
            return true;
        }
        catch (Exception)
        {
            // Handle the exception
        }

        return false;
    }
1
répondu Yaseer Arafat 2015-06-04 22:15:19

bien sûr. Assurez-vous juste que chaque caractère est à l'intérieur a-z , A-Z , 0-9 , / , ou + , et la chaîne se termine par == . (Du moins, c'est la mise en œuvre de la Base64 la plus courante. Vous pouvez trouver des implémentations qui utilisent des caractères différents de / ou + pour les deux derniers caractères.)

0
répondu bdares 2011-06-10 16:35:29

Yes, since Base64 encodes des données binaires dans des chaînes ASCII en utilisant un ensemble limité de caractères, vous pouvez simplement le vérifier avec cette expression régulière:

/ ^[A-Za-z0-9\=\+\ / \S\n]+$/s

qui assure que la chaîne ne contient que A - Z, a-z, 0-9, '+', '/', '=', et whitespace.

0
répondu Rob Raisch 2011-06-10 16:36:46

je suggère de créer une regex pour faire le travail. Vous devez vérifier quelque chose comme ceci: [A-zA-Z0-9+ / =] Vous devrez aussi vérifier la longueur de la corde. Je ne suis pas sûr sur celui-ci, mais je suis presque sûr que si quelque chose est coupé (autre que le rembourrage "=") il exploserait.

ou mieux encore, cochez this stackoverflow question

0
répondu Jay 2017-05-23 12:34:44

j'aime l'Idée d'une Expression Régulière vérifier. Les Expressions régulières peuvent être rapides, et sauver la charge de codage parfois. l'enquête originale, avait une mise à jour qui a fait juste cela. Je trouve cependant, que je ne peux jamais supposer que les ficelles ne seraient pas nulles. Je voudrais étendre la fonction D'Extension pour vérifier la chaîne source pour null, ou des caractères blancs seulement.

    public static bool IsBase64String(this string s)
    {
        if (string.IsNullOrWhiteSpace(s))
            return false;

        s = s.Trim();
        return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);

    }
0
répondu Joseph 2015-02-06 19:43:08

je viens d'avoir une exigence très similaire où je laisse l'Utilisateur faire une manipulation d'image dans un élément <canvas> et puis envoyer l'image résultante récupérée avec .toDataURL() au backend. Je voulais faire une validation du serveur avant de sauvegarder l'image et j'ai implémenté un ValidationAttribute en utilisant une partie du code d'autres réponses:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class Bae64PngImageAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null || string.IsNullOrWhiteSpace(value as string))
            return true; // not concerned with whether or not this field is required
        var base64string = (value as string).Trim();

        // we are expecting a URL type string
        if (!base64string.StartsWith("data:image/png;base64,"))
            return false;

        base64string = base64string.Substring("data:image/png;base64,".Length);

        // match length and regular expression
        if (base64string.Length % 4 != 0 || !Regex.IsMatch(base64string, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None))
            return false;

        // finally, try to convert it to a byte array and catch exceptions
        try
        {
            byte[] converted = Convert.FromBase64String(base64string);
            return true;
        }
        catch(Exception)
        {
            return false;
        }
    }
}

comme vous pouvez le voir, je m'attends à une chaîne de caractères image/png, qui est la chaîne par défaut retournée par <canvas> lorsqu'on utilise .toDataURL() .

0
répondu germankiwi 2016-07-31 23:48:35

Imho ce n'est pas vraiment possible. toutes les solutions postées échouent pour les chaînes comme "test" et ainsi de suite. S'ils peuvent être divisés par 4, ne sont pas nuls ou vides, et s'ils sont un caractère de base64 valide, ils passeront tous les tests. Qui peut être de nombreuses chaînes ...

il n'y a donc pas de véritable solution autre que sachant qu'il s'agit d'une chaîne de caractères codée par la base 64 . De ce que j'ai, c'est ça:

if (base64DecodedString.StartsWith("<xml>")
{
    // This was really a base64 encoded string I was expecting. Yippie!
}
else
{
    // This is gibberish.
}

Je m'attends à ce que la chaîne décodée commence par une certaine structure, donc je vérifie.

0
répondu testing 2017-04-26 17:16:52