iOS Push通知(APNs)通过GAE的SSL握手失败

5

我正在尝试展示如何从Google App Engine应用程序实例使用此RPC处理程序发送iOS推送通知的概念验证...

PAYLOAD = {'aps': {'alert':'Push!','sound':'default'}}
TOKEN = '[...]'


class APNsTest(BaseRPCHandler):

  def get(self, context, name):
    self._call_method(context, name)

  def send_push(self):

    # certificate files
    filename = 'VisitorGuidePush'
    abs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../archive/certificate'))
    ca_certs = os.path.abspath(os.path.join(abs_path, '%s.ca'%filename))
    certfile = os.path.abspath(os.path.join(abs_path, '%s.crt'%filename))
    keyfile = os.path.abspath(os.path.join(abs_path, '%s.key'%filename))

    # serialize payload
    payload = json.dumps(PAYLOAD)

    # APNS server address...
    # apns_address = ('api.development.push.apple.com', 443) # Development server
    # apns_address = ('api.development.push.apple.com', 2197) # Development server
    # apns_address = ('api.push.apple.com', 443) # Production server
    apns_address = ('api.push.apple.com', 2197) # Production server

    # a socket to connect to APNS over SSL
    _sock = socket.socket()
    _ssl = ssl.wrap_socket(_sock, keyfile=keyfile,
                                  certfile=certfile,
                                  server_side=False,
                                  cert_reqs=ssl.CERT_REQUIRED,
                                  ssl_version=ssl.PROTOCOL_TLSv1,
                                  ca_certs=ca_certs)
    _ssl.connect(apns_address)

    # Generate a notification packet
    token = binascii.unhexlify(TOKEN)
    fmt = '!cH32sH{0:d}s'.format(len(payload))
    cmd = '\x00'
    message = struct.pack(fmt, cmd, len(token), token, len(payload), payload)

    _ssl.write(message)
    _ssl.close()

    return self.response_result(PAYLOAD)

当执行“_ssl.connect(apns_address)”时,需要帮助解决此错误:

SSLError: [Errno 1] _ssl.c:507: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure

一个星期前,我们团队的移动开发人员生成了我的PEM文件(从.p12派生)和设备令牌,有关验证这些内容的建议将非常有用。目前我相信它们是当前和有效的。

虽然正在指定TLSv1协议,但我注意到握手失败标识sslv3。

我尝试了许多wrap_socket和apns_address的变化和组合,并且一直被握手失败所阻止。这让我怀疑我应用pem证书的方式存在问题。

我主要参考wrap_socket的文献是使用OpenSSL用于套接字对象的TLS / SSL包装器,更不用说StackOverflow上的许多帖子了。

请提供有关GAE基于APNs通信的适当keyfile、certfile和ca_certs值以及任何其他可用资源或建议。谢谢~

更新问题...

使用Pusher验证了原始的.p12,并通过openssl分割...

openssl pkcs12 -in vgp.p12 -out VisitorGuidePush.key -nodes -nocerts
openssl pkcs12 -in vgp.p12 -out VisitorGuidePush.crt -nodes -nokeys
openssl pkcs12 -in vgp.p12 -out VisitorGuidePush.ca -nodes -cacerts

我收到了一个新的错误,似乎与ca_certs相关... SSLError: [Errno 0] _ssl.c:343: error:00000000:lib(0):func(0):reason(0) 如果删除ca_certs的要求或传递其他文件(如.p12或.crt),则会返回到原始的握手失败状态。

使用 Pusher 应用程序,我能够确认我的 PEM 文件存在问题。在获取原始的 .p12 文件后,Pusher 能够建立 APNs 连接。 - Buffalo Rabor
3个回答

1

可以考虑使用像pyapns这样的库,这是我在GAE上实现推送通知所使用的。为了测试你是否使用了正确的密钥/证书文件,你可以使用像Pusher这样的应用程序。此外,我知道要在GAE上获得SSL功能,必须启用计费,所以这可能是问题所在。祝好运!


计费已启用,已验证,已经运行了一段时间。现在正在使用Pusher,这非常有帮助,因为Pusher应用程序正在抱怨私钥(非常有用的反馈)。 - Buffalo Rabor

1
适当的支持文件以创建通用推送通知客户端SSL证书为起点,作为一个p12文件。接下来,使用命令行openssl将p12解析为所需的证书和密钥文件...
openssl pkcs12 -in VisitorGuide.p12 -out VisitorGuide.key -nodes -nocerts
openssl pkcs12 -in VisitorGuide.p12 -out VisitorGuide.crt -nodes -nokeys
openssl pkcs12 -in VisitorGuide.p12 -out VisitorGuide.pem -nodes

最后,您需要获取一个合格的证书颁发机构文件(来自推送通知故障排除

除了由会员中心创建的SSL身份(证书和相关私钥)之外,您还应在提供程序上安装Entrust CA(2048)根证书。

Entrust.net证书颁发机构(2048)下载 ~ entrust_2048_ca.cer

请注意,每个GAE实例都托管其自己的证书颁发机构,位于/ etc / ca-certificates.crt,如此处所述,使用OpenSSL


使用这些文件添加到您的项目中,您可以创建两个同样有效的SSL套接字对象...
_ssl = ssl.wrap_socket(_sock, keyfile=VisitorGuide.key,
                              certfile=VisitorGuide.crt,
                              server_side=False,
                              cert_reqs=ssl.CERT_REQUIRED,
                              ssl_version=ssl.PROTOCOL_TLSv1,
                              ca_certs=entrust_2048_ca.cer)

...或者...

_ssl = ssl.wrap_socket(_sock, certfile=VisitorGuide.pem,
                              server_side=False,
                              cert_reqs=ssl.CERT_REQUIRED,
                              ssl_version=ssl.PROTOCOL_TLSv1,
                              ca_certs=entrust_2048_ca.cer)

TLS/SSL wrapper for socket objects 17.3.4.3. Combined key and certificate 解释了为什么两者都是有效的参数选项。


在我提供最终的代码块之前,我必须指出与APNs地址有关的一些事情(这被证明是关键点,使我解决了握手失败并在GAE和APNs之间获得了SSL连接)。
根据iOS开发者文库APNs Provider API
发送远程通知的第一步是与适当的APNs服务器建立连接:
开发服务器:api.development.push.apple.com:443
生产服务器:api.push.apple.com:443
注意:您也可以在与APNs通信时使用端口2197。例如,您可能会这样做,以允许APNs流量通过防火墙,但阻止其他HTTPS流量。
但直到我深入研究Pusher源代码,才发现我可以连接到哪些APNs地址...

gateway.sandbox.push.apple.com:2195

gateway.push.apple.com:2195


不再拖延,...
class APNsTest(BaseRPCHandler):

  def get(self, context, name):
    self._call_method(context, name)

  def send_push(self):

    # certificate files
    abs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../cert'))
    pem_file = os.path.abspath(os.path.join(abs_path, 'VisitorGuide.pem'))
    ca_certs = '/etc/ca-certificates.crt'

    # APNS server address...
    apns_address = ('gateway.sandbox.push.apple.com', 2195)
    # apns_address = ('gateway.push.apple.com', 2195)

    # a socket to connect to APNS over SSL
    _sock = socket.socket()
    _ssl = ssl.wrap_socket(_sock, certfile=pem_file,
                                  server_side=False,
                                  cert_reqs=ssl.CERT_REQUIRED,
                                  ssl_version=ssl.PROTOCOL_TLSv1,
                                  ca_certs=ca_certs)
    _ssl.connect(apns_address)

    # a notification packet
    payload = json.dumps(PAYLOAD)
    token = binascii.unhexlify(TOKEN)
    fmt = '!cH32sH{0:d}s'.format(len(payload))
    cmd = '\x00'
    message = struct.pack(fmt, cmd, len(token), token, len(payload), payload)

    _ssl.write(message)
    _ssl.close()

    return self.response_result(PAYLOAD)

...无错误执行。


0

同时,通过实验,我发现在应用内置的第三方 SSL 库时,App Engine 上通常会消除与 SSL 相关的错误:

libraries:
- name: ssl
  version: latest

或者:

libraries:
- name: ssl
  version: "2.7"

1
好建议,我一直在我的app.ymal中使用“- name: ssl version:“2.7”符号,并且此处理程序正在读取import ssl而没有错误。 - Buffalo Rabor
握手失败了吗? - Jeffrey Godwyll
继续执行,或替换为“SSLError:[Errno 0] _ssl.c:343:error:00000000:lib(0):func(0):reason(0)”。我遇到的问题似乎集中在证书上。 - Buffalo Rabor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接