streamlink / streamlink

Streamlink is a CLI utility which pipes video streams from various services into a video player
https://streamlink.github.io/
BSD 2-Clause "Simplified" License
10.02k stars 1.11k forks source link

Incorrect AES key length (48 bytes) #2253

Closed dexter101010 closed 5 years ago

dexter101010 commented 5 years ago

Hello all,

I have the following problem when i am trying to download an encrypted m3u8 link(AES 128) :

[cli][info] Found matching plugin hls for URL https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bcd586ee4b04b639bfddcf2/u/5c28ab6fe4b0efa6ebda1830/t/857c9994539db0b48bd818abaa6bcaaa/p/assets/videos/2018/10/22/5bcd586ee4b04b639bfddcf2/hls_350k_.m3u8
[cli][info] Available streams: live (worst, best)
[cli][info] Opening stream: live (hls)
Exception in thread Thread-HLSStreamWriter:
Traceback (most recent call last):
  File "threading.py", line 916, in _bootstrap_inner
  File "D:\ww\streamlink1\packages\streamlink\stream\segmented.py", line 167, in run
    self.write(segment, result)
  File "D:\ww\streamlink1\packages\streamlink\stream\hls.py", line 124, in write
    sequence.num)
  File "D:\ww\streamlink1\packages\streamlink\stream\hls.py", line 80, in create_decryptor
    return AES.new(self.key_data, AES.MODE_CBC, iv)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\AES.py", line 260, in new
    return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\__init__.py", line 130, in _create_cipher
    return modes[mode](factory, **kwargs)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\_mode_cbc.py", line 232, in _create_cbc_cipher
    cipher_state = factory._create_base_cipher(kwargs)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\AES.py", line 130, in _create_base_cipher
    raise ValueError("Incorrect AES key length (%d bytes)" % len(key))
ValueError: Incorrect AES key length (48 bytes)

I hope somebody from here can help me.

Thanks!

jackyzy823 commented 5 years ago

It seems that this m3u8 link requires decrypt key from URI k/timestamp However https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bcd586ee4b04b639bfddcf2/u/5c28ab6fe4b0efa6ebda1830/t/857c9994539db0b48bd818abaa6bcaaa/p/assets/videos/2018/10/22/5bcd586ee4b04b639bfddcf2/k/timestamp just return 200 ok with no content.

So maybe this site has some protection or authorization for this link.

dexter101010 commented 5 years ago

It seems that this m3u8 link requires decrypt key from URI k/timestamp However https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bcd586ee4b04b639bfddcf2/u/5c28ab6fe4b0efa6ebda1830/t/857c9994539db0b48bd818abaa6bcaaa/p/assets/videos/2018/10/22/5bcd586ee4b04b639bfddcf2/k/timestamp just return 200 ok with no content.

So maybe this site has some protection or authorization for this link.

Thank you for your answer. I have the credentials for login and the cookies but i don't know how to use pass them to streamlink.

Thanks!

jackyzy823 commented 5 years ago

You could try --http-cookie to pass your cookies.

dexter101010 commented 5 years ago

You could try --http-cookie to pass your cookies.

I've tried but still get the error :

streamlink --http-cookie "Cookie: c_user=4d8ab70dea91890fae57afacd83b3432zxYzzdoJteLK783PZ4YB8zbeZ6rx6cAs1Tb5U/+u80KxpJ36d84V1bqZ4KhkwjRhe1bb63beb815d54c46d3fa02da3e6eed; c_p=daf2bebbba9bb71895658b8cddfe367aRuD4Po96H8HHa9/arUGIvA==efc0aa751a4cc28541b34fd3b8ffb9ae; id=0eff6a49-1374-4913-90b7-77482d73fd8e; _ga=GA1.2.970484282.1546776087; JSESSIONID=FE882976C062C66271CEBCF437FE4B6E; __extfc=1" https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bf7d067e4b068ec781593db/u/5c28ab6fe4b0efa6ebda1830/t/6316a8da0fb034daa0bd5d745ccb12f7/p/assets/videos/2018/11/23/5bf7d067e4b068ec781593db/hls_1M_.m3u8 best [cli][info] Found matching plugin hls for URL https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bf7d067e4b068ec781593db/u/5c28ab6fe4b0efa6ebda1830/t/6316a8da0fb034daa0bd5d745ccb12f7/p/assets/videos/2018/11/23/5bf7d067e4b068ec781593db/hls_1M_.m3u8 [cli][info] Available streams: live (worst, best) [cli][info] Opening stream: live (hls) Exception in thread Thread-HLSStreamWriter: Traceback (most recent call last): File "c:\python3\lib\threading.py", line 917, in _bootstrap_inner self.run() File "c:\python3\lib\site-packages\streamlink\stream\segmented.py", line 167, in run self.write(segment, result) File "c:\python3\lib\site-packages\streamlink\stream\hls.py", line 119, in write sequence.num) File "c:\python3\lib\site-packages\streamlink\stream\hls.py", line 75, in create_decryptor return AES.new(self.key_data, AES.MODE_CBC, iv) File "c:\python3\lib\site-packages\Crypto\Cipher\AES.py", line 232, in new return _create_cipher(sys.modules[name], key, mode, *args, kwargs) File "c:\python3\lib\site-packages\Crypto\Cipher__init__.py", line 79, in _create_cipher return modes[mode](factory, kwargs) File "c:\python3\lib\site-packages\Crypto\Cipher_mode_cbc.py", line 274, in _create_cbc_cipher cipher_state = factory._create_base_cipher(kwargs) File "c:\python3\lib\site-packages\Crypto\Cipher\AES.py", line 93, in _create_base_cipher raise ValueError("Incorrect AES key length (%d bytes)" % len(key)) ValueError: Incorrect AES key length (48 bytes)

Thanks!

jackyzy823 commented 5 years ago

After some investigation, I found that i provided a wrong anwser before.

Here's update:

AES-128 's key is 128 bit == 16 bytes. However streamlink 's log show that the key is 48bytes ValueError: Incorrect AES key length (48 bytes)
So this site encrypts the key.

Found encrypt method in /resources/scripts/scoursePlayer.js

az.load = function (aB, aA, aC) {
var aE = [
    '/key',
    'endsWith',
    'url',
    '/timestamp',
    'onSuccess',
    'data',
    'subarray',
    'parse',
    'u8array',
    'enc',
    'ECB',
    'mode',
    'NoPadding',
    'pad',
    'decrypt',
    'AES',
    'stringify',
    'buffer'
];
if (aB[aE[2]][aE[1]](aE[0]) || aB[aE[2]][aE[1]](aE[3])) {
    var aD = aC[aE[4]];
    aC[aE[4]] = function (aK, aJ, aI) {
    var aH = new Uint8Array(aK[aE[5]]);
    var aG = aH[aE[6]](0, 16),
    aF = aH[aE[6]](32, 48);
    aH = cjs[aE[15]][aE[14]](convertArrayToBase64(aG), cjs[aE[9]][aE[8]][aE[7]](aF), {
        mode: cjs[aE[11]][aE[10]],
        padding: cjs[aE[13]][aE[12]]
    });
    aH = cjs[aE[9]][aE[8]][aE[16]](cjs[aE[15]][aE[14]](convertArrayToBase64(aF), aH, {
        mode: cjs[aE[11]][aE[10]],
        padding: cjs[aE[13]][aE[12]]
    }));
    aK[aE[5]] = aH[aE[17]];
    aD(aK, aJ, aI)
    }
}
ay(aB, aA, aC)
};

After some simple deobufscation

var aH = new Uint8Array(resp.data);
var aG = aH[subarray](0, 16),
aF = aH[subarray](32, 48);
aH = cjs[aes.decrypt](convertArrayToBase64(aG), cjs[enc.u8array.parse](aF), {
mode: cjs[mode.ecb],
padding: cjs[pad.nopadding]
});
aH = cjs[enc.u8array.stringify](cjs[aes.decrypt](convertArrayToBase64(aF), aH, {
mode: cjs[mode.ecb],
padding: cjs[pad.nopadding]
}));
resp.data = aH[buffer];

So you may need to write a plugin for this site and override create_decryptor function to get real key.

dexter101010 commented 5 years ago

After some investigation, I found that i provided a wrong anwser before.

Here's update:

AES-128 's key is 128 bit == 16 bytes. However streamlink 's log show that the key is 48bytes ValueError: Incorrect AES key length (48 bytes) So this site encrypts the key.

Found encrypt method in /resources/scripts/scoursePlayer.js

az.load = function (aB, aA, aC) {
var aE = [
    '/key',
    'endsWith',
    'url',
    '/timestamp',
    'onSuccess',
    'data',
    'subarray',
    'parse',
    'u8array',
    'enc',
    'ECB',
    'mode',
    'NoPadding',
    'pad',
    'decrypt',
    'AES',
    'stringify',
    'buffer'
];
if (aB[aE[2]][aE[1]](aE[0]) || aB[aE[2]][aE[1]](aE[3])) {
    var aD = aC[aE[4]];
    aC[aE[4]] = function (aK, aJ, aI) {
    var aH = new Uint8Array(aK[aE[5]]);
    var aG = aH[aE[6]](0, 16),
    aF = aH[aE[6]](32, 48);
    aH = cjs[aE[15]][aE[14]](convertArrayToBase64(aG), cjs[aE[9]][aE[8]][aE[7]](aF), {
        mode: cjs[aE[11]][aE[10]],
        padding: cjs[aE[13]][aE[12]]
    });
    aH = cjs[aE[9]][aE[8]][aE[16]](cjs[aE[15]][aE[14]](convertArrayToBase64(aF), aH, {
        mode: cjs[aE[11]][aE[10]],
        padding: cjs[aE[13]][aE[12]]
    }));
    aK[aE[5]] = aH[aE[17]];
    aD(aK, aJ, aI)
    }
}
ay(aB, aA, aC)
};

After some simple deobufscation

var aH = new Uint8Array(resp.data);
var aG = aH[subarray](0, 16),
aF = aH[subarray](32, 48);
aH = cjs[aes.decrypt](convertArrayToBase64(aG), cjs[enc.u8array.parse](aF), {
mode: cjs[mode.ecb],
padding: cjs[pad.nopadding]
});
aH = cjs[enc.u8array.stringify](cjs[aes.decrypt](convertArrayToBase64(aF), aH, {
mode: cjs[mode.ecb],
padding: cjs[pad.nopadding]
}));
resp.data = aH[buffer];

So you may need to write a plugin for this site and override create_decryptor function to get real key.

Thanks for your help and answer. Anyway i am not a programmer. I am just a network engineer.Maybe someone smarter than me will be able to write that plugin. I wish that...

jackyzy823 commented 5 years ago

A simple plugin implemention

import re
import logging

from Crypto.Cipher import AES
from streamlink import StreamError
from streamlink.plugin import Plugin
from streamlink.stream import HLSStream
from streamlink.stream.hls import HLSStreamWriter, HLSStreamReader

log = logging.getLogger(__name__)

class SpayeeStreamWriter(HLSStreamWriter):
    def _decrypt_key(self,data):
        tmp1 = data[0:16]
        tmp2 = data[32:48]
        dec1 = AES.new(tmp2,AES.MODE_ECB)
        tmp3 = dec1.decrypt(tmp1)
        dec2 = AES.new(tmp3,AES.MODE_ECB)
        return dec2.decrypt(tmp2)

    def create_decryptor(self, key, sequence):
        if key.method != "AES-128":
            raise StreamError("Unable to decrypt cipher {0}", key.method)

        if not self.key_uri_override and not key.uri:
            raise StreamError("Missing URI to decryption key")

        key_uri = self.key_uri_override if self.key_uri_override else key.uri

        if self.key_uri != key_uri:
            res = self.session.http.get(key_uri, exception=StreamError,
                                        retries=self.retries,
                                        **self.reader.request_params)
            res.encoding = "binary/octet-stream"
            self.key_data = self._decrypt_key(res.content)
            self.key_uri = key_uri

        iv = key.iv or num_to_iv(sequence)

        # Pad IV if needed
        iv = b"\x00" * (16 - len(iv)) + iv

        return AES.new(self.key_data, AES.MODE_CBC, iv)

class SpayeeStreamReader(HLSStreamReader):
    __writer__ = SpayeeStreamWriter

class SpayeeStream(HLSStream):
    def open(self):
        reader = SpayeeStreamReader(self)
        reader.open()

        return reader

class Spayee(Plugin):
    url_re = re.compile(r"""https://cdn.spayee.in/.*.m3u8""")

    def __init__(self, url):
        super(Spayee, self).__init__(url)

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def _get_streams(self):
        return {"live":SpayeeStream(self.session,self.url)}

__plugin__ = Spayee

save it to streamlink\plugins\spayee.py

Only tested under python 2.7

dexter101010 commented 5 years ago

A simple plugin implemention

import re
import logging

from Crypto.Cipher import AES
from streamlink import StreamError
from streamlink.plugin import Plugin
from streamlink.stream import HLSStream
from streamlink.stream.hls import HLSStreamWriter, HLSStreamReader

log = logging.getLogger(__name__)

class SpayeeStreamWriter(HLSStreamWriter):
    def _decrypt_key(self,data):
        tmp1 = data[0:16]
        tmp2 = data[32:48]
        dec1 = AES.new(tmp2,AES.MODE_ECB)
        tmp3 = dec1.decrypt(tmp1)
        dec2 = AES.new(tmp3,AES.MODE_ECB)
        return dec2.decrypt(tmp2)

    def create_decryptor(self, key, sequence):
        if key.method != "AES-128":
            raise StreamError("Unable to decrypt cipher {0}", key.method)

        if not self.key_uri_override and not key.uri:
            raise StreamError("Missing URI to decryption key")

        key_uri = self.key_uri_override if self.key_uri_override else key.uri

        if self.key_uri != key_uri:
            res = self.session.http.get(key_uri, exception=StreamError,
                                        retries=self.retries,
                                        **self.reader.request_params)
            res.encoding = "binary/octet-stream"
            self.key_data = self._decrypt_key(res.content)
            self.key_uri = key_uri

        iv = key.iv or num_to_iv(sequence)

        # Pad IV if needed
        iv = b"\x00" * (16 - len(iv)) + iv

        return AES.new(self.key_data, AES.MODE_CBC, iv)

class SpayeeStreamReader(HLSStreamReader):
    __writer__ = SpayeeStreamWriter

class SpayeeStream(HLSStream):
    def open(self):
        reader = SpayeeStreamReader(self)
        reader.open()

        return reader

class Spayee(Plugin):
    url_re = re.compile(r"""https://cdn.spayee.in/.*.m3u8""")

    def __init__(self, url):
        super(Spayee, self).__init__(url)

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def _get_streams(self):
        return {"live":SpayeeStream(self.session,self.url)}

__plugin__ = Spayee

save it to streamlink\plugins\spayee.py

Only tested under python 2.7

Thanks a lot sir. I hope it works. I will test it and i will come back with a reply to you.Thanks again for a million times.

dexter101010 commented 5 years ago

A simple plugin implemention

import re
import logging

from Crypto.Cipher import AES
from streamlink import StreamError
from streamlink.plugin import Plugin
from streamlink.stream import HLSStream
from streamlink.stream.hls import HLSStreamWriter, HLSStreamReader

log = logging.getLogger(__name__)

class SpayeeStreamWriter(HLSStreamWriter):
    def _decrypt_key(self,data):
        tmp1 = data[0:16]
        tmp2 = data[32:48]
        dec1 = AES.new(tmp2,AES.MODE_ECB)
        tmp3 = dec1.decrypt(tmp1)
        dec2 = AES.new(tmp3,AES.MODE_ECB)
        return dec2.decrypt(tmp2)

    def create_decryptor(self, key, sequence):
        if key.method != "AES-128":
            raise StreamError("Unable to decrypt cipher {0}", key.method)

        if not self.key_uri_override and not key.uri:
            raise StreamError("Missing URI to decryption key")

        key_uri = self.key_uri_override if self.key_uri_override else key.uri

        if self.key_uri != key_uri:
            res = self.session.http.get(key_uri, exception=StreamError,
                                        retries=self.retries,
                                        **self.reader.request_params)
            res.encoding = "binary/octet-stream"
            self.key_data = self._decrypt_key(res.content)
            self.key_uri = key_uri

        iv = key.iv or num_to_iv(sequence)

        # Pad IV if needed
        iv = b"\x00" * (16 - len(iv)) + iv

        return AES.new(self.key_data, AES.MODE_CBC, iv)

class SpayeeStreamReader(HLSStreamReader):
    __writer__ = SpayeeStreamWriter

class SpayeeStream(HLSStream):
    def open(self):
        reader = SpayeeStreamReader(self)
        reader.open()

        return reader

class Spayee(Plugin):
    url_re = re.compile(r"""https://cdn.spayee.in/.*.m3u8""")

    def __init__(self, url):
        super(Spayee, self).__init__(url)

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def _get_streams(self):
        return {"live":SpayeeStream(self.session,self.url)}

__plugin__ = Spayee

save it to streamlink\plugins\spayee.py

Only tested under python 2.7

Here is what i get :

streamlink --http-cookie "Cookie: c_user=2c6bf6dbacac303d72edcc5cd38afafdXfOIISNZp8b40r2V2xCU6tsulxvffMqoWxgi5x4HLPzJrZox1UvVTuPk66RrSjrJdf5c366881e766b6e88ba03343e10dc2; c_p=82e07fa2298c41ff73f6d4137998d90auPgDJ0EfI6id4IwhiuRSQQ==43d466565a11448f52fd7671a78e6004; id=0eff6a49-1374-4913-90b7-77482d73fd8e; _ga=GA1.2.970484282.1546776087; JSESSIONID=7C53A52AF3CBD3ECD89B346F710E7373; __extfc=1" https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bf7d067e4b068ec781593db/u/5c28ab6fe4b0efa6ebda1830/t/f8a99530b420bd8a80f939b023a83794/p/assets/videos/2018/11/23/5bf7d067e4b068ec781593db/hls_350k_.m3u8 best [cli][info] Found matching plugin spayee for URL https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bf7d067e4b068ec781593db/u/5c28ab6fe4b0efa6ebda1830/t/f8a99530b420bd8a80f939b023a83794/p/assets/videos/2018/11/23/5bf7d067e4b068ec781593db/hls_350k_.m3u8 [cli][info] Available streams: live (worst, best) [cli][info] Opening stream: live (hls) Exception in thread Thread-SpayeeStreamWriter: Traceback (most recent call last): File "threading.py", line 914, in _bootstrap_inner File "C:\Program Files (x86)\Streamlink\pkgs\streamlink\stream\segmented.py", line 167, in run self.write(segment, result) File "C:\Program Files (x86)\Streamlink\pkgs\streamlink\stream\hls.py", line 119, in write sequence.num) File "C:\Program Files (x86)\Streamlink\pkgs\streamlink\plugins\spayee.py", line 26, in create_decryptor if not self.key_uri_override and not key.uri: AttributeError: 'SpayeeStreamWriter' object has no attribute 'key_uri_override'

[cli][error] Try 1/1: Could not open stream <HLSStream('https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bf7d067e4b068ec781593db/u/5c28ab6fe4b0efa6ebda1830/t/f8a99530b420bd8a80f939b023a83794/p/assets/videos/2018/11/23/5bf7d067e4b068ec781593db/hls_350k_.m3u8')> (Failed to read data from stream: Read timeout) error: Could not open stream <HLSStream('https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bf7d067e4b068ec781593db/u/5c28ab6fe4b0efa6ebda1830/t/f8a99530b420bd8a80f939b023a83794/p/assets/videos/2018/11/23/5bf7d067e4b068ec781593db/hls_350k_.m3u8')>, tried 1 times, exiting [cli][info] Closing currently open stream...

I am using streamlink 0.14.2 and Python 3.7.2 also i've tried with python 2.7.0

Thanks!

jackyzy823 commented 5 years ago

You'd better using the lastest streamlink (branch master) from github instead of windows installer version. And i tested that it also works under python 3.

dexter101010 commented 5 years ago

You'd better using the lastest streamlink (branch master) from github instead of windows installer version. And i tested that it also works under python 3.

Thank you man! I will do that. Thanks again!

idileepd commented 5 years ago

I will be great @dexter101010 if you do a video

kiranfegade121 commented 4 years ago

You'd better using the lastest streamlink (branch master) from github instead of windows installer version. And i tested that it also works under python 3.

This doesn't work. I have tested, its not working. Can you please help?

kiranfegade121 commented 4 years ago

You'd better using the lastest streamlink (branch master) from github instead of windows installer version. And i tested that it also works under python 3.

Thank you man! I will do that. Thanks again!

Have you tried it? Is it working for you?

Ksab070 commented 3 years ago

You'd better using the lastest streamlink (branch master) from github instead of windows installer version. And i tested that it also works under python 3.

Thank you man! I will do that. Thanks again!

Can you help me? @dexter101010 I'm stuck with the same issue.

YuvrajRaghuvanshiS commented 2 years ago

Any update on this one? I have same issue with same domain, perhaps need to apply plugin manually if that is not automatically applied.

chrisdma commented 2 years ago

@jackyzy823 Can this Spayee plugin you wrote above be updated possibly? I have a qcdn.spayee.in m3u8 and have updated the plugin with a qcdn.spayee return but it still errors as so:

[session][info] Resolved plugin spayee with deprecated can_handle_url API [cli][info] Found matching plugin spayee for URL https://qcdn.spayee.in/spees/w/o/xxxxxxxxxxxx/index.m3u8 [cli][info] Available streams: live (worst, best) [cli][info] Opening stream: live (hls) [cli][info] Starting player: "C:\Program Files\VideoLAN\VLC\vlc.exe" [stream.hls][error] Attempted to play a variant playlist, use 'hls://https://qcdn.spayee.in/spees/w/o/xxxxxxxxxxxxxx/index.m3u8' instead [cli][error] Try 1/1: Could not open stream <HLSStream('https://qcdn.spayee.in/spees/w/o/xxxxxxxxxxxxx/index.m3u8', None)> (No data returned from stream) error: Could not open stream <HLSStream('https://qcdn.spayee.in/spees/w/o/xxxxxxxxxxxxxxxx/index.m3u8', None)>, tried 1 times, exiting [cli][info] Closing currently open stream...

Though when I copy paste the m3u8 link that says no data returned it starts downloading the m3u8 in my browser and its a valid m3u8. Any way this could possibly be updated? I can send you the m3u8 and key file if you need so.

Thanks!

jackyzy823 commented 2 years ago

the decrypt_key method is changed. (Note: pseudocode)

    def _decrypt_key(self,data):
        tmp1 = data[0:16]
        tmp2 = data[32:48]
        dec1 = AES.new(base64.b64decode(spka),AES.MODE_ECB)
        tmp3 = dec1.decrypt(tmp1)
        dec2 = AES.new(tmp3,AES.MODE_ECB)
        return dec2.decrypt(tmp2)

And the spka key is from web page's javascript and may be dynamically generated.

Sample

<script type="text/javascript" id="dsf239df">
    var ctx = "/s",
        isFirstLogin = "", cjs = CryptoJS;

    var defaultCoreColor = "#5755d9", theme1CoreColor = "#572b7a", theme2CoreColor = "#3f51b5", theme3CoreColor = "#009051", 
        theme4CoreColor = "#596439", theme5CoreColor = "#F0591D", theme6CoreColor = "#28a745", theme7CoreColor = "#1bbc9b", 
        theme8CoreColor = "#f38f20", theme9CoreColor = "#c0af01", theme10CoreColor = "#e8d3a4", spka = "YjM3ZGVhNWU4N2I1MTI2Yw==",
        webColor = defaultCoreColor,

spka = "YjM3ZGVhNWU4N2I1MTI2Yw=="

You can make a option like "--spayee-spka=xxx" to pass spka key to the this plugin.