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

Chr*_*ins 113 c# validation base64

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

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

我想避免如果该值不是有效的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);

}
Run Code Online (Sandbox Code Playgroud)

对于那些对测试性能与捕获和异常感到疑惑的人来说,在大多数情况下对于这个基础64事物来说,检查比捕获异常直到达到一定长度更快.它的长度越小越快

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

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

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

Ani*_*han 44

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

  • 标记正确,因为你是第一个提到多件事.我通过解决方案的实现更新了我的问题,如果您发现任何问题,请告诉我. (4认同)

har*_*anb 34

我知道你说你不想抓一个例外.但是,因为捕获异常更可靠,我将继续并发布此答案.

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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 如果“base64String”是一个大字符串,多次调用“base64String.Contains”可能会导致性能不佳。 (2认同)
  • 由于我们现在可以访问 .NET 源代码,我们可以看到 FromBase64String() 函数执行所有这些检查。https://referencesource.microsoft.com/#mscorlib/system/convert.cs,08c34f52087ba624 如果它是一个有效的 base64 字符串,那么你要检查它两次。尝试/捕获异常可能更便宜。 (2认同)

Tom*_*bes 27

从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);
}
Run Code Online (Sandbox Code Playgroud)

  • 仅适用于 .NET Core 2.1+ 或 .NET Standard 2.1+ (6认同)
  • 对于非填充字符串,这将返回 false,这里有一个修复: `Convert.TryFromBase64String(base64.PadRight(base64.Length / 4 * 4 + (base64.Length % 4 == 0 ? 0 : 4), '=' ), new Span&lt;byte&gt;(new byte[base64.Length]), out _)`。谢谢。 (4认同)
  • 我不知道那是一回事。我认为这应该是新的答案,如果使用 c# 7.2 (2认同)
  • 就其价值而言,“.FromBase64String”在幕后使用此方法,并且如果对“.TryFromBase64String”的底层调用返回“false”,则简单地引发异常。唯一的区别是“.FromBase64String”从输入中删除尾随空格,因此如果您之前依赖它来自动清理输入字符串,则可能会遇到一些问题。 (2认同)

jaz*_*dev 14

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

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

只匹配一个或两个尾随'='符号,而不是三个.

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

  • 不检查字符串长度是否为 4 = 0 (4认同)

Tyl*_*ves 7

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

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

  • 在一个完美的世界中,不应编写业务逻辑被设计为或已知会引发异常的代码。异常 try/catch 块过于昂贵,无法用作决策块。 (4认同)
  • 正则表达式永远不会比Tyler所建议的快。 (2认同)

Rol*_*and 6

答案必须取决于字符串的用法。根据几位发帖人建议的语法,许多字符串可能是“有效的 base64”,但毫无例外地可能“正确”解码为垃圾。示例:8char 字符串Portland是有效的 Base64。声明这是有效的 Base64 有何意义?我想在某些时候您会想知道这个字符串应该或不应该进行 Base64 解码。

就我而言,我正在从文件 app.config 中读取 Oracle 连接字符串,该字符串可能是纯文本形式,例如:

Data source=mydb/DBNAME;User Id=Roland;Password=secret1;
Run Code Online (Sandbox Code Playgroud)

或类似base64

VXNlciBJZD1sa.....................................==
Run Code Online (Sandbox Code Playgroud)

(我的前任认为 base64 是加密:-)

为了确定是否需要进行 Base64 解码,在这个特定的用例中,我应该简单地检查字符串是否以“Data”开头(不区分大小写)。这比仅仅尝试解码并查看是否发生异常更容易、更快、更可靠:

if (ConnectionString.Substring(0, 4).ToLower() != "data")
{
  //..DecodeBase64..
}
Run Code Online (Sandbox Code Playgroud)

我更新了这个答案;我以前的结论是:

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


Oyb*_*bek 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

正如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;
    } 
Run Code Online (Sandbox Code Playgroud)

可以用来替换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;
    }
}
Run Code Online (Sandbox Code Playgroud)


PKO*_*KOS 5

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

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;
}
Run Code Online (Sandbox Code Playgroud)