简体   繁体   English

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

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

I am attempting to show proof of concept for iOS Push Notifications from a Google AppEngine application instance using this RPC handler... 我试图使用此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)

And need help resolving this error when executing "_ssl.connect(apns_address)" 执行“_ssl.connect(apns_address)”时需要帮助解决此错误

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

My PEM file (derived from a .p12) and device token were generated a week ago by a mobile developer on our team, suggestions for validating these would be helpful. 我的PEM文件(源自.p12)和设备令牌是我们团队的移动开发人员一周前生成的,验证这些内容的建议会很有帮助。 For now I believe there are current and valid. 现在我相信目前有效。

While the TLSv1 protocol is being specified, I've notice the handshake failure identifies sslv3. 在指定TLSv1协议时,我注意到握手失败标识了sslv3。

I have attempted many variations and combination of wrap_socket and apns_address, and am consistently stopped by the handshake failure. 我尝试过wrap_socket和apns_address的许多变体和组合,并且由于握手失败而一直停止。 Which leads me to suspect a problem with the way I am applying the pem certificate. 这导致我怀疑我使用pem证书的方式有问题。

The primary references I have been using for wrap_socket are Using OpenSSL and TLS/SSL wrapper for socket objects , not to mention more than a few StackOverflow posts. 我一直用于wrap_socket的主要引用是对套接字对象 使用OpenSSLTLS / SSL包装器 ,更不用说几个StackOverflow帖子了。

Please provide advice concerning the appropriate keyfile, certfile, and ca_certs values and any other advice or resources available for GAE based APNs communication. 请提供有关适当的密钥文件,certfile和ca_certs值以及可用于基于GAE的APN通信的任何其他建议或资源的建议。 Thanks ~ 谢谢〜

Updated question... 更新了问题......

The original .p12 has been validated using Pusher, and divided via openssl... 最初的.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

I'm receiving a new error which appears related to the ca_certs... 我收到一个与ca_certs相关的新错误...

SSLError: [Errno 0] _ssl.c:343: error:00000000:lib(0):func(0):reason(0)

Removing the ca_certs requirement or passing in other files like the .p12 or the .crt result in a return to the original handshake failure. 删除ca_certs要求或传入.p12或.crt等其他文件会导致返回原始握手失败。

Look into using a library such as pyapns , which is what I used to get push notifications to work on GAE. 考虑使用诸如pyapns之类的库,这是我用来获取推送通知以在GAE上工作的。 To test whether you're using the correct key/cert file, you can use apps like Pusher . 要测试您是否使用了正确的密钥/证书文件,您可以使用Pusher等应用程序。 Also, I know that to get SSL capabilities on GAE you have to enable billing, so maybe that's the problem. 另外,我知道要在GAE上获得SSL功能,你必须启用计费,所以这可能是问题所在。 Good luck! 祝好运!

The appropriate support files start with Creating a Universal Push Notification Client SSL Certificate as a p12 file. 相应的支持文件以创建通用推送通知客户端SSL证书作为p12文件开始。

Next, utilizing command line openssl to parse the p12 into the desired certificate and key files... 接下来,利用命令行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

And finally to obtain a qualified Certificate Authority file (from Troubleshooting Push Notifications ) 最后获取合格的证书颁发机构文件(来自故障排除推送通知

In addition to the SSL identity (certificate and associated private key) created by Member Center, you should also install the Entrust CA (2048) root certificate on your provider. 除了由成员中心创建的SSL身份(证书和关联的私钥)之外,您还应该在提供者上安装Entrust CA(2048)根证书。

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

Note that every GAE instance hosts its own Certificate Authority at /etc/ca-certificates.crt as described here, Using OpenSSL . 请注意,每个GAE实例都在/etc/ca-certificates.crt上托管自己的证书颁发机构,如此处所述, 使用OpenSSL


With these files added to your project you can make one of two, equally valid, ssl socket objects... 将这些文件添加到项目中,您可以创建两个同样有效的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)

...or... ...要么...

_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. 套接字对象的TLS / SSL包装器17.3.4.3。 Combined key and certificate explains why both are valid parameter options. 组合键和证书解释了为什么两者都是有效的参数选项。


Before I provide the final code block, I have to point out something concerning the APNs address (this proved to be the key point, allowing me to resolve the handshake failure and obtain an SSL connection between GAE and APNs) 在我提供最终代码块之前,我必须指出有关APNs地址的事情(这被证明是关键点,允许我解决握手失败并获得GAE和APN之间的SSL连接)

According to the iOS Developer Library APNs Provider API 根据iOS Developer Library APNs Provider API

The first step in sending a remote notification is to establish a connection with the appropriate APNs server: 发送远程通知的第一步是与相应的APN服务器建立连接:

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

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

Note: You can alternatively use port 2197 when communicating with APNs. 注意:您也可以在与APN通信时使用端口2197。 You might do this, for example, to allow APNs traffic through your firewall but to block other HTTPS traffic. 例如,您可以执行此操作,以允许APN通过防火墙进行流量,但阻止其他HTTPS流量。

But it wasn't until I dug into the Pusher source that I discovered the APNs addresses to which I could connect... 但直到我挖掘Pusher来源,才发现我可以连接的APNs地址......

gateway.sandbox.push.apple.com:2195

gateway.push.apple.com:2195


Without further ado... 无需再费周折...

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)

...executes without error. ...执行没有错误。

Also, from experimenting, I've found out that SSL related errors on App Engine usually go away when I apply the built-in 3rd party ssl lib: 此外,通过实验,我发现当我应用内置的第三方ssl lib时,App Engine上的SSL相关错误通常会消失:

libraries:
- name: ssl
  version: latest

or: 要么:

libraries:
- name: ssl
  version: "2.7"

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

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