Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OAEP Padding Error When Decrypting Data in C# That Was Encrypted in JavaScript with RSA-OAEP

Before I get too much into the details, the high level thing I'm trying to accomplish is encrypting some data in JavaScript, sending that to a web server, then decrypting that encrypted data in C#. The part I'm having trouble with is decrypting the data in C#.

I'm encrypting some data in JavaScript like this (I removed the extraneous code):

// https://github.com/diafygi/webcrypto-examples#rsa-oaep---encrypt
window.crypto.subtle.encrypt(
    {
        name: "RSA-OAEP"
    },
    publicKey,
    data
)
.then(function (encrypted) {
    // ...
});

I confirmed that I can decrypt it in JavaScript like so (note that I don't actually want to do this, but I did it to prove that the data could be decrypted):

function decryptValue () {

    // Base64 decode the encrypted data for the value "Bob".
    var data = base64Decode("CthOUMzRdtSwo+4twgtjCA674G3UosWypUZv5E7uxG7GqYPiIJ+E+Uq7vbElp/bahB1fJrgq1qbdMrUZnSypVqBwYnccSxwablO15OOXl9Rn1e7w9V9fuMxtUqvhn+YZezk1623Qd7f5XTYjf6POwixtrgfZtdA+qh00ktKiVBpQKNG/bxhV94fK9+hb+qnzPmXilr9QF5rSQTd4hYHmYcR2ljVCDDZMV3tCVUTecWjS5HbOA1254ve/q3ulBLoPQTE58g7FwDQUZnd7XBdRSwYnrBWTJh8nmJ0PDfn+mCTGEI86S7HtoFYsE+Hezd24Z523phGEVrdMC9Ob1LlXEA==");

    // Get private key.
    var keyPromise = importPrivateKey();
    return keyPromise.then(function (privateKey) {

        // Decrypt the value.
        return window.crypto.subtle.decrypt(
            {
                name: "RSA-OAEP"
            },
            privateKey,
            data
        )
        .then(function (decrypted) {

            // Log the decrypted value to the console.
            console.log(arrayBufferToString(decrypted));

        });

    });

}

For simplicity, that code sample is decrypting a previously encrypted value of "Bob". This works fine.

The problem occurs when I try to decrypt the value in C#:

public static string Decrypt()
{

    // The encrypted and base64 encoded value for "Bob".
    var encryptedValue = "CthOUMzRdtSwo+4twgtjCA674G3UosWypUZv5E7uxG7GqYPiIJ+E+Uq7vbElp/bahB1fJrgq1qbdMrUZnSypVqBwYnccSxwablO15OOXl9Rn1e7w9V9fuMxtUqvhn+YZezk1623Qd7f5XTYjf6POwixtrgfZtdA+qh00ktKiVBpQKNG/bxhV94fK9+hb+qnzPmXilr9QF5rSQTd4hYHmYcR2ljVCDDZMV3tCVUTecWjS5HbOA1254ve/q3ulBLoPQTE58g7FwDQUZnd7XBdRSwYnrBWTJh8nmJ0PDfn+mCTGEI86S7HtoFYsE+Hezd24Z523phGEVrdMC9Ob1LlXEA==";

    // Assuming RSA-OAEP.
    var doOaep = true;

    // Setup encryption algorithm.
    var provider = GetPrivateKey();

    // Decrypt value.
    var encryptedData = Convert.FromBase64String(encryptedValue);
    // This line throws an error: "Error occurred while decoding OAEP padding."
    var decryptedData = provider.Decrypt(encryptedData, doOaep);
    var decryptedText = Encoding.Unicode.GetString(decryptedData);

    // Return decrypted text.
    return decryptedText;

}

The line that says provider.Decrypt(encryptedData, doOaep) throws an error with a message of "Error occurred while decoding OAEP padding." The stack trace is:

Error occurred while decoding OAEP padding.
    at System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey(SafeKeyHandle pKeyContext, Byte[] pbEncryptedKey, Int32 cbEncryptedKey, Boolean fOAEP, ObjectHandleOnStack ohRetDecryptedKey)
    at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)

It seems like maybe the way the JavaScript is encrypting the value is not compatible with the way the C# is encrypting the value. Before I completely abandon this approach and try another JavaScript library for encryption, is there some way around this error?

For additional context, I am guessing this error is related to something mentioned in this article: https://www.codeproject.com/Articles/11479/RSA-Interoperability-between-JavaScript-and-RSACry

It says:

Incompatible padding scheme from the JavaScript code would produce the "bad data" exception at the server side.

The JavaScript code therefore needs to implement one of two padding schemes used in the .NET RSA implementation, the first is PKCS#1 v1.5 padding and another is OAEP (PKCS#1 v2) padding.

I'm not getting that exact exception, but maybe since that article was written the error message has changed. In any event, what that article says seems to imply that the way the JavaScript is encrypting isn't compatible with the way the C# is decrypting (namely, due to C#'s requirement for padding).

Is there something I'm missing? Is there some parameter or some easy way to get encryption working in JavaScript and decryption working in C#? Perhaps there is some C# library that decrypts in a way that is compatible with the way the JavaScript is encrypting?

Here's a full example that shows the JavaScript is decrypting properly (only works on some browsers... probably not going to work on IE):

function decryptValue () {

    // Base64 decode the encrypted data for the value "Bob".
    var data = base64Decode("CthOUMzRdtSwo+4twgtjCA674G3UosWypUZv5E7uxG7GqYPiIJ+E+Uq7vbElp/bahB1fJrgq1qbdMrUZnSypVqBwYnccSxwablO15OOXl9Rn1e7w9V9fuMxtUqvhn+YZezk1623Qd7f5XTYjf6POwixtrgfZtdA+qh00ktKiVBpQKNG/bxhV94fK9+hb+qnzPmXilr9QF5rSQTd4hYHmYcR2ljVCDDZMV3tCVUTecWjS5HbOA1254ve/q3ulBLoPQTE58g7FwDQUZnd7XBdRSwYnrBWTJh8nmJ0PDfn+mCTGEI86S7HtoFYsE+Hezd24Z523phGEVrdMC9Ob1LlXEA==");

    // Get private key.
    var keyPromise = importPrivateKey();
    return keyPromise.then(function (privateKey) {

        // Decrypt the value.
        return window.crypto.subtle.decrypt(
            {
                name: "RSA-OAEP"
            },
            privateKey,
            data
        )
        .then(function (decrypted) {

            // Log the decrypted value to the console.
            console.log("Decrypted value: " + arrayBufferToString(decrypted));

        });

    });

}

function importPrivateKey() {
    var rawKey = {
        "alg": "RSA-OAEP-256",
        "d": "E4KDwgxy7jFrqeXqKjxPTGOdbEoZ2aWj5qcZhUJcnr9Qh_jg_grkgpHVwEbQifTxsipXTiR3_ygspI4XFoeV-wDVfWqWCVR3_bHChF9PW8Ak1x_dBSS28BMs8PdthI1pDbpqPhmMcF4riHCtNo1M1v8cLdeaiqiXitNVBkaTePsDiucfwOy1rgxwBqAL1CNJhP8oRiYkxD-gfE_EapWuXY9-wF9O-lXPLSTKWgMmmVxSmhUP-Uqk7cJ24UH9C7W7hnSQU4pkfD5XHx3_2WO2GMKKZcqz39wJUrQzrIO7539SYsQ3rEe4aMJyL4U-Ib4_purzVS0DRjzGxK8chT2guQ",
        "dp": "kibhWHk1R6yBlhZbjIrNl9beAkyV5vtFsj_F0ixbIITzjSqI_td71sWjKQvJ2rR7hu5DYTZ4p3XwBeQ2jpYQV-y5uh4v7rGngh-0GHuHqMiUQnejgYGcHgng4iCM4e3aTO7QUlP8jqRfxw6xpfNTjrVbAL8LtdCG21vmqOiLkXE",
        "dq": "qLF9x-zKfaXlLsNgBQ1ZnaQexrnJRqrRh9JSU85fCNy5mmpKWAUbCHB-59CGAId8wMAnAyEpjcBOKNTqWSlNzp84xeUHcyPI-Dt4Yp_Y_dXjGAYntALSJs4qeF2rk55MSpiSD_KSU4DknX_E_G2rFMY7AZOSwi1D8YcNmj5okTE",
        "e": "AQAB",
        "ext": true,
        "key_ops": [
            "decrypt"
        ],
        "kty": "RSA",
        "n": "oQeTwOlTc6rIb2kddwIOc0Ywslc7YzJSRZd_PegW7T3nO3DqCI5kp5EJmnGP8JJ9sbyVYyAHFLZQtMP69UspZFn__fBk2LTp2QdqBSMHbObENcSiG2FH-pZSwCaj3Pvy-qvTjnkxxN-3OE6oB8EcX5ekZwCZzAxazbVXctY_hCcaTWG7ugwc_ZyvhsdE7wa3pnTfXYHWXcDDT8FTpYl62aqWsEIUAJSkgmQ9zce0RiDUjBJyJEM9P0ihp1Ab8BD88pEM22-PXfiOesRzp5yOsjzI3kdr5KPsshstneJEGHYae5GZXLUpnVMRY1TCFFLbkPwK6oVkRaVU1RvK9ssO3Q",
        "p": "2TTEToB4AuPIPPpg3yTyBlGb_m-f4r-TxpU96ConV2p696_4QI6jlPWwgcC9Vdma_Da43AGuyLzIptgkzF8nSjV80VwwDKQ1YkFPc6ZqB2isvExuieSP6_jLlB-fCyCLqtTxpPm2VcK16Pqm0s5T0QGH6cQjjm1r2Ww1wuaiQbk",
        "q": "vcpFwkZKZ3hx3FpHgy3ScuuTRSPO2ge8TE8UMJdCrEnpftAeYuVYrJqnxfzKgyl02OijAUi1eozJxj_lM5McxrKZEEAvo6e8wtzl2hnkUh-KWoBJ8ii0VJcu6U5vs4pcv-lYBPFC6fzoGnUw8LNWMxb5ejgYbLUWp10BbfkWGEU",
        "qi": "Mza7JYleki7BvmD3dX5CO2nkD3mBGz4_0P_aoWyHEkWu4p5XWillaRVWyLnQEubLvAduUCr-lhfNmzdUhHecpE438_LQNtKRyOq9zkvjsMOGDmbkKpZ7-aTSshax6KNlYOWdOkadjuLtRExCmwbzu5lgI4NwacxSs5MfjHMrTCo"
    };
    return window.crypto.subtle.importKey(
        "jwk",
        rawKey,
        {
            name: "RSA-OAEP",
            hash: { name: "SHA-256" }
        },
        true,
        ["decrypt"]
    );
}

function arrayBufferToString(buffer) {
    var result = '';
    var bytes = new Uint8Array(buffer);
    for (var i = 0; i < bytes.length; i++) {
        result += String.fromCharCode(bytes[i]);
    }
    return result;
}

// Decodes a base64 encoded string into an ArrayBuffer.
// https://stackoverflow.com/a/36378903/2052963
function base64Decode(base64) {
    var binary_string = window.atob(base64);
    return stringToArrayBuffer(binary_string);
}

// Converts a string to an ArrayBuffer.
function stringToArrayBuffer(value) {
    var bytes = new Uint8Array(value.length);
    for (var i = 0; i < value.length; i++) {
        bytes[i] = value.charCodeAt(i);
    }
    return bytes.buffer;
}

decryptValue();

BTW, some of my code samples show the private key I'm using. That's intentional to help you understand the code (it's a throw away key). In fact, here's how I am getting the private key in C#:

private static RSACryptoServiceProvider GetPrivateKey()
{
    RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
    RSAParameters RSAparams = new RSAParameters();
    RSAparams.Modulus = Base64UrlDecode("oQeTwOlTc6rIb2kddwIOc0Ywslc7YzJSRZd_PegW7T3nO3DqCI5kp5EJmnGP8JJ9sbyVYyAHFLZQtMP69UspZFn__fBk2LTp2QdqBSMHbObENcSiG2FH-pZSwCaj3Pvy-qvTjnkxxN-3OE6oB8EcX5ekZwCZzAxazbVXctY_hCcaTWG7ugwc_ZyvhsdE7wa3pnTfXYHWXcDDT8FTpYl62aqWsEIUAJSkgmQ9zce0RiDUjBJyJEM9P0ihp1Ab8BD88pEM22-PXfiOesRzp5yOsjzI3kdr5KPsshstneJEGHYae5GZXLUpnVMRY1TCFFLbkPwK6oVkRaVU1RvK9ssO3Q");
    RSAparams.Exponent = Base64UrlDecode("AQAB");
    RSAparams.D = Base64UrlDecode("E4KDwgxy7jFrqeXqKjxPTGOdbEoZ2aWj5qcZhUJcnr9Qh_jg_grkgpHVwEbQifTxsipXTiR3_ygspI4XFoeV-wDVfWqWCVR3_bHChF9PW8Ak1x_dBSS28BMs8PdthI1pDbpqPhmMcF4riHCtNo1M1v8cLdeaiqiXitNVBkaTePsDiucfwOy1rgxwBqAL1CNJhP8oRiYkxD-gfE_EapWuXY9-wF9O-lXPLSTKWgMmmVxSmhUP-Uqk7cJ24UH9C7W7hnSQU4pkfD5XHx3_2WO2GMKKZcqz39wJUrQzrIO7539SYsQ3rEe4aMJyL4U-Ib4_purzVS0DRjzGxK8chT2guQ");
    RSAparams.P = Base64UrlDecode("2TTEToB4AuPIPPpg3yTyBlGb_m-f4r-TxpU96ConV2p696_4QI6jlPWwgcC9Vdma_Da43AGuyLzIptgkzF8nSjV80VwwDKQ1YkFPc6ZqB2isvExuieSP6_jLlB-fCyCLqtTxpPm2VcK16Pqm0s5T0QGH6cQjjm1r2Ww1wuaiQbk");
    RSAparams.Q = Base64UrlDecode("vcpFwkZKZ3hx3FpHgy3ScuuTRSPO2ge8TE8UMJdCrEnpftAeYuVYrJqnxfzKgyl02OijAUi1eozJxj_lM5McxrKZEEAvo6e8wtzl2hnkUh-KWoBJ8ii0VJcu6U5vs4pcv-lYBPFC6fzoGnUw8LNWMxb5ejgYbLUWp10BbfkWGEU");
    RSAparams.DP = Base64UrlDecode("kibhWHk1R6yBlhZbjIrNl9beAkyV5vtFsj_F0ixbIITzjSqI_td71sWjKQvJ2rR7hu5DYTZ4p3XwBeQ2jpYQV-y5uh4v7rGngh-0GHuHqMiUQnejgYGcHgng4iCM4e3aTO7QUlP8jqRfxw6xpfNTjrVbAL8LtdCG21vmqOiLkXE");
    RSAparams.DQ = Base64UrlDecode("qLF9x-zKfaXlLsNgBQ1ZnaQexrnJRqrRh9JSU85fCNy5mmpKWAUbCHB-59CGAId8wMAnAyEpjcBOKNTqWSlNzp84xeUHcyPI-Dt4Yp_Y_dXjGAYntALSJs4qeF2rk55MSpiSD_KSU4DknX_E_G2rFMY7AZOSwi1D8YcNmj5okTE");
    RSAparams.InverseQ = Base64UrlDecode("Mza7JYleki7BvmD3dX5CO2nkD3mBGz4_0P_aoWyHEkWu4p5XWillaRVWyLnQEubLvAduUCr-lhfNmzdUhHecpE438_LQNtKRyOq9zkvjsMOGDmbkKpZ7-aTSshax6KNlYOWdOkadjuLtRExCmwbzu5lgI4NwacxSs5MfjHMrTCo");
    RSA.ImportParameters(RSAparams);
    return RSA;
}

// From the PDF here: https://www.rfc-editor.org/info/rfc7515
// Also see: https://auth0.com/docs/jwks
public static byte[] Base64UrlDecode(string arg)
{
    string s = arg;
    s = s.Replace('-', '+'); // 62nd char of encoding
    s = s.Replace('_', '/'); // 63rd char of encoding
    switch (s.Length % 4) // Pad with trailing '='s
    {
        case 0: break; // No pad chars in this case
        case 2: s += "=="; break; // Two pad chars
        case 3: s += "="; break; // One pad char
        default:
            throw new System.Exception(
        "Illegal base64url string!");
    }
    return Convert.FromBase64String(s); // Standard base64 decoder
}
like image 527
Nicholas Westby Avatar asked Oct 24 '17 17:10

Nicholas Westby


2 Answers

Because you're using OAEP with SHA-2-256 you need to move from RSACryptoServiceProvider to RSACng (.NET 4.6+). Note that aside from the ctor call, I've eliminated the knowledge of which implementation is being used.

private static RSA GetPrivateKey()
{
    // build the RSAParams as before, then
    RSA rsa = new RSACng();
    rsa.ImportParameters(RSAparams);
    return rsa;
}

// Setup encryption algorithm.
var provider = GetPrivateKey();
...
var decryptedData = provider.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);
like image 51
bartonjs Avatar answered Oct 08 '22 13:10

bartonjs


I am unable to test @bartonjs's answer because I don't have access to a Windows computer and Mono apparently doesn't implement RSACng. Below is an example that decrypts your ciphertext using the Bouncycastle C# library. Notice the OaepPadding(...) uses SHA-256 for both the Oaep hash and the MGF hash. This is apparently what is needed to interoperate with your javascript code. Also, notice I used Encoding.UTF8.GetString() whereas you used Encoding.Unicode.GetString(). The encoding is definitely not UTF-16 which is what Encoding.Unicode gives you.

using System;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;

namespace RsaSha256OaepDecrypt
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            var encryptedValue = "CthOUMzRdtSwo+4twgtjCA674G3UosWypUZv5E7uxG7GqYPiIJ+E+Uq7vbElp/bahB1fJrgq1qbdMrUZnSypVqBwYnccSxwablO15OOXl9Rn1e7w9V9fuMxtUqvhn+YZezk1623Qd7f5XTYjf6POwixtrgfZtdA+qh00ktKiVBpQKNG/bxhV94fK9+hb+qnzPmXilr9QF5rSQTd4hYHmYcR2ljVCDDZMV3tCVUTecWjS5HbOA1254ve/q3ulBLoPQTE58g7FwDQUZnd7XBdRSwYnrBWTJh8nmJ0PDfn+mCTGEI86S7HtoFYsE+Hezd24Z523phGEVrdMC9Ob1LlXEA==";
            var encryptedData = Convert.FromBase64String(encryptedValue);
            var rsaPrivate = GetPrivateKey();
            IAsymmetricBlockCipher cipher0 = new RsaBlindedEngine();
            cipher0 = new OaepEncoding(cipher0, new Sha256Digest(), new Sha256Digest(), null);
            BufferedAsymmetricBlockCipher cipher = new BufferedAsymmetricBlockCipher(cipher0);
            cipher.Init(false, rsaPrivate);
            cipher.ProcessBytes(encryptedData, 0, encryptedData.Length);
            var decryptedData = cipher.DoFinal();
            var decryptedText = Encoding.UTF8.GetString(decryptedData);
            Console.WriteLine(decryptedText);
        }

        private static BigInteger makeBigInt(String b64Url)
        {
            var bytes = Base64UrlDecode(b64Url);
            if ((sbyte)bytes[0] < 0)
            {
                // prepend a zero byte to make it positive.
                var bytes1 = new byte[bytes.Length + 1];
                bytes1[0] = 0;
                bytes.CopyTo(bytes1, 1);
                bytes = bytes1;
            }

            return new BigInteger(bytes);
        }
        private static AsymmetricKeyParameter GetPrivateKey()
        {
            //RSAParameters RSAparams = new RSAParameters();
            var Modulus = makeBigInt("oQeTwOlTc6rIb2kddwIOc0Ywslc7YzJSRZd_PegW7T3nO3DqCI5kp5EJmnGP8JJ9sbyVYyAHFLZQtMP69UspZFn__fBk2LTp2QdqBSMHbObENcSiG2FH-pZSwCaj3Pvy-qvTjnkxxN-3OE6oB8EcX5ekZwCZzAxazbVXctY_hCcaTWG7ugwc_ZyvhsdE7wa3pnTfXYHWXcDDT8FTpYl62aqWsEIUAJSkgmQ9zce0RiDUjBJyJEM9P0ihp1Ab8BD88pEM22-PXfiOesRzp5yOsjzI3kdr5KPsshstneJEGHYae5GZXLUpnVMRY1TCFFLbkPwK6oVkRaVU1RvK9ssO3Q");
            var Exponent = makeBigInt("AQAB");
            var D = makeBigInt("E4KDwgxy7jFrqeXqKjxPTGOdbEoZ2aWj5qcZhUJcnr9Qh_jg_grkgpHVwEbQifTxsipXTiR3_ygspI4XFoeV-wDVfWqWCVR3_bHChF9PW8Ak1x_dBSS28BMs8PdthI1pDbpqPhmMcF4riHCtNo1M1v8cLdeaiqiXitNVBkaTePsDiucfwOy1rgxwBqAL1CNJhP8oRiYkxD-gfE_EapWuXY9-wF9O-lXPLSTKWgMmmVxSmhUP-Uqk7cJ24UH9C7W7hnSQU4pkfD5XHx3_2WO2GMKKZcqz39wJUrQzrIO7539SYsQ3rEe4aMJyL4U-Ib4_purzVS0DRjzGxK8chT2guQ");
            var P = makeBigInt("2TTEToB4AuPIPPpg3yTyBlGb_m-f4r-TxpU96ConV2p696_4QI6jlPWwgcC9Vdma_Da43AGuyLzIptgkzF8nSjV80VwwDKQ1YkFPc6ZqB2isvExuieSP6_jLlB-fCyCLqtTxpPm2VcK16Pqm0s5T0QGH6cQjjm1r2Ww1wuaiQbk");
            var Q = makeBigInt("vcpFwkZKZ3hx3FpHgy3ScuuTRSPO2ge8TE8UMJdCrEnpftAeYuVYrJqnxfzKgyl02OijAUi1eozJxj_lM5McxrKZEEAvo6e8wtzl2hnkUh-KWoBJ8ii0VJcu6U5vs4pcv-lYBPFC6fzoGnUw8LNWMxb5ejgYbLUWp10BbfkWGEU");
            var DP = makeBigInt("kibhWHk1R6yBlhZbjIrNl9beAkyV5vtFsj_F0ixbIITzjSqI_td71sWjKQvJ2rR7hu5DYTZ4p3XwBeQ2jpYQV-y5uh4v7rGngh-0GHuHqMiUQnejgYGcHgng4iCM4e3aTO7QUlP8jqRfxw6xpfNTjrVbAL8LtdCG21vmqOiLkXE");
            var DQ = makeBigInt("qLF9x-zKfaXlLsNgBQ1ZnaQexrnJRqrRh9JSU85fCNy5mmpKWAUbCHB-59CGAId8wMAnAyEpjcBOKNTqWSlNzp84xeUHcyPI-Dt4Yp_Y_dXjGAYntALSJs4qeF2rk55MSpiSD_KSU4DknX_E_G2rFMY7AZOSwi1D8YcNmj5okTE");
            var InverseQ = makeBigInt("Mza7JYleki7BvmD3dX5CO2nkD3mBGz4_0P_aoWyHEkWu4p5XWillaRVWyLnQEubLvAduUCr-lhfNmzdUhHecpE438_LQNtKRyOq9zkvjsMOGDmbkKpZ7-aTSshax6KNlYOWdOkadjuLtRExCmwbzu5lgI4NwacxSs5MfjHMrTCo");
            var rsa = new RsaPrivateCrtKeyParameters(Modulus, Exponent, D, P, Q, DP, DQ, InverseQ);
            return rsa;
        }

        // From the PDF here: https://www.rfc-editor.org/info/rfc7515
        // Also see: https://auth0.com/docs/jwks

        public static byte[] Base64UrlDecode(string arg)
        {
            string s = arg;
            s = s.Replace('-', '+'); // 62nd char of encoding
            s = s.Replace('_', '/'); // 63rd char of encoding
            switch (s.Length % 4) // Pad with trailing '='s
            {
                case 0: break; // No pad chars in this case
                case 2: s += "=="; break; // Two pad chars
                case 3: s += "="; break; // One pad char
                default:
                    throw new System.Exception(
                "Illegal base64url string!");
            }
            return Convert.FromBase64String(s); // Standard base64 decoder
        }
    }
}
like image 25
President James K. Polk Avatar answered Oct 08 '22 14:10

President James K. Polk