유효한 Base64 인코딩 문자열을 확인하는 방법


127

C #에서 문자열이 Base 64로 인코딩되었는지 확인하는 방법이 있습니까? 변환을 시도하고 오류가 있는지 확인하는 것 외에는 무엇입니까? 다음과 같은 코드 코드가 있습니다.

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

값이 유효한 base 64 문자열이 아닌 경우 발생하는 "Invalid character in a Base-64 string"예외를 피하고 싶습니다. 때때로이 값이 기본 64 문자열이 아닐 것으로 예상하기 때문에 예외를 처리하는 대신 false를 확인하고 반환하고 싶습니다. 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);

}

테스트 대 catch 및 예외의 성능에 대해 궁금한 사람들을 위해 대부분의 경우이 base 64의 경우 특정 길이에 도달 할 때까지 예외를 포착하는 것보다 확인하는 것이 더 빠릅니다. 길이가 작을수록 빨라집니다

내 매우 비과학적인 테스트에서 : 문자 길이 100,000-110000에 대해 10000 번 반복하는 경우 먼저 테스트하는 것이 2.7 배 더 빨랐습니다.

총 16,000 개의 테스트에서 1-16 자 길이의 문자 길이에 대해 1000 번 반복하면 10.9 배 더 빨라졌습니다.

예외 기반 방법으로 테스트하는 것이 더 나아지는 지점이 있다고 확신합니다. 나는 그것이 어떤 시점인지 모른다.


1
수표를 얼마나 "철저히"원하는지에 따라 다릅니다. 다른 사람들이 대답했듯이 정규식을 사용하여 일부 사전 유효성 검사를 사용할 수 있지만 이것이 유일한 지표는 아닙니다. base64 인코딩에는 =기호를 사용하는 경우에 패딩이 필요합니다 . 패딩이 잘못되면 입력이 표현식과 일치하더라도 오류가 발생합니다.
vcsjones 2011-06-10

1
조건이 base64 문자열 만 충족하지 않습니다. 문자열을 고려하십시오 \n\fLE16-귀하의 방법은 이에 대해 거짓 긍정을 산출합니다. 읽기 쉽고 안전한 방법을 찾는 사람을 위해; FormatException을 잡거나 사양에 맞는 RegEx를 사용하는 것이 좋습니다 . stackoverflow.com/questions/475074/…를 참조하십시오 .
nullable

위의 메서드가 false를 반환하면 문자열을 올바른 길이로 어떻게 채울 수 있습니까?
폴 알렉산더

3
나는 RegEx가되어야한다고 믿는다@"^[a-zA-Z0-9\+/]*={0,2}$"
azatar

이 솔루션은 신뢰할 수 없습니다. 4 개의 동일한 문자열을 추가하면 실패합니다.
Bettimms

답변:


49

Base64 문자열은 문자로만 구성되며 'A'..'Z', 'a'..'z', '0'..'9', '+', '/'길이를 4의 배수로 만들기 위해 끝에 최대 3 개의 '='가 채워지는 경우가 많기 때문에 인식하기가 매우 쉽습니다 . d 예외가 발생하면 무시하는 것이 좋습니다.


1
나는 당신이 올바른 길을 가고 있다고 생각합니다. 몇 가지 테스트를했는데 3 대신 4의 배수 인 것 같습니다.
Chris Mullins

1
인코딩에 성공하려면 길이가 3의 배수 여야합니다. 죄송합니다 ... 그리고 맞습니다 ... 인코딩 된 문자열의 길이는 4의 배수입니다. 그래서 우리는 3 '='까지 채울 것입니다.
Anirudh Ramanathan 2011 년

4
여러 항목을 처음 언급했기 때문에 정답으로 표시되었습니다. 솔루션 구현으로 내 질문을 업데이트하여 문제가 있으면 알려주십시오.
Chris Mullins

48

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

1
그게 무슨 일인지 몰랐습니다. 나는 # 7.2 c를 사용하는 경우이 새로운 답해야한다고 생각
크리스 멀린스

4
.NET Core 2.1 이상 또는 .NET Standard 2.1 이상에서만 작동
Cyrus

C #은 컴파일러이고 TryFromBase64String은 .NET 프레임 워크의 API입니다. :)
user960567

패딩되지 않은 문자열에 대해 false를 반환 Convert.TryFromBase64String(base64.PadRight(base64.Length / 4 * 4 + (base64.Length % 4 == 0 ? 0 : 4), '='), new Span<byte>(new byte[base64.Length]), out _)합니다. 수정 사항은 다음과 같습니다 .. 감사합니다.
rvnlord

44

예외를 포착하고 싶지 않다고 말한 것을 알고 있습니다. 그러나 예외를 잡는 것이 더 신뢰할 수 있기 때문에 계속해서이 답변을 게시하겠습니다.

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 덕분에 상태를 업데이트했습니다 .


1
base64String.Contains여러 번 호출 base64String하면 큰 문자열 인 경우 성능이 저하 될 수 있습니다 .
NucS

@NucS 맞습니다. 여기서 컴파일 된 정규식을 사용할 수 있습니다.
harsimranb

1
당신은 확인할 수 base64String== null || base64String.Length == 0string.IsNullOrEmpty(base64String)
다니엘 TULP에게

Base64에는 문제없이 공백 (예 : 줄 바꿈)이 포함될 수 있습니다. 파서에서 무시됩니다.
Timothy

2
.NET 소스 코드에 액세스 할 수 있으므로 이제 FromBase64String () 함수가 이러한 모든 검사를 수행하는 것을 볼 수 있습니다. referencesource.microsoft.com/#mscorlib/system/… 유효한 base64 문자열이면 두 번 확인하는 것입니다. 예외를 시도 / 잡는 것이 더 안전 할 수 있습니다.
iheartcsharp

16

정규식은 다음과 같아야한다고 생각합니다.

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

3 개가 아닌 하나 또는 2 개의 후행 '='기호 만 일치합니다.

s검사 할 문자열이어야합니다. 네임 스페이스의 Regex일부입니다 System.Text.RegularExpressions.


2
문자열 길이가 mod 4 = 0인지 확인하지 않음
calingasan

7

왜 예외를 잡아서 False를 반환하지 않습니까?

이것은 일반적인 경우에 추가 오버 헤드를 방지합니다.


1
이것은 내가 값을 사용할 위치가 base 64가 아닐 가능성이 더 높기 때문에 예외의 오버 헤드를 피하고 싶습니다. 전에 확인하는 것이 훨씬 빠릅니다. 일반 텍스트 암호에서 상속 된 이전 시스템을 해시 된 값으로 변환하려고합니다.
Chris Mullins

2
정규식은 Tyler가 제안하는 것보다 빠르지 않습니다.
Vincent Koeman 2011 년

내 게시물 하단의 댓글을 참조하세요. 작업하는 문자열의 길이에 따라 특히 해시 된 암호와 같은 작은 문자열의 경우 먼저 테스트하는 것이 더 빠를 수 있다고 생각합니다. 정규식에 도달하려면 문자열이 4의 배수 여야하며 작은 문자열의 정규식은 매우 큰 문자열보다 빠릅니다.
Chris Mullins

2
완벽한 세상에서는 비즈니스 로직이 설계되었거나 예외를 던지는 것으로 알려진 코드를 작성해서는 안됩니다. 예외 try / catch 블록은 결정 블록으로 사용하기에는 너무 비쌉니다.
Ismail Hawayel 2018

7

완전성을 위해 일부 구현을 제공하고 싶습니다. 일반적으로 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;
    }
}

4

대답은 문자열의 사용법에 따라 달라져야합니다. 여러 포스터에서 제안한 구문에 따르면 "유효한 base64"일 수 있지만 예외없이 정크로 "올바르게"디코딩 할 수있는 문자열이 많이 있습니다. 예 : 8char 문자열Portland 은 유효한 Base64입니다. 이것이 유효한 Base64라는 점은 무엇입니까? 어느 시점에서이 문자열이 Base64로 디코딩되어야하는지 여부를 알고 싶을 것 같습니다.

제 경우에는 다음과 같은 일반 텍스트 일 ​​수있는 Oracle 연결 문자열이 있습니다.

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

또는 base64에서

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

세미콜론이 있는지 확인하기 만하면됩니다. 이는 그것이 base64가 아니라는 것을 증명하기 때문입니다. 물론 위의 방법보다 빠릅니다.


동의합니다. 케이스 세부 사항은 특정 추가 빠른 확인을 부과합니다. 일반 텍스트 연결 문자열과 base64로 인코딩 된 것과 같습니다.
Oybek 2014

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

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

먼저 변환 할 이유를 다음 다른 일을 제어
SNR

@ Snr 당신이 맞아요. 나는 이것이 그가 바꿀 필요가 있다고 생각한다. if (value.EndsWith ( "=")) {value = value.Trim (); int mod4 = value.Length % 4; if (mod4! = 0) {return false; } Convert.FromBase64String (값); true를 반환하십시오. } else {반환 거짓; }
Wajid 칸

2

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

2

디코딩, 다시 인코딩 및 결과를 원래 문자열과 비교

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

1

Imho 이것은 실제로 불가능합니다. 게시 된 모든 솔루션은 "test" 등과 같은 문자열에 대해 실패합니다 . 4로 나눌 수 있고 null이 아니거나 비어 있지 않고 유효한 base64 문자이면 모든 테스트를 통과합니다. 그것은 많은 문자열이 될 수 있습니다 ...

따라서 이것이 base 64로 인코딩 된 문자열이라는 것을 아는 것 외에는 실제적인 해결책이 없습니다 . 내가 생각 해낸 것은 다음과 같습니다.

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

디코딩 된 문자열이 특정 구조로 시작될 것으로 예상하므로 확인합니다.


0

확실한. 그냥 확인 각 문자 내에 있도록 a-z, A-Z, 0-9, /, 또는 +로, 그리고 문자열의 끝 ==. (적어도, 그것은 가장 일반적인 Base64로 구현합니다. 당신은 일부 구현에서 사용하는 문자 다른 사실을 발견 /또는 +마지막 두 문자.)


내가 이해했다면 끝 문자는 인코딩 된 텍스트의 최종 길이에 따라 다릅니다. 따라서 인코딩 된 텍스트가 길이 % 4가 아니면 '='가 포함됩니다.
Rafael Diego Nicoletti

0

예, Base64 부터 는 제한된 문자 집합을 사용하여 이진 데이터를 ASCII 문자열로 인코딩 다음 정규식으로 간단히 확인할 수 있습니다.

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

그러면 문자열에 AZ, az, 0-9, '+', '/', '='및 공백 만 포함됩니다.


그것이 항상 확실한 불 방법은 아닙니다. Base64는 =끝에 문자 를 사용하여 약간의 패딩을 수행합니다 . 해당 패딩이 유효하지 않으면 정규식과 일치하더라도 올바른 base64 인코딩이 아닙니다. =끝에 1 또는 2가있는 기본 64 문자열을 찾아서 제거하고 디코딩을 시도하여이를 데모 할 수 있습니다 .
vcsjones 2011-06-10

나는 OP가 str이 합법적 인 Base64가 아닌 불법 문자를 트랩하도록 요청했다고 생각합니다. 후자의 경우 맞지만 Base64의 패딩 오류는 예외를 사용하여 트랩하기가 더 쉽습니다.
Rob Raisch

사실이 아닙니다. 적어도 base64 파서의 .Net 버전은 패딩을 완전히 무시합니다.
Jay

0

작업을 수행하기 위해 정규식을 만드는 것이 좋습니다. 다음과 같은 것을 확인해야합니다. [a-zA-Z0-9 + / =] 또한 문자열의 길이를 확인해야합니다. 나는 이것에 대해 확신하지 못하지만, 무언가가 다듬어지면 (패딩 "="이외의) 그것이 터질 것이라고 확신합니다.

또는 이 stackoverflow 질문을 확인하십시오.


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

보시다시피 .NET을 <canvas>사용할 때 반환되는 기본값 인 image / png 유형 문자열이 필요합니다 .toDataURL().


0

Base64 또는 일반 문자열 확인

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

}


0

모든 답변은 결과가 정확함을 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;
                    return;
                }
                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;
                    return;
                }
            }
            else
            {
                return false;
                return;
            }

            return true;
            return;
        }
        catch (FormatException ex)
        {
            return false;
            return;
        }
    }

-1

정규식 확인 아이디어가 마음에 듭니다. 정규 표현식은 빠르며 때때로 코딩 오버 헤드를 줄일 수 있습니다. 원래 문의에는이 작업을 수행 한 업데이트가 있습니다. 그래도 문자열이 null이 아닐 것이라고 결코 가정 할 수 없습니다. 확장 기능을 확장하여 소스 문자열에서 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);

    }

이것은 실패합니다. 'aaaa'와 같은 4 개의 문자가있는 문자열을 전달하십시오.
Bettimms
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.