0

I want to convert following base64-encoded String in Swift 3:

dfYcSGpvBqyzvkAXkdbHDA==

to its equivalant String:

uöHjo¬³¾@‘ÖÇ

Following websites do the job very fine:

http://www.motobit.com/util/base64-decoder-encoder.asp

http://www.utilities-online.info/base64/#.WG-FwrFh2Rs

So does the PHP's function base64_decode. The documentation of this function says:

Returns FALSE if input contains character from outside the base64 alphabet.

But I am unable to do the same in Swift 3. Following code doesn't do the job too:

func convertBase64ToNormalString(base64String:String)->String!{
   let decodedData = Data(base64Encoded: base64String, options:    Data.Base64DecodingOptions())
   let bytes = decodedData?.bytes
   return String(bytes: bytes, encoding: NSUTF8StringEncoding)
}

Here is the contextual information about why I need to convert the base64 string into an string: My Php developer wants me to send all APIs params values encrypted with AES algorithm. For that, I am using this lib. He has given me AES key in Hex format (I mentioned in my last question) and iv in base64 (given above) and he instructed me decode this base64 key before using because he was also doing the same in his PHP code. Here is his PHP code of encryption and decryption:

function encryptParamAES($plaintext, $encryptionEnabled = true) {
    if (! $encryptionEnabled) {
        return $plaintext;
    }
    // --- ENCRYPTION ---

    // Constants =========================================================
    // the key should be random binary, use scrypt, bcrypt or PBKDF2 to
    // convert a string into a key
    // key is specified using hexadecimal
    $key = pack ( 'H*', "dcb04a9e103a5cd8b53763051cef09bc66abe029fdebae5e1d417e2ffc2a07a4" );
    // create a random IV to use with CBC encoding
    $iv_size = 16;
    $iv = base64_decode ( "dfYcSGpvBqyzvkAXkdbHDA==" );

    // End Of constants ===================================================

    // echo "IV: " . base64_encode ( $iv ) . "\n<br>";
    // echo "IV Size: " . $iv_size . "\n<br>";

    // show key size use either 16, 24 or 32 byte keys for AES-128, 192
    // and 256 respectively

    // $key_size = strlen ( $key );
    // echo "Key size: " . $key_size . "\n<br><br>";

    // creates a cipher text compatible with AES (Rijndael block size = 128)
    // to keep the text confidential
    // only suitable for encoded input that never ends with value 00h
    // (because of default zero padding)
    $ciphertext = mcrypt_encrypt ( MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv );

    // prepend the IV for it to be available for decryption
    $ciphertext = $iv . $ciphertext;

    // encode the resulting cipher text so it can be represented by a string
    $ciphertext_base64 = base64_encode ( $ciphertext );

    return $ciphertext_base64;
}


function decryptParamAES($ciphertext_base64, $encryptionEnabled = true) {
    if (! $encryptionEnabled) {
        return $ciphertext_base64;
    }
    // --- DECRYPTION ---

    // Constants =========================================================
    // the key should be random binary, use scrypt, bcrypt or PBKDF2 to
    // convert a string into a key
    // key is specified using hexadecimal
    $key = pack ( 'H*', "dcb04a9e103a5cd8b53763051cef09bc66abe029fdebae5e1d417e2ffc2a07a4" );
    // create a random IV to use with CBC encoding
    $iv_size = 16;
    $iv = base64_decode ( "dfYcSGpvBqyzvkAXkdbHDA==" );

    // End Of constants ===================================================

    $ciphertext_dec = base64_decode ( $ciphertext_base64 );

    // retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
    $iv_dec = substr ( $ciphertext_dec, 0, $iv_size );

    // retrieves the cipher text (everything except the $iv_size in the front)
    $ciphertext_dec = substr ( $ciphertext_dec, $iv_size );

    // may remove 00h valued characters from end of plain text
    $plaintext_dec = mcrypt_decrypt ( MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec );
    return rtrim ( $plaintext_dec );
}

I just saw this PHP code and wondered, why he is not using $iv as mcrypt_decrypt function's last param!! Will update you on it. But still question remains the same, PHP function base64_decode doesn't return FALSE for the above base64 string! I tested this function myself by terminal command: php test.php. Here test.php contains following code:

<?php
$iv = base64_decode ( "dfYcSGpvBqyzvkAXkdbHDA==" );
echo $iv;
?>

And the output was: u?Hjo???@???

Community
  • 1
  • 1
Chanchal Raj
  • 4,176
  • 4
  • 39
  • 46
  • 2
    This is essentially the same problem as in your previous question http://stackoverflow.com/questions/41485494/convert-hex-encoded-string-to-string-in-swift-3, and my advice http://stackoverflow.com/a/41487408/1187415 is the same: *Don't* convert the data to a String. – If you have to do so (for some mysterious reason), then choose a different encoding (as also told in an answer to your previous question). – Martin R Jan 06 '17 at 12:19

1 Answers1

1

Looking at your revised question, you're trying to take this base-64 string, and using it as the iv in your AES algorithm. I can understand why you are wondering how to convert that resulting Data into a String, but you should not do that. Yes, there's a rendition of AES that expects the iv as a string. But there's another rendition that expects an Array<UInt8>. So, just like MartinR said in his answer to your other question, build an array of UInt8 instead, like so:

let iv = Array(Data(base64Encoded: "dfYcSGpvBqyzvkAXkdbHDA==")!)

That resulting iv is an Array<UInt8> (also known as [UInt8]). You can use that with your AES function.

My original discussion about converting Data objects to UTF8 strings is below. But the key message is that you shouldn't try to do so. Just build your array of UInt8 and use that with your library's AES function.


Looking at your other question (Convert hex-encoded String to String in Swift 3), you revealed in comments that you were dealing with an AES key. I'm suspicious that we're dealing with a similar issue here (though that was 32 bytes of data, and here we have 16 bytes).

Bottom line, I'd suggest you completely drop this "how to I get a string representation of the data captured in this base-64 string" line of inquiry. If it's an encryption key (or some token or whatever), don't bother trying to represent it as a string. (This is the raison d'être of base-64, to create transmittable string representations of data that isn't a string.)

I'd suggest you step back and describe the broader problem that you are trying to solve. Stop trying to create strings out of these binary payloads. In your code snippet, you successfully create a Data from the base-64 string. The real question, I think, is not "how do I now get a string from that?", but rather "what do I do this Data?"

We can't answer that question without more context about where you got this data and what it is for.

By the way, my original answer to your question is below.


The problem is that base-64 string translates to 16 bytes of data whose hexadecimal representation is

75f61c48 6a6f06ac b3be4017 91d6c70c

But that payload is not valid UTF8 string. The third byte, 1c is not consistent with UTF8 string. If you look at the definition of UTF8, it says that if a byte is in the range of f6fb, which the second byte is, that the character consists of that and the following two bytes, both of which should be in the range of 217e or a0ff, which 1c is not.

So, this simply is not a valid UTF8 string. Clearly those sites you're using are not gracefully detecting/handling invalid UTF8 strings.

Perhaps the data in this base-64 string was converted from a string using a different encoding. Or, perhaps it was not originally a string at all. Not all binary payloads have clean string representations. Frankly, this is why we use base-64 representations in the first place, to come up with a text representation of a blob of data that is not a string.

If you provide more information about the source of the data contained in this base-64 string, we might be able to advise you further.

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    Apparently those sites interpret the data in ISO Latin encoding, and I wonder what OP learned from the discussion and answers to his previous question :( – Martin R Jan 06 '17 at 12:28
  • 1
    @MartinR - Perhaps. Or perhaps they're just not doing error detection at all (because if you explicitly select UTF8, that first site still shows you a string). Frankly, when I noticed that they weren't detecting UTF8 string representation errors, I lost all confidence in those sites, which is why I went back to the definition of UTF8 to confirm. – Rob Jan 06 '17 at 12:33
  • Sure, you are completely right. – *If* you convert the given data with `.isoLatin1` then the result is `uöHjo¬³¾@‘ÖÇ` which is what OP expects. But we had all this discussed with OP in his previous question and I am tempted to close this as a duplicate... – Martin R Jan 06 '17 at 12:37
  • 1
    @MartinR - Fine. But I still wouldn't assume that that was the original string and that they explicitly got the latin-1 representation and then converted that to base-64 string representation. It's far more likely that the original payload was simply not a string at all and that the OP is persistently misguided in his attempt to represent it as a string. But we're simply guessing until the OP tells us the source of that base-64 encoded payload. – Rob Jan 06 '17 at 12:42
  • 1
    That is *exactly* what I mean (perhaps I expressed myself badly). In the previous question, it was an AES key and I suggested in my answer to pass that to the AES init function as a byte array, *without* converting it to a string. Of course I don't know if it is the same issue here. – Martin R Jan 06 '17 at 12:45
  • @ChanchalRaj - Thanks for clarifying. So, just take your base-64 string and convert it to a `Data`. But just like MartinR said [in his answer to your other question](http://stackoverflow.com/a/41487408/1271826), do not then attempt to convert that `Data` to a string just because there is an AES function expecting `String` parameters. There are also renditions of `AES` that expect `Array`, so do that. See my revised answer above. – Rob Jan 07 '17 at 07:22
  • @Rob, thanks mate for the support. And I accept that using `Array[UInt8]` is better. Another reason for all this problem was PHP guy (if u see above encrypt code) was prepending the iv base16-decoded string with ciphered text. He gave me this whole sample ciphered text with the prepended string without telling me that iv string is also prepended with the ciphered text!! OMG, thanks you both saved a lot of time and made me learn several things. Thumbs up! – Chanchal Raj Jan 07 '17 at 07:31
  • @MartinR I guest I have the similar problem. Could you check my question? http://stackoverflow.com/questions/43453556/aes-128-message-decryption-swift-ios – Anton Kashpor Apr 18 '17 at 11:26