0

我的应用程序就像 IPSec VPN 服务器 (strongSwan) 的 VPN 客户端。我已经使用 NEVPNManager 来设置 VPN 配置文件。代码是这样的:

#define KEY_PASSWORD @"password"
#define PASSWORD @"password"
#define VPN_SERVER @"10.1.1.1"
#define KEY_USERNAME @"username"
#define USERNAME @"myusername"
#define KEY_SHARED_SECRET @"sharedSecret"
#define SHARED_SECRET @"thisisthesecretekey"
#define LOCAL_IDENTIFIER @"myserver.com.client"
#define REMOTE_IDENTIFIER @"myserver.com.server"

-(void) setupVPNProfile {    
    NEVPNProtocolIKEv2 *protocol = [[NEVPNProtocolIKEv2 alloc] init];
    protocol.username = USERNAME;
    NSData *passwdRef = [self getData:KEY_PASSWORD];
    if (passwdRef == nil) {
        [self storeData:KEY_PASSWORD data:[PASSWORD dataUsingEncoding:NSUTF8StringEncoding]];
        passwdRef = [self getData:PASSWORD];
        NSLog(@"passwdRef: %@", [[NSString alloc] initWithData:passwdRef encoding:NSUTF8StringEncoding]);
    }
    protocol.passwordReference = passwdRef;

    protocol.serverAddress = VPN_SERVER;

    protocol.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
    NSData *sharedSecretRef = [self getData:KEY_SHARED_SECRET];
    if (sharedSecretRef == nil) {
        [self storeData:KEY_SHARED_SECRET data:[SHARED_SECRET dataUsingEncoding:NSUTF8StringEncoding]];
        sharedSecretRef = [self getData:KEY_SHARED_SECRET];
        NSLog(@"sharedSecretRef: %@", [[NSString alloc] initWithData:sharedSecretRef encoding:NSUTF8StringEncoding]);

    }
    protocol.sharedSecretReference = sharedSecretRef;

    protocol.localIdentifier = LOCAL_IDENTIFIER;
    protocol.remoteIdentifier = REMOTE_IDENTIFIER;

    protocol.useExtendedAuthentication = YES;

    protocol.disconnectOnSleep = NO;

    self.manager.protocolConfiguration = protocol;

    self.manager.enabled = YES;
}

#pragma mark - Keychain methods

- (void) storeData: (NSString * )key data:(NSData *)data {
    NSLog(@"Store Data");
    NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
    [dict setObject:data forKey:(__bridge id)kSecValueData];

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
    if(errSecSuccess != status) {
        NSLog(@"Unable add item with key =%@ error:%d",key,(int)status);
    }
}

- (NSData *) getData: (NSString *)key {
    NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
    [dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    [dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnPersistentRef];

    CFTypeRef result = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);

    if( status != errSecSuccess) {
        NSLog(@"Unable to fetch item for key %@ with error:%d",key,(int)status);
        return nil;
    }

    NSData *resultData = (__bridge NSData *)result;
   return resultData;
}

- (BOOL) removeData: (NSString *) key {
    NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];

    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dict);

    if( status != errSecSuccess) {
        NSLog(@"Unable to fetch item for key %@ with error:%d",key,(int)status);
        return NO;
    }

    return YES;
}

这里的用户名、密码服务器地址等值存储在上面定义的 MACROS 中。根据所需的设计,密码和 sharedSecret 将存储为 Persistent KeyChain 项。在此设置中,如何优雅地更改用户名、密码、serverAddress、sharedSecret 等值?

到目前为止我已经尝试过:

  1. 我更改了代码中的服务器地址、用户名等值。我使用 storeData 函数覆盖密码和 sharedSecret 持久钥匙串项。但是,当我在进行这些更改后运行应用程序时,我无法连接到新的 VPN 服务器。请注意,我必须使用的新值没有犯任何错误。我在更新前仔细检查了。另请注意,当我使用这些新参数创建 .mobileconfig 文件时,我能够连接到 VPN 服务器。只是我的 VPN 客户端应用程序无法再连接到服务器。它会尝试连接并再次断开连接。卸载应用程序也没有效果。

  2. 设置 manager.protocolConfiguration = nil。这样做没有任何效果。安装的 protocolConfiguration 保持不变。

我不想通过调用 manager removePreferencesWithCompletionHandler:] 来删除 VPN 配置文件,因为用户在尝试连接到 VPN 服务器时会再次看到与 VPN 相关的弹出窗口。如果有人以前这样做过,请提供帮助。谢谢。

4

1 回答 1

0

要更改 VPN 帐户,您应该能够简单地更改 VPN 帐户参数,然后使用新值启动 VPNManager。您不需要停止当前运行的 VPN 会话,当然也不需要删除配置文件,只需调用 NEVPNManager.shared.locadFromPreferences,进行更改,然后正常运行 .saveToPreferences 和 startVPNTunnel。

由于即使使用变量而不是宏的已知良好凭据,您也无法启动 VPN 隧道,因此您可能从钥匙串中错误地传递了值,或者不是您所期望的。我将使用打印语句比较每个参数,首先与您当前使用宏的工作解决方案,然后与非工作钥匙串代码。

于 2017-03-24T20:42:18.057 回答