You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NEVPNManager配置IPSec VPN时未提供共享密钥问题求助

Fixing "no VPN shared secret was provided" Error in iOS IPSec VPN Configuration (Objective-C)

Hey there, let's get this error sorted out for you. The core issue here is how you're handling the sharedSecretReference property on your NEVPNProtocolIPSec instance.

You’re currently passing raw NSData from your secret string directly to this property, but sharedSecretReference expects a persistent keychain reference, not the plaintext secret data. iOS requires sensitive VPN credentials like shared keys to be stored securely in the keychain, and you need to pass a reference to that keychain entry instead of the actual value.

Here's the revised implementation:

First, add a helper method to save the shared secret to the keychain and retrieve its persistent reference:

- (NSData *)saveSharedSecretToKeychain:(NSString *)secret service:(NSString *)service {
    NSDictionary *addQuery = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: service,
        (__bridge id)kSecAttrAccount: @"VPNSharedSecret",
        (__bridge id)kSecValueData: [secret dataUsingEncoding:NSUTF8StringEncoding],
        (__bridge id)kSecReturnPersistentRef: @YES
    };
    
    CFTypeRef persistentRef = NULL;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, &persistentRef);
    
    // If the secret already exists in the keychain, fetch its existing reference
    if (status == errSecDuplicateItem) {
        NSDictionary *fetchQuery = @{
            (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
            (__bridge id)kSecAttrService: service,
            (__bridge id)kSecAttrAccount: @"VPNSharedSecret",
            (__bridge id)kSecReturnPersistentRef: @YES
        };
        status = SecItemCopyMatching((__bridge CFDictionaryRef)fetchQuery, &persistentRef);
    }
    
    if (status == errSecSuccess && persistentRef != NULL) {
        return CFBridgingRelease(persistentRef);
    }
    return nil;
}

Then update your main VPN configuration code to use this helper, and fix the password reference handling too:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[NEVPNManager sharedManager] setEnabled:YES];
    [[NEVPNManager sharedManager] loadFromPreferencesWithCompletionHandler: ^(NSError *error) {
        if (error) {
            NSLog(@"Failed to load VPN preferences: %@", error);
            return;
        }

        NEVPNProtocolIPSec *vpnProtocol = [[NEVPNProtocolIPSec alloc] init];
        vpnProtocol.serverAddress = @"178.62.78.101";
        vpnProtocol.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
        vpnProtocol.useExtendedAuthentication = YES;
        vpnProtocol.localIdentifier = @"londra1";
        vpnProtocol.username = @"root";
        vpnProtocol.disconnectOnSleep = NO;

        // Handle shared secret: save to keychain and get persistent reference
        NSString *sharedSecret = @"gfJL$$";
        NSData *secretRef = [self saveSharedSecretToKeychain:sharedSecret service:@"MyVPNService"];
        if (!secretRef) {
            NSLog(@"Failed to store shared secret in keychain");
            return;
        }
        vpnProtocol.sharedSecretReference = secretRef;

        // Handle password: get persistent reference from keychain (not raw data)
        NSDictionary *passwordQuery = @{
            (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
            (__bridge id)kSecAttrService:@"2eee9fa59",
            (__bridge id)kSecAttrAccount:@"2eee9fa5",
            (__bridge id)kSecReturnPersistentRef: @YES
        };
        CFTypeRef passwordRef = NULL;
        OSStatus passwordStatus = SecItemCopyMatching((__bridge CFDictionaryRef)passwordQuery, &passwordRef);
        if (passwordStatus != errSecSuccess || !passwordRef) {
            NSLog(@"Failed to retrieve password reference from keychain");
            return;
        }
        vpnProtocol.passwordReference = CFBridgingRelease(passwordRef);

        // Configure on-demand rules and save preferences
        [NEVPNManager sharedManager].protocolConfiguration = vpnProtocol;
        [NEVPNManager sharedManager].localizedDescription = @"vpn";
        [NEVPNManager sharedManager].onDemandRules = @[[NEOnDemandRuleConnect new]];
        [NEVPNManager sharedManager].enabled = YES;

        [[NEVPNManager sharedManager] saveToPreferencesWithCompletionHandler: ^(NSError *saveError) {
            NSLog(@"VPN preferences save completed");
            if (saveError) {
                NSLog(@"Save error: %@", saveError);
                return;
            }

            // Start tunnel ONLY after saving preferences successfully
            NSError *startError;
            BOOL tunnelStarted = [[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
            if (!tunnelStarted && startError) {
                NSLog(@"Tunnel start error: %@", startError.localizedDescription);
            }
        }];
    }];
    return YES;
}

Important Reminders:

  • Never pass plaintext credentials to sharedSecretReference or passwordReference—these properties require keychain persistent references for security compliance.
  • Always start the VPN tunnel after the saveToPreferencesWithCompletionHandler confirms success. Starting it early can lead to configuration mismatches.
  • Ensure your Xcode project has the VPN capability enabled (Target > Signing & Capabilities > Add Capability > VPN).
  • For iOS 14+, double-check that your on-demand rules align with Apple's latest network extension guidelines.

内容的提问来源于stack exchange,提问作者user3236289

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:45:18