NSData не будет принимать действительную строку с кодировкой base64

Я использую аутентификацию JSON Web Token на стороне iOS (7). Он работает красиво. Мое приложение вводит токены и может выполнять аутентифицированные вызовы на моем сервере с ними.

Теперь я хочу, чтобы мой код на стороне клиента проверял дату истечения срока действия на токене, чтобы он мог знать, когда нужно повторно аутентифицировать. Проверка даты истечения срока действия на токере JWT auth проста. Ток авторизации - это 3 кодированные JSON-кодированные base64, разделенные символом '.' - Временная метка истечения находится в среднем блобе в поле под названием ext. Это секунды с момента unix.

Итак, мой код выглядит так:

- (NSDate*) expirationDate
{
    if ( !_tokenAppearsValid ) return nil;

    if ( !_parsedExpirationDate )
    {
        //
        //  Token is three base64 encoded payloads separated by '.'
        //  The payload we want is the middle one, which is a JSON dict, with
        //  'exp' being the unix seconds timestamp of the expiration date
        //  Returning nil is appropriate if no 'exp' is findable
        //

        NSArray *components = [self.token componentsSeparatedByString:@"."];

        NSString *payload = components[1];

        NSData* payloadJsonData = [[NSData alloc]
            initWithBase64EncodedString:payload
            options:NSDataBase64DecodingIgnoreUnknownCharacters];

        NSError* jsonError = nil;
        NSDictionary* payloadJson = [NSJSONSerialization JSONObjectWithData:payloadJsonData options:0 error:&jsonError];
        if ( payloadJson )
        {
            if ( payloadJson[@"exp"] )
            {
                NSTimeInterval timestampSeconds = [payloadJson[@"exp"] doubleValue];
                _expirationDate = [NSDate dateWithTimeIntervalSince1970:timestampSeconds];
            }
        }

        _parsedExpirationDate = YES;
    }

    return _expirationDate;
}

Проблема проста. Среднее значение base64 blob при анализе NSData -initWithBase64EncodedString nil - и это плохо.

Я проверил blob base64, и он кажется действительным. Мой сервер возвращает фиктивные данные на данный момент, так что вот пример blob:   eyJlbWFpbCI6ImZvb0BiYXIuYmF6IiwiYWNjb3VudElkIjoiMTIzNDUtNjc4OTAtYmFyLWJheiIsImV4cCI6MTM5MDkxNTAzNywiaWF0IjoxMzkwOTE0MTM3fQ

Он декодирует:

{"email":"[email protected]","accountId":"12345-67890-bar-baz","exp":1390915037,"iat":1390914137}

Я протестировал его здесь: http://www.base64decode.org

Я использовал NSData base64 методы elswhere в моем приложении с успехом - я не думаю, что я делаю что-то особенно сломанное здесь. Но я все уши! Любые идеи?

Ответы

Ответ 1

Ваша строка Base64 недопустима. Он должен быть дополнен символами =, чтобы длина, кратная 4. В вашем случае: "eyJlbWFp....MTM3fQ==".

С этим дополнением initWithBase64EncodedString правильно декодирует строку Base64.

Ответ 2

Хотя ответ Мартина верен, вот быстрый и правильный (!) способ устранить проблему:

NSString *base64String = @"<the token>";
NSUInteger paddedLength = base64String.length + (4 - (base64String.length % 4));
NSString* correctBase64String = [base64String stringByPaddingToLength:paddedLength withString:@"=" startingAtIndex:0];

Ответ 3

Вот решение, которое правильно накладывает строку Base-64 и работает в iOS 4 +:

NSData + Base64.h

@interface NSData (Base64)

/**
 Returns a data object initialized with the given Base-64 encoded string.
 @param base64String A Base-64 encoded NSString
 @returns A data object built by Base-64 decoding the provided string. Returns nil if the data object could not be decoded.
 */
- (instancetype) initWithBase64EncodedString:(NSString *)base64String;

/**
 Create a Base-64 encoded NSString from the receiver contents
 @returns A Base-64 encoded NSString
 */
- (NSString *) base64EncodedString;

@end

NSData + Base64.m

@interface NSString (Base64)

- (NSString *) stringPaddedForBase64;

@end

@implementation NSString (Base64)

- (NSString *) stringPaddedForBase64 {
    NSUInteger paddedLength = self.length + (self.length % 3);
    return [self stringByPaddingToLength:paddedLength withString:@"=" startingAtIndex:0];
}

@end

@implementation NSData (Base64)

- (instancetype) initWithBase64EncodedString:(NSString *)base64String {
    return [self initWithBase64Encoding:[base64String stringPaddedForBase64]];
}

- (NSString *) base64EncodedString {
    return [self base64Encoding];
}

@end

Ответ 4

Быстрая версия Пол отвечает

func paddedBase64EncodedString(encodedString: String) -> String
{
    let encodedStringLength = encodedString.characters.count
    let paddedLength = encodedStringLength + (4 - (encodedStringLength % 4))
    let paddedBase64String = encodedString.stringByPaddingToLength(paddedLength,
                                                                    withString: "=",
                                                                    startingAtIndex: 0)

    return paddedBase64String
}

Ответ 5

Я столкнулся с той же проблемой, но разрешил ее, добавив == в конец строки

base64UserStr = NSString(format: "%@%@", base64UserStr,"==") as String
let decodedData = NSData(base64EncodedString: base64UserStr, options: NSDataBase64DecodingOptions.init(rawValue: 0))


if (decodedData != nil)
{
    let decodedString = NSString(data: decodedData!, encoding: NSUTF8StringEncoding)

    print("Base 64 decode string is \(decodedString)")
}

Это определенно будет работать.

Ответ 6

NSString *base64String = @"<the token>";
NSUInteger paddedLength = base64String.length +     ((base64String.length % 4)>0 ? (4 - (base64String.length % 4)): 0);
NSString* correctBase64String = [base64String stringByPaddingToLength:paddedLength withString:@"=" startingAtIndex:0];

исправлена ​​ошибка в ответе pauls, которая заставила base64string получить дополнительные ==== в конце, если строка не требовала заполнения. Это добавило дополнительный символ после декодирования строки базы 64. В моем сценарии сломался json-парсер.