如何从钥匙串获取 NEPacketTunnelProvider Networkextension 的身份?
How to get an identity for NEPacketTunnelProvider Networkextension from keychain?
我正在使用 NEPacketTunnelProvider 进行网络扩展。我正在使用具有 com.apple.vpn.managed 负载的配置文件。此外,我使用带有 com.apple.security.pkcs12 负载的 ClientCertificate 身份验证。根据 NETunnelProviderManager 文档,我的扩展应该可以使用 com.apple.managed.vpn.shared 钥匙串访问组检索此身份。
如果我在钥匙串中查询身份,我总是得到错误代码 -25300
。根据 www.osstatus.com 这意味着:"The item cannot be found."
代码 1:
我尝试使用 protocolConfiguration 提供的持久引用来检索身份。
class PacketTunnelProvider: NEPacketTunnelProvider {
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
NSLog("vpn-service startTunnel")
let pc = self.protocolConfiguration
NSLog("vpn-service protocolConfiguration " + pc.debugDescription)
if let identity = pc.identityReference {
let persistentRef = identity as NSData
NSLog("vpn-service persistentRef " + persistentRef.description)
var copyResult: AnyObject?
let copyErr = SecItemCopyMatching([
kSecValuePersistentRef as String: persistentRef,
kSecReturnData as String: true
] as CFDictionary, ©Result)
NSLog("vpn-service getCert copyErr " + copyErr.description)
}
}
}
输出为:
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service startTunnel
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service protocolConfiguration
type = plugin
identifier = 3B39941E-AF39-45CE-B869-68AF392FBCA0
serverAddress = DEFAULT
password = {
domain = user
accessGroup = com.apple.managed.vpn.shared
}
identity = {
identifier = <Subject Name Common Name of the identity installed by the com.apple.security.pkcs12 payload>
persistentReference = <69646e74 00000000 00000011>
domain = user
}
identityDataImported = NO
identityReference = <69646e74 00000000 00000011>
proxySettings = {
autoProxyDiscovery = NO
autoProxyConfigurationEnabled = NO
HTTPEnabled = NO
HTTPSEnabled = NO
FTPEnabled = NO
SOCKSEnabled = NO
RTSPEnabled = NO
gopherEnabled = NO
excludeSimpleHostnames = NO
usePassiveFTP = YES
}
disconnectOnSleep = NO
disconnectOnIdle = NO
disconnectOnIdleTimeout = 0
disconnectOnWake = NO
disconnectOnWakeTimeout = 0
pluginType = <my-bundle-id>
authenticationMethod = 1
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service persistentRef <69646e74 00000000 00000011>
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service getCert copyErr -25300
代码2:
如果我尝试在没有参考的情况下检索所有身份,我也会得到 -25300
。
let getQuery: [String: Any] = [
kSecClass as String: kSecClassIdentity,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: true,
]
var item: CFTypeRef?
let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
NSLog("vpn-service status: " + status.description)
我重新检查了构建结果是否获得了钥匙串访问组:
codesign -d --ent :- build/Debug-iphoneos/agent.app/PlugIns/vpn.appex/
Executable=/Users/timb/projects/xcode/ios-client/build/Debug-iphoneos/agent.app/PlugIns/vpn.appex/vpn
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string><my-application-identifier-of-the-network-extension></string>
<key>com.apple.developer.networking.networkextension</key>
<array/>
<key>com.apple.developer.team-identifier</key>
<string><my-team-identifier></string>
<key>get-task-allow</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string><my-team-identifier>.com.apple.managed.vpn.shared</string>
</array>
</dict>
</plist>
如何从钥匙串获取 NEPacketTunnelProvider Networkextension 的身份?
我会自己回答,因为我解决了问题:
1:您需要特殊权限才能访问 com.apple.managed.vpn.shared 组。创建一个 TSI 来获取它。这大约需要一个月的时间。 https://forums.developer.apple.com/thread/67613#9
2:确保您的容器应用程序和网络扩展已设置授权。您可以使用 codesign -d --entitlements :- <filename>
查看
它应该是这样的:
<key>keychain-access-groups</key>
<array>
<string>com.apple.managed.vpn.shared</string>
</array>
我正在使用 XCode 10 Beta,当您使用 GUI 时出现问题。它将值设置为 <string>$(AppIdentifierPrefix)com.apple.managed.vpn.shared</string>
你必须用文本编辑器来修复它。
3:从钥匙串访问数据。我将 post 一些示例代码将数据用作 base64 编码字符串。
guard let identityReference = protocolConfiguration.identityReference else {
throw ConfigError.errorIdentityNotFound
}
let query: [String : Any] = [
kSecValuePersistentRef as String: identityReference as CFData,
kSecReturnRef as String: true,
]
var copyResult: AnyObject?
SecItemCopyMatching(query as CFDictionary, ©Result)
guard let existingCopyResult = copyResult else {
throw ConfigError.errorIdentityNotFound
}
let identity = existingCopyResult as! SecIdentity
var certificate: SecCertificate!
SecIdentityCopyCertificate(identity, &certificate)
let DERCertificate = SecCertificateCopyData(certificate) as NSData
let DERCertificateStringBase64Encoded = DERCertificate.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
let PEMCertificateString = "-----BEGIN CERTIFICATE-----\n\(DERCertificateStringBase64Encoded)\n-----END CERTIFICATE-----"
var privateKey: SecKey!
SecIdentityCopyPrivateKey(identity, &privateKey)
let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, nil) as! NSData
let privateKeyDataBase64Encoded = privateKeyData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
let privateKeyPKCS1String = "-----BEGIN RSA PRIVATE KEY-----\n\(privateKeyDataBase64Encoded)\n-----END RSA PRIVATE KEY-----"
我正在使用 NEPacketTunnelProvider 进行网络扩展。我正在使用具有 com.apple.vpn.managed 负载的配置文件。此外,我使用带有 com.apple.security.pkcs12 负载的 ClientCertificate 身份验证。根据 NETunnelProviderManager 文档,我的扩展应该可以使用 com.apple.managed.vpn.shared 钥匙串访问组检索此身份。
如果我在钥匙串中查询身份,我总是得到错误代码 -25300
。根据 www.osstatus.com 这意味着:"The item cannot be found."
代码 1: 我尝试使用 protocolConfiguration 提供的持久引用来检索身份。
class PacketTunnelProvider: NEPacketTunnelProvider {
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
NSLog("vpn-service startTunnel")
let pc = self.protocolConfiguration
NSLog("vpn-service protocolConfiguration " + pc.debugDescription)
if let identity = pc.identityReference {
let persistentRef = identity as NSData
NSLog("vpn-service persistentRef " + persistentRef.description)
var copyResult: AnyObject?
let copyErr = SecItemCopyMatching([
kSecValuePersistentRef as String: persistentRef,
kSecReturnData as String: true
] as CFDictionary, ©Result)
NSLog("vpn-service getCert copyErr " + copyErr.description)
}
}
}
输出为:
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service startTunnel
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service protocolConfiguration
type = plugin
identifier = 3B39941E-AF39-45CE-B869-68AF392FBCA0
serverAddress = DEFAULT
password = {
domain = user
accessGroup = com.apple.managed.vpn.shared
}
identity = {
identifier = <Subject Name Common Name of the identity installed by the com.apple.security.pkcs12 payload>
persistentReference = <69646e74 00000000 00000011>
domain = user
}
identityDataImported = NO
identityReference = <69646e74 00000000 00000011>
proxySettings = {
autoProxyDiscovery = NO
autoProxyConfigurationEnabled = NO
HTTPEnabled = NO
HTTPSEnabled = NO
FTPEnabled = NO
SOCKSEnabled = NO
RTSPEnabled = NO
gopherEnabled = NO
excludeSimpleHostnames = NO
usePassiveFTP = YES
}
disconnectOnSleep = NO
disconnectOnIdle = NO
disconnectOnIdleTimeout = 0
disconnectOnWake = NO
disconnectOnWakeTimeout = 0
pluginType = <my-bundle-id>
authenticationMethod = 1
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service persistentRef <69646e74 00000000 00000011>
Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] <Notice>: vpn-service getCert copyErr -25300
代码2:
如果我尝试在没有参考的情况下检索所有身份,我也会得到 -25300
。
let getQuery: [String: Any] = [
kSecClass as String: kSecClassIdentity,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: true,
]
var item: CFTypeRef?
let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
NSLog("vpn-service status: " + status.description)
我重新检查了构建结果是否获得了钥匙串访问组:
codesign -d --ent :- build/Debug-iphoneos/agent.app/PlugIns/vpn.appex/
Executable=/Users/timb/projects/xcode/ios-client/build/Debug-iphoneos/agent.app/PlugIns/vpn.appex/vpn
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string><my-application-identifier-of-the-network-extension></string>
<key>com.apple.developer.networking.networkextension</key>
<array/>
<key>com.apple.developer.team-identifier</key>
<string><my-team-identifier></string>
<key>get-task-allow</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string><my-team-identifier>.com.apple.managed.vpn.shared</string>
</array>
</dict>
</plist>
如何从钥匙串获取 NEPacketTunnelProvider Networkextension 的身份?
我会自己回答,因为我解决了问题:
1:您需要特殊权限才能访问 com.apple.managed.vpn.shared 组。创建一个 TSI 来获取它。这大约需要一个月的时间。 https://forums.developer.apple.com/thread/67613#9
2:确保您的容器应用程序和网络扩展已设置授权。您可以使用 codesign -d --entitlements :- <filename>
它应该是这样的:
<key>keychain-access-groups</key>
<array>
<string>com.apple.managed.vpn.shared</string>
</array>
我正在使用 XCode 10 Beta,当您使用 GUI 时出现问题。它将值设置为 <string>$(AppIdentifierPrefix)com.apple.managed.vpn.shared</string>
你必须用文本编辑器来修复它。
3:从钥匙串访问数据。我将 post 一些示例代码将数据用作 base64 编码字符串。
guard let identityReference = protocolConfiguration.identityReference else {
throw ConfigError.errorIdentityNotFound
}
let query: [String : Any] = [
kSecValuePersistentRef as String: identityReference as CFData,
kSecReturnRef as String: true,
]
var copyResult: AnyObject?
SecItemCopyMatching(query as CFDictionary, ©Result)
guard let existingCopyResult = copyResult else {
throw ConfigError.errorIdentityNotFound
}
let identity = existingCopyResult as! SecIdentity
var certificate: SecCertificate!
SecIdentityCopyCertificate(identity, &certificate)
let DERCertificate = SecCertificateCopyData(certificate) as NSData
let DERCertificateStringBase64Encoded = DERCertificate.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
let PEMCertificateString = "-----BEGIN CERTIFICATE-----\n\(DERCertificateStringBase64Encoded)\n-----END CERTIFICATE-----"
var privateKey: SecKey!
SecIdentityCopyPrivateKey(identity, &privateKey)
let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, nil) as! NSData
let privateKeyDataBase64Encoded = privateKeyData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
let privateKeyPKCS1String = "-----BEGIN RSA PRIVATE KEY-----\n\(privateKeyDataBase64Encoded)\n-----END RSA PRIVATE KEY-----"