2011-06-10 165 views
71

有沒有在C#中看到一個字符串是否是Base 64編碼的方法,而不是隻是試圖轉換它,看看是否有錯誤?我有代碼這樣的代碼:如何檢查一個有效的Base64編碼字符串

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

我要避免「一中的Base-64字符串的字符無效」的異常出現這種情況,如果值是無效的基地64串。我只想檢查並返回false而不是處理異常,因爲我期望有時這個值不會是基本的64字符串。在使用Convert.FromBase64String函數之前有什麼方法可以檢查嗎?

謝謝!

更新:
感謝您的所有答案。這裏是一個你可以使用的擴展方法,目前爲止,它似乎確保你的字符串將傳遞Convert.FromBase64String沒有例外。 .NET似乎將立足64,「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); 

} 

對於那些想知道關於測試的性能VS捕捉和例外,在大多數情況下這個基礎64的事情比檢查異常直到你達到一定的長度檢查要快。

在我非常不科學的測試中: 對於字符長度爲100,000 - 110000的10000次迭代,測試速度是第一次測試的2.7倍。

對於1000個字符長度的迭代,1-16個字符對於總共16000個測試來說,速度提高了10.9倍。

我確定有一點可以使用基於異常的方法進行測試。我只是不知道那是什麼。

+1

這取決於你想要檢查的「徹底」。您可以使用正則表達式進行一些預驗證,如其他人已經回答的那樣,但這不是唯一的指標。 base64編碼在某些情況下需要使用「=」符號進行填充。如果填充錯誤,即使輸入與表達式匹配,也會發生錯誤。 – vcsjones 2011-06-10 16:41:48

+0

您的情況並不完全滿足base64字符串。考慮字符串'\ n \ fLE16' - 你的方法會產生誤判。對於任何人閱讀和尋找一種萬無一失的方法;我會建議捕捉FormatException或使用特定的RegEx,請參閱http://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data。 – nullable 2017-05-18 20:21:27

+0

如果上述方法返回false,如何將字符串填充到正確的長度? – 2017-07-07 09:55:36

回答

30

它很容易識別Base64字符串,因爲它只會被編寫的字符'A'..'Z', 'a'..'z', '0'..'9', '+', '/',它通常在最後以兩個'='填充,以使長度爲4的倍數。但是不要比較這些字符,如果它發生,最好忽略異常。

+1

我認爲你是在正確的軌道上。我做了一些測試,看起來它是4的倍數而不是3。 – 2011-06-10 17:24:10

+1

它的長度需要是編碼時的3的倍數,以便編碼成功!對不起... ...是的,你是對的...編碼字符串的長度是4的倍數。這就是爲什麼我們要填充3'='。 – 2011-06-10 17:27:24

+4

標記爲正確,因爲您首先提到了多重事件。如果您發現任何問題,我通過實施解決方案更新了我的問題。 – 2011-06-10 18:05:25

0

我會建議創建一個正則表達式來完成這項工作。 你必須檢查這樣的事情:[a-zA-Z0-9 +/=] 你還必須檢查字符串的長度。我不確定這一個,但我很確定是否有東西被修剪(除了填充「=」)它會炸燬。

或更好的結算this stackoverflow question

0

當然可以。只要確保每個字符在a-z,A-Z,0-9,/+之內,並且字符串以==結尾。 (至少,這是最常見的Base64實現,你可能會發現一些實現使用與/+不同的字符作爲最後兩個字符)。

0

是的,因爲Base64二進制數據編碼成ASCII字符串使用一組有限的字符,可以簡單地與該正則表達式檢查:

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

這將確保字符串只包含AZ,az,0-9,'+','/','='和空格。

+0

這並不總是一個可靠的解釋方式。 Base64在最後使用'='字符爲你做一些填充。如果該填充無效,則它不是正確的base64編碼,即使它與您的正則表達式匹配。您可以通過在末尾找到一個帶有1或2'='的基本64字符串來演示它,然後將其刪除並嘗試對其進行解碼。 – vcsjones 2011-06-10 16:40:15

+0

我相信OP要求陷害非法角色,如果str是合法的Base64,則不行。如果是後者,你是正確的,儘管Base64中的填充錯誤更容易使用異常來捕獲。 – 2011-06-10 16:42:08

+0

不正確,至少base64分析器的.Net版本會完全忽略填充。 – Jay 2011-06-10 16:44:43

6

爲什麼不只是捕捉異常並返回False?

這可以避免常見情況下的額外開銷。

+1

這是一個不尋常的情況,我想我要去哪裏使用該值更可能不是基數64,所以我寧願避免異常的開銷。以前檢查要快得多。我試圖將我從明文密碼繼承的舊系統轉換爲哈希值。 – 2011-06-10 17:22:55

+1

正則表達式永遠不會比泰勒建議的速度快。 – 2011-06-10 17:33:29

+0

查看我的帖子底部的評論。我認爲,根據您使用的字符串的長度,首先測試會更快,尤其是對於像散列密碼這樣的小字符串。該字符串必須是4的倍數才能進入正則表達式,然後對一個小字符串的正則表達式比對一個非常大的字符串要快。 – 2011-06-10 18:25:18

21

我知道你說過你不想發生異常。但是,因爲捕捉異常更可靠,我會繼續併發布這個答案。

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

更新:我的狀態由於更新爲oybek,進一步提高了可靠性。

+0

多次調用'base64String.Contains'可能導致'base64String'成爲一個大字符串,導致性能不佳。 – NucS 2015-07-23 11:46:09

+0

@NucS你說得對,我們可以在這裏使用一個編譯好的正則表達式。 – harsimranb 2016-01-14 19:21:52

+1

你可以檢查'base64String == null || base64String.Length == 0'與'string.IsNullOrEmpty(base64String)' – 2017-04-13 13:54:19

4

答案必須取決於字符串的用法。根據幾個海報提出的語法,有很多字符串可能是「有效的base64」,但可能「正確」解碼,無一例外地是垃圾。例如:8char字符串Portland是有效的Base64。說這是有效的Base64有什麼意義?我想在某些時候你想知道這個字符串應該或不應該被Base64解碼。

在我的情況,我有Oracle連接字符串可以是純文本,如:

​​

或類似

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

的base64我只是來檢查分號的存在,因爲這證明它不是base64,這當然比上述任何方法都快。

+0

同意,具體案件也施加一些額外的快速檢查。就像明文連接字符串vs base64編碼一樣。 – Oybek 2014-11-24 05:11:24

3

只是爲了完整起見,我想提供一些實現。 一般來說,正則表達式是一種昂貴的方法,特別是如果字符串很大(在傳輸大文件時會發生這種情況)。以下方法首先嚐試最快的檢測方式。

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

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

我喜歡正則表達式檢查的想法。正則表達式可以很快,並且有時節省編碼開銷。原來的詢問,有一個更新,就是這樣做的。我發現雖然,我永遠不能假定字符串不會爲空。我將展開擴展函數來檢查源字符串是否爲空字符或僅空白字符。

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

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

我相信,正則表達式應該是:

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

只有匹配一個或兩個尾 '=' 的標誌,而不是三個。

1

我會用這樣讓我不需要調用轉換方法再次

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

正如你可以看到我期待的圖像/ PNG類型的字符串,這是默認當使用.toDataURL()時由<canvas>返回。

0

Imho這不是真的可能。所有發佈的解決方案都失敗,如「測試」等字符串。如果它們可以通過4分割,不爲空或空,並且如果它們是有效的base64字符,則它們將通過所有測試。這可能是很多字符串...

因此,除了知道這是一個基本的64編碼字符串,以外,沒有真正的解決方案。我想到的是這樣的:

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

我認爲解碼後的字符串以特定的結構開始,所以我檢查一下。

相關問題