APNS,当程序在 Mac 上运行时,如何修复树莓派上的“无法获取本地颁发者证书”错误?

Posted

技术标签:

【中文标题】APNS,当程序在 Mac 上运行时,如何修复树莓派上的“无法获取本地颁发者证书”错误?【英文标题】:APNS, how do I fix "unable to get local issuer certificate" error on raspberry Pi when program works on Mac? 【发布时间】:2021-06-01 11:39:00 【问题描述】:

我正在尝试通过可在我的 Mac 上运行的 python 脚本发送 ios 推送通知,但程序收到错误“httpcore.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] 证书验证失败:无法获取本地颁发者证书 (_ssl .c:1122)”当我在我的树莓派上运行它时。我尝试添加 GeoTrust 和新的 AAACertificateServices 证书,但也许我在那里做错了什么。证书永远让我感到困惑,所以我非常感谢您的帮助。我正在使用基于令牌的身份验证,所以我对错误首先指的是什么证书有点困惑......

在带有 Debian 的 Raspberry Pi 3 Model B 上运行。

整个 Traceback 是:

    Traceback (most recent call last):
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_exceptions.py", line 326, in map_exceptions
    yield
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_client.py", line 861, in _send_single_request
    (status_code, headers, stream, ext) = transport.request(
  File "/home/jake/.local/lib/python3.9/site-packages/httpcore/_sync/connection_pool.py", line 218, in request
    response = connection.request(
  File "/home/jake/.local/lib/python3.9/site-packages/httpcore/_sync/connection.py", line 93, in request
    self.socket = self._open_socket(timeout)
  File "/home/jake/.local/lib/python3.9/site-packages/httpcore/_sync/connection.py", line 119, in _open_socket
    return self.backend.open_tcp_stream(
  File "/home/jake/.local/lib/python3.9/site-packages/httpcore/_backends/sync.py", line 143, in open_tcp_stream
    return SyncSocketStream(sock=sock)
  File "/usr/local/opt/python-3.9.0/lib/python3.9/contextlib.py", line 135, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/jake/.local/lib/python3.9/site-packages/httpcore/_exceptions.py", line 12, in map_exceptions
    raise to_exc(exc) from None
httpcore.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1122)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/jake/Programming/Security/MQTT/NotificationServer.py", line 73, in <module>
    sendAlarmNotification()
  File "/home/jake/Programming/Security/MQTT/NotificationServer.py", line 66, in sendAlarmNotification
    r = client.post('/3/device/'.format(server, deviceToken), json=notification, headers=headers)
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_client.py", line 992, in post
    return self.request(
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_client.py", line 733, in request
    return self.send(
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_client.py", line 767, in send
    response = self._send_handling_auth(
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_client.py", line 805, in _send_handling_auth
    response = self._send_handling_redirects(
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_client.py", line 837, in _send_handling_redirects
    response = self._send_single_request(request, timeout)
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_client.py", line 861, in _send_single_request
    (status_code, headers, stream, ext) = transport.request(
  File "/usr/local/opt/python-3.9.0/lib/python3.9/contextlib.py", line 135, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/jake/.local/lib/python3.9/site-packages/httpx/_exceptions.py", line 343, in map_exceptions
    raise mapped_exc(message, **kwargs) from exc  # type: ignore
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1122)

我的代码是

import httpx
import time
from jwcrypto import jwt, jwk

devServer = "https://api.sandbox.push.apple.com:443"
prodServer = "https://api.push.apple.com:443"
server = devServer 

pemFilePath = "pushCerts/PushNotificationAuthKey_**********.p8"

# This generates an auth token with the current time, using our pem files
def generateAuthToken(): 
    issueTime = int(time.time())
    token = jwt.JWT( header= "alg" : "ES256", "kid" : "**********", claims= "iss": "********", "iat": issueTime )
    with open(pemFilePath, "rb") as pemfile:
        key = jwk.JWK.from_pem(pemfile.read())
    token.make_signed_token(key)
    return token.serialize()
 

deviceToken = "long device token" 
authToken = 'bearer ' + generateAuthToken()
pushType = 'alert'                                      
expiration = '3600'                                         
priority = '10'                                                     
topic = 'com.MyName.MyAppName'  

headers =  
    'authorization' : authToken,
    'apns-push-type' : pushType,
    'apns-expiration' : expiration,
    'apns-priority' : priority,
    'apns-topic' : topic
    

def sendAlarmNotification():
    notification =  "aps" :  "alert": "Alarm Triggered!", "sound" :  "critical": 1, "name": "Alarm.caf", "volume": 1.0 
    client = httpx.Client(http2=True)
    try:
        r = client.post('/3/device/'.format(server, deviceToken), json=notification, headers=headers)
        print(r)
    finally:
        client.close()

sendAlarmNotification()

另外,运行 openssl s_client -connect api.sandbox.push.apple.com:443 后返回成功验证,所以我真的很困惑。

【问题讨论】:

【参考方案1】:

从这里:https://www.python-httpx.org/advanced/ 看起来您需要在 httpx 调用中指定 pem 文件:

导入httpx

r = httpx.get("https://example.org", verify="path/to/client.pem")

【讨论】:

那么为什么我需要在 Pi 而不是我的 Mac 上执行此操作,因为程序在我的 Mac 上完美运行?由于我通过令牌进行身份验证,因此我将提供 GeoTrust pem 文件? 尝试使用 GeoTrust 证书无济于事:/ 您将访问 apple.com 地址,并且 MAC 默认情况下可能具有该证书链,因此它可以与 Apple 通信,而 Pi 默认情况下不会。使用基于令牌的身份验证时出现此问题的原因是连接是 SSL,需要首先验证。不确定为什么存在 GeoTrust,您需要验证 api.push.apple.com 使用的证书,该证书看起来是由 Apple 颁发的。您可以尝试 verify=False 如果可行,则说明您没有正确的证书更改来验证 api.push.apple.com 使用的 ssl GeoTrust 是您需要的 Apple 的 APNS 证书,他们的网站声明“创建该连接需要在您的每个提供商服务器上安装 GeoTrust Global CA 根证书。”如果我理解正确?我不知道我会使用什么其他证书......我试过 verify=False 并没有改变任何东西。还有更多见解吗?

以上是关于APNS,当程序在 Mac 上运行时,如何修复树莓派上的“无法获取本地颁发者证书”错误?的主要内容,如果未能解决你的问题,请参考以下文章

当应用程序在后台时,APNS令牌会更改还是无效?

raspbian 如何启动系统修复

如何修复“读写数据沙箱:使用 Mac Catalyst 时出错”

如何以与 WhatsApp 相同的方式实施 APNS?

当应用程序在本地运行时如何修复 PHP 上的重定向错误

rust 交叉编译树莓派程序