繁体   English   中英

iOS推送通知(APN)超过GAE,SSL握手失败

[英]iOS Push Notifications (APNs) over GAE, SSL Handshake Failure

我试图使用此RPC处理程序从Google AppEngine应用程序实例中显示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的主要引用是对套接字对象 使用OpenSSLTLS / SSL包装器 ,更不用说几个StackOverflow帖子了。

请提供有关适当的密钥文件,certfile和ca_certs值以及可用于基于GAE的APN通信的任何其他建议或资源的建议。 谢谢〜

更新了问题......

最初的.p12已经使用Pusher进行了验证,并通过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等其他文件会导致返回原始握手失败。

考虑使用诸如pyapns之类的库,这是我用来获取推送通知以在GAE上工作的。 要测试您是否使用了正确的密钥/证书文件,您可以使用Pusher等应用程序。 另外,我知道要在GAE上获得SSL功能,你必须启用计费,所以这可能是问题所在。 祝好运!

相应的支持文件以创建通用推送通知客户端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)下载 ~encoust_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包装器17.3.4.3。 组合键和证书解释了为什么两者都是有效的参数选项。


在我提供最终代码块之前,我必须指出有关APNs地址的事情(这被证明是关键点,允许我解决握手失败并获得GAE和APN之间的SSL连接)

根据iOS Developer Library APNs Provider API

发送远程通知的第一步是与相应的APN服务器建立连接:

开发服务器:api.development.push.apple.com:443

生产服务器:api.push.apple.com:443

注意:您也可以在与APN通信时使用端口2197。 例如,您可以执行此操作,以允许APN通过防火墙进行流量,但阻止其他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)

...执行没有错误。

此外,通过实验,我发现当我应用内置的第三方ssl lib时,App Engine上的SSL相关错误通常会消失:

libraries:
- name: ssl
  version: latest

要么:

libraries:
- name: ssl
  version: "2.7"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM