首页 > 编程问答 >binascii.Error:无效的 base64 编码字符串:数据字符数 (41) 不能多于 1 4 的倍数

binascii.Error:无效的 base64 编码字符串:数据字符数 (41) 不能多于 1 4 的倍数

时间:2024-07-25 09:37:53浏览次数:15  
标签:python django base64 web-push django-push-notifications

我正在尝试使用 py-vapid pywebpush django-push-notifications 通过 Webpush 发送通知。当我尝试从 django 管理网站发送测试通知时,我在控制台中收到此回溯日志:

      | Internal Server Error: /djangoadmin/push_notifications/webpushdevice/
      | Traceback (most recent call last):
      |   File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 518, in thread_handler
      |     raise exc_info[1]
      |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 38, in inner
      |     response = await get_response(request)
      |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 233, in _get_response_async
      |     response = await wrapped_callback(request, *callback_args, **callback_kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 468, in __call__
      |     ret = await asyncio.shield(exec_coro)
      |   File "/usr/local/lib/python3.8/site-packages/asgiref/current_thread_executor.py", line 40, in run
      |     result = self.fn(*self.args, **self.kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 522, in thread_handler
      |     return func(*args, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 616, in wrapper
      |     return self.admin_site.admin_view(view)(*args, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
      |     response = view_func(request, *args, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
      |     response = view_func(request, *args, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 232, in inner
      |     return view(request, *args, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
      |     return bound_method(*args, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
      |     response = view_func(request, *args, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1723, in changelist_view
      |     response = self.response_action(request, queryset=cl.get_queryset(request))
      |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1408, in response_action
      |     response = func(self, request, queryset)
      |   File "/usr/local/lib/python3.8/site-packages/push_notifications/admin.py", line 109, in send_message
      |     self.send_messages(request, queryset)
      |   File "/usr/local/lib/python3.8/site-packages/push_notifications/admin.py", line 38, in send_messages
      |     r = device.send_message("Test single notification")
      |   File "/usr/local/lib/python3.8/site-packages/push_notifications/models.py", line 270, in send_message
      |     return webpush_send_message(self, message, **kwargs)
      |   File "/usr/local/lib/python3.8/site-packages/push_notifications/webpush.py", line 35, in webpush_send_message
      |     response = webpush(
      |   File "/usr/local/lib/python3.8/site-packages/pywebpush/__init__.py", line 560, in webpush
      |     vv = Vapid.from_string(private_key=vapid_private_key)
      |   File "/usr/local/lib/python3.8/site-packages/py_vapid/__init__.py", line 144, in from_string
      |     key = b64urldecode(pkey)
      |   File "/usr/local/lib/python3.8/site-packages/py_vapid/utils.py", line 14, in b64urldecode
      |     return base64.urlsafe_b64decode(data + b"===="[len(data) % 4:])
      |   File "/usr/local/lib/python3.8/base64.py", line 133, in urlsafe_b64decode
      |     return b64decode(s)
      |   File "/usr/local/lib/python3.8/base64.py", line 87, in b64decode
      |     return binascii.a2b_base64(s)
      | binascii.Error: Invalid base64-encoded string: number of data characters (41) cannot be 1 more than a multiple of 4

它似乎说我的密钥没有采用有效的格式来转换为 Base64,但我检查了 pywebpush文档和我的密钥格式正确。它们是用 VAPID EC2 private key PEM file 生成的,如下所示: py-vapid 以下是有问题的测试密钥:

import os
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, PublicFormat
from py_vapid import Vapid
import base64

PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Generate VAPID key pair
vapid = Vapid()
vapid.generate_keys()

# Get public and private keys for the vapid key pair
vapid.save_public_key(os.path.join(PROJECT_ROOT, "keys", "webpush", "public_key.pem"))
public_key_bytes = vapid.public_key.public_bytes(Encoding.X962, PublicFormat.UncompressedPoint)

vapid.save_key(os.path.join(PROJECT_ROOT, "keys", "webpush", "private_key.pem"))


# Convert the public key to applicationServerKey format
application_server_key = base64.urlsafe_b64encode(public_key_bytes).replace(b'=', b'').decode('utf8')

with open(os.path.join(PROJECT_ROOT, "keys", "webpush", "ApplicationServerKey.key"), "w") as f:
    f.write(application_server_key)

我尝试使用 OpenSSL 手动生成密钥,但它仍然无法工作。

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFCZ0WxsNENwKK3Pf
c0HFZPDlr/KAmjIwB6Hz/OdH/YKhRANCAASFpOW98Q4xfCjD0Taog9ZstRQgS+Ch
L7HoOBGHFZAQwpEd83Qt58bnUCIZFwMZc4tyHKV+b0UxM2cMzm8pMI4z
-----END PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhaTlvfEOMXwow9E2qIPWbLUUIEvg
oS+x6DgRhxWQEMKRHfN0LefG51AiGRcDGXOLchylfm9FMTNnDM5vKTCOMw==
-----END PUBLIC KEY-----

I've tried to generate keys manually using OpenSSL but it still won't work.


错误信息 "binascii.Error: Invalid base64-encoded string: number of data characters (41) cannot be 1 more than a multiple of 4" 表明你正试图将一个无效的 Base64 字符串解码。问题并非出在你的密钥格式,而是出在你如何读取或使用密钥。

看起来你在代码中并没有直接使用私钥进行 Base64 解码。 py_vapid 库应该在内部处理这个过程。问题可能出现在以下地方:

  1. 错误地读取私钥: 确保你正确地从文件中读取私钥,并且没有引入额外的空格或字符。使用 open() 函数时,最好使用 'rb' 模式以二进制格式读取文件,避免潜在的编码问题:
with open(os.path.join(PROJECT_ROOT, "keys", "webpush", "private_key.pem"), "rb") as f:
    private_key = f.read()

# ... 然后使用 private_key 字符串
  1. vapid_private_key 变量的值不正确: 确认你将读取的私钥内容正确地传递给了 webpush() 函数中的 vapid_private_key 参数。 打印 vapid_private_key 的值,确保它包含正确的私钥字符串,包括 "-----BEGIN PRIVATE KEY-----" 和 "-----END PRIVATE KEY-----" 部分。

  2. django-push-notifications 配置问题: 检查 django-push-notifications 的设置,确保你正确地配置了 VAPID 密钥路径或变量。

调试建议:

  1. 打印 pkey 的值: py_vapid/__init__.py 文件的 from_string 函数中,添加一行代码打印 pkey 的值,看看它是否是预期的私钥字符串:
    def from_string(cls, pkey):
        print("pkey:", pkey)  # 添加这行
        key = b64urldecode(pkey)
        # ...
  1. 检查 django-push-notifications 设置: 仔细检查 settings.py 文件或其他相关配置文件,确保你正确配置了 VAPID 密钥路径或变量。

  2. 使用调试器逐步执行代码: 使用调试器可以帮助你逐行检查代码,观察变量的值,并确定问题的确切位置。

如果以上步骤都不能解决问题,请提供以下信息:

  • 你是如何调用 webpush() 函数的?请分享相关代码片段。
  • django-push-notifications 的版本号。
  • 你是如何配置 django-push-notifications 的 VAPID 密钥设置的?

通过提供更多信息,我可以更好地帮助你解决问题。

标签:python,django,base64,web-push,django-push-notifications
From: 78791129

相关文章

  • 如何在 Mac 上运行 Python 文件来读取 txt 文件并将其写入外部硬盘?
    我目前有一个充满了我想阅读的epub的文件夹,一个我已经阅读过并想再次阅读的epub的文件夹,以及一个相应的文件,其中每个文件都有epub文件的名称。问题是,这些文件夹仅位于我的外部硬盘上。我想要做的是让我的脚本解析这些文件夹中的epub列表,并在我的下载文件夹中创建最新的副......
  • 深入探索:使用Python进行网站数据加载逻辑分析与请求
    作为一名资深的Python程序员,我经常需要从网站中提取数据以供分析或进一步处理。这项任务涉及到对网站数据加载逻辑的深入分析,以及使用Python进行高效的网络请求。在本文中,我将分享如何分析网站的数据加载方式,并使用Python的requests库来模拟浏览器行为,获取所需的数据。网站......
  • 如何将 Python 列表添加到 Excel 中已有值的列的末尾?
    我目前正在尝试编写一个程序,将值附加到列表中,然后将这些值添加到Excel数据表中的列中。每次运行该程序时,我都希望在同一列的末尾添加更多值。所以我不确定如何解决这个问题,而且我在网上找到的其他答案也没有取得多大成功。以下是使用openpyxl库在Python中将......
  • 如何学习Python:糙快猛的大数据之路(学习地图)
    在这个AI和大数据主宰的时代,Python无疑是最炙手可热的编程语言之一。无论你是想转行还是提升技能,学习Python都是一个明智之选。但是,该如何开始呢?今天,让我们聊聊"糙快猛"的Python学习之道。什么是"糙快猛"学习法?"糙快猛"学习法,顾名思义,就是:糙:不追求完美,允许存......
  • 如何使用 Django 在 neo4j 中创建节点
    我正在开发一个网络应用程序。对于后端,我选择了Django,作为数据库,我想使用图形数据库,所以我选择了neo4j。有一个Python库neomodel用于使用neo4j和一个特定的库django_neomodel要合并|||使用Django。neomodel我可以使用Django和neo4j从......
  • Python 中 __get__ 方法的内部原理
    我正在摆弄描述符,结果碰壁了。我以为我可以像使用任何其他方法一样直接调用它,但显然,它似乎不一致或者我遗漏了一些东西。假设我有一个用作描述符的坐标类:|||还有一个Point类,它有2个坐标属性:classCoordinate:def__set_name__(self,owner,name):self._na......
  • 使用带有私钥的云前端生成签名 URL 的问题..使用 Python 3.7 为带有空格的 S3 对象生
    我在使用Python3.7为S3对象生成签名URL时遇到问题。具体来说,键中带有空格的对象的URL会导致“访问被拒绝”错误,而没有空格的对象的URL通常工作正常。但是,并非所有不带空格的对象都能正常工作,带空格的对象始终会失败。fromdatetimeimportdatetime,timedeltaimpo......
  • 有没有更好的方法来在存储库中的一组 python 程序之间共享公共代码
    当我想要快速、轻松地做许多不同的事情时,我会选择Python-即我总是会得到许多Python“程序”-例如一组脚本-或者如果我正在玩一些东西,一堆测试程序等-即始终是许多不同程序的松散集合。但是,我会分享某些内容。例如,如果我正在使用AI-我可能有30个左右完全不相......
  • 如何在Python中从两个不同长度的列表创建DataFrame,为第二个列表中的每个值重复第一个
    我是一个超级初学者,所以请耐心等待。我觉得这应该很容易,但我无法弄清楚。我不确定是否应该创建两个列表,然后将它们组合起来,或者是否有办法以这种方式直接创建DataFrame。我需要一列包含这些值:df=pd.DataFrame({'x1':np.linspace(-2.47,2.69,num=101)})然后我将值A......
  • Python multiprocessing.connection.Connection 的行为不符合规范
    根据python规范,recv()pythonConnection的方法,(从multiprocessing.Pipe()返回,当管道为空且管道的另一端关闭时抛出EOFError(这里参考:https://docs.python.org/3.9/library/multiprocessing.html#multiprocessing.connection.Connection.re......