如何检查有效的Base64编码字符串

时间:2011-06-10 16:31:30

标签: c# validation base64

在C#中是否有办法查看字符串是否为Base 64编码,而不仅仅是尝试转换它并查看是否有错误?我有这样的代码:

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

如果值不是有效的base 64字符串,我想避免出现“Base-64字符串中的无效字符”异常。我想检查并返回false而不是处理异常,因为我希望有时这个值不会是一个基本的64字符串。在使用Convert.FromBase64String函数之前有没有办法检查?

谢谢!

更新
谢谢你的所有答案。这是一个你可以使用的扩展方法到目前为止似乎确保你的字符串将传递Convert.FromBase64String而没有异常。当转换为base 64时,.NET似乎忽略所有尾随和结尾空格,因此“1234”有效,因此“1234”

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);

}

对于那些想知道测试与捕获和异常的性能的人来说,在大多数情况下,对于这个基础64事物,检查比捕获异常要快到达一定长度。它的长度越短

在我非常不科学的测试中: 对于字符长度100,000 - 110000的10000次迭代,它首先测试快2.7倍。

对于字符长度为1到16个字符的1000次迭代,总共16,000次测试,它的速度提高了10.9倍。

我确信有一点可以更好地使用基于异常的方法进行测试。我只是不知道那是什么意思。

20 个答案:

答案 0 :(得分:40)

识别Base64字符串非常容易,因为它只由字符'A'..'Z', 'a'..'z', '0'..'9', '+', '/'组成,并且通常在最后用最多两个'='填充,以使长度为4的倍数。但是,如果发生异常,你最好忽略异常,而不是比较它们。

答案 1 :(得分:31)

我知道你说过你不想发现异常。但是,因为捕获异常更可靠,我会继续发布这个答案。

public static bool IsBase64(this string base64String) {
     // Credit: oybek https://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{
         Convert.FromBase64String(base64String);
         return true;
     }
     catch(Exception exception){
     // Handle the exception
     }
     return false;
}

更新:由于oybek,我已更新了条件,以进一步提高可靠性。

答案 2 :(得分:14)

我相信正则表达式应该是:

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

仅匹配一个或两个尾随' ='标志,而不是三个。

s应该是要检查的字符串。 RegexSystem.Text.RegularExpressions命名空间的一部分。

答案 3 :(得分:9)

使用C#7.2中的Convert.TryFromBase64String

public static bool IsBase64String(string base64)
{
   Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
   return Convert.TryFromBase64String(base64, buffer , out int bytesParsed);
}

答案 4 :(得分:7)

为什么不捕获异常,并返回False?

这避免了常见情况下的额外开销。

答案 5 :(得分:6)

为了完整起见,我想提供一些实现。 一般来说,Regex是一种昂贵的方法,特别是如果字符串很大(在传输大文件时会发生)。以下方法首先尝试最快的检测方法。

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;
    }
}

修改

根据Sam的建议,您也可以稍微更改源代码。他为最后一步测试提供了更好的表现方法。例程

    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;
    } 

可用于将if (!Base64Chars.Contains(value[i]))行替换为if (IsInvalid(value[i]))

包含Sam增强功能的完整源代码如下所示(为清晰起见,删除了评论)

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;
    }
}

答案 6 :(得分:4)

答案必须取决于字符串的用法。根据几张海报建议的语法,有许多字符串可能是“有效的base64”,但可以“正确地”解码,无一例外地是垃圾。示例:8char字符串Portland是有效的Base64。说明这是有效的Base64有什么意义?我想在某些时候你想知道这个字符串应该或不应该被Base64解码。

就我而言,我有Oracle连接字符串,可能是纯文本,如:

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

或在base64中

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

我只需要检查是否存在分号,因为这证明它不是base64,这当然比上述方法更快。

答案 7 :(得分:2)

Knibb高级足球规则!

这应该相对快速和准确但我承认我没有通过彻底的测试,只有少数。

它避免了昂贵的异常,正则表达式,也避免了循环字符集,而是使用ascii范围进行验证。

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;
    }

答案 8 :(得分:2)

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;
            }
        }

答案 9 :(得分:2)

我将这样使用,以便我不需要再次调用转换方法

   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;
    }

答案 10 :(得分:1)

我更喜欢这种用法:

    public static class StringExtensions
    {
        /// <summary>
        /// Check if string is Base64
        /// </summary>
        /// <param name="base64"></param>
        /// <returns></returns>
        public static bool IsBase64String(this string base64)
        {
            //https://stackoverflow.com/questions/6309379/how-to-check-for-a-valid-base64-encoded-string
            Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
            return Convert.TryFromBase64String(base64, buffer, out int _);
        }
    }

然后使用

if(myStr.IsBase64String()){

    ...

}

答案 11 :(得分:1)

所有答案均被汇总为1个函数,可确保100%的结果是准确的。


1)使用以下功能:

    string encoded = "WW91ckJhc2U2NHN0cmluZw==";
    msgbox("Is string base64=" + IsBase64(encoded));

2)下面是该功能:

  public bool IsBase64(string base64String)
    {
        try
        {
            if (!base64String.Length < 1)
            {
                if (!base64String.Equals(Convert.ToBase64String(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(Convert.FromBase64String(base64String)))), StringComparison.InvariantCultureIgnoreCase) & !System.Text.RegularExpressions.Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,2}$"))
                {
                    return false;
                }
                if ((base64String.Length % 4) != 0 || string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0 || base64String.Contains(" ") || base64String.Contains(Constants.vbTab) || base64String.Contains(Constants.vbCr) || base64String.Contains(Constants.vbLf))
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
                
            return true;
        }
        catch (FormatException ex)
        {
            return false;
        }
    }

答案 12 :(得分:1)

Imho这是不可能的。所有发布的解决方案都失败了,如“test”等字符串。如果它们可以被划分为4,则不为null或为空,如果它们是有效的base64字符,则它们将通过所有测试。这可能是很多字符串......

除了知道这是一个基本的64位编码字符串之外,没有真正的解决方案。我想出的是:

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

我希望解码后的字符串以某种结构开头,所以我会检查它。

答案 13 :(得分:0)

我刚才有一个非常相似的要求,我让用户在<canvas>元素中进行一些图像处理,然后将用.toDataURL()检索到的结果图像发送到后端。我想在保存图像之前进行一些服务器验证,并使用其他答案中的一些代码实现了ValidationAttribute

[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;
        }
    }
}

如您所见,我期待一个image / png类型字符串,这是<canvas>使用.toDataURL()时返回的默认字符串。

答案 14 :(得分:0)

我喜欢正则表达式检查的想法。正则表达式可以很快,有时可以节省编码开销。原始调查,有一个更新,做到这一点。我发现,我永远不会认为字符串不会为空。我会扩展Extension函数以检查源字符串是否为null,或者只检查空白字符。

    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);

    }

答案 15 :(得分:0)

是的,由于Base64使用一组有限的字符将二进制数据编码为ASCII字符串,您可以使用此正则表达式进行检查:

/ ^ [A-ZA-Z0-9 \ = \ + \ / \ S \ n]的+ $ / S

这将确保字符串仅包含A-Z,a-z,0-9,'+','/','='和空格。

答案 16 :(得分:0)

进行解码,重新编码并将结果与​​原始字符串进行比较

public static Boolean IsBase64(this String str)
{
    if ((str.Length % 4) != 0)
    {
        return false;
    }

    //decode - encode and compare
    try
    {
        string decoded = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(str));
        string encoded = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(decoded));
        if (str.Equals(encoded, StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }
    catch { }
    return false;
}

答案 17 :(得分:0)

检查Base64或普通字符串

公共布尔IsBase64Encoded(String str)

{

try

{
    // If no exception is caught, then it is possibly a base64 encoded string
    byte[] data = Convert.FromBase64String(str);
    // The part that checks if the string was properly padded to the
    // correct length was borrowed from d@anish's solution
    return (str.Replace(" ","").Length % 4 == 0);
}
catch
{
    // If exception is caught, then it is not a base64 encoded string
   return false;
}

}

答案 18 :(得分:0)

不确定。只需确保每个字符都在a-zA-Z0-9/+内,字符串以==结尾。 (至少,这是最常见的Base64实现。您可能会发现一些实现使用与/+不同的字符来表示最后两个字符。)

答案 19 :(得分:0)

我建议创建一个正则表达式来完成这项工作。 你必须检查这样的事情:[a-zA-Z0-9 + / =] 您还必须检查字符串的长度。我不确定这个,但我很确定如果某些东西被修剪(除了填充“=”)它会爆炸。

或者更好的是查看this stackoverflow question