iOS 通过 GAE 推送通知 (APN),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 中使用的主要参考文献是 Using OpenSSL and TLS/SSL wrapper for socket objects,更不用说一些 Whosebug 帖子了。
Please provide advice concerning the appropriate keyfile, certfile, and ca_certs values and any other advice or resources available for GAE based APNs communication. Thanks ~
更新问题...
原.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)会导致 return 原始握手失败。
考虑使用 pyapns, which is what I used to get push notifications to work on GAE. To test whether you're using the correct key/cert file, you can use apps like Pusher 等库。另外,我知道要在 GAE 上获得 SSL 功能,您必须启用计费,所以这可能就是问题所在。祝你好运!
此外,通过实验,我发现当我应用内置的第 3 方 ssl 库时,App Engine 上与 SSL 相关的错误通常会消失:
libraries:
- name: ssl
version: latest
或:
libraries:
- name: ssl
version: "2.7"
适当的支持文件以 Creating a Universal Push Notification Client SSL Certificate 开头作为 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
最后获得合格的证书颁发机构文件(来自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.
Entrust.net Certificate Authority (2048) download~entrust_2048_ca.cer
请注意,每个 GAE 实例都在 /etc/ca-certificates.crt 托管自己的证书颁发机构,如此处所述,Using 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
The first step in sending a remote notification is to establish a connection with the appropriate APNs server:
Development server: api.development.push.apple.com:443
Production server: api.push.apple.com:443
Note: You can alternatively use port 2197 when communicating with APNs. You might do this, for example, to allow APNs traffic through your firewall but to block other HTTPS traffic.
但直到我深入研究 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)
...执行无误。
我正在尝试使用此 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 中使用的主要参考文献是 Using OpenSSL and TLS/SSL wrapper for socket objects,更不用说一些 Whosebug 帖子了。
Please provide advice concerning the appropriate keyfile, certfile, and ca_certs values and any other advice or resources available for GAE based APNs communication. Thanks ~
更新问题...
原.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)会导致 return 原始握手失败。
考虑使用 pyapns, which is what I used to get push notifications to work on GAE. To test whether you're using the correct key/cert file, you can use apps like Pusher 等库。另外,我知道要在 GAE 上获得 SSL 功能,您必须启用计费,所以这可能就是问题所在。祝你好运!
此外,通过实验,我发现当我应用内置的第 3 方 ssl 库时,App Engine 上与 SSL 相关的错误通常会消失:
libraries:
- name: ssl
version: latest
或:
libraries:
- name: ssl
version: "2.7"
适当的支持文件以 Creating a Universal Push Notification Client SSL Certificate 开头作为 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
最后获得合格的证书颁发机构文件(来自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.
Entrust.net Certificate Authority (2048) download~entrust_2048_ca.cer
请注意,每个 GAE 实例都在 /etc/ca-certificates.crt 托管自己的证书颁发机构,如此处所述,Using 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
The first step in sending a remote notification is to establish a connection with the appropriate APNs server:
Development server: api.development.push.apple.com:443
Production server: api.push.apple.com:443
Note: You can alternatively use port 2197 when communicating with APNs. You might do this, for example, to allow APNs traffic through your firewall but to block other HTTPS traffic.
但直到我深入研究 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)
...执行无误。