簡體   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