繁体   English   中英

如何从KeyChain获取代理主机的用户名?

[英]How can I get the username for a proxy host from KeyChain?

我正在Mac上编写实用程序,需要自动确定代理信息。 我设法(从自动代理配置文件中)获取了代理主机和端口,但是如何从钥匙串中获取用户名呢?

我知道您可以使用SecKeychainAddInternetPassword来获取代理密码,但是我也不知道用户名。 有没有办法获取用户名和密码?

我发现它有点复杂。 首先,您必须询问系统配置代理是打开还是关闭。 然后,您必须询问钥匙串,它在返回的主机名上是否具有给定代理(HTTP,HTTPS等)的任何帐户。 然后,如果钥匙串说是,您可以从结果中获取用户名,并要求钥匙串提供匹配的密码。 此时,用户可能会看到一条警告,要求您的应用访问密码。

这是一些示例代码(Mac OS X 10.6 +,ARC)。

ProxyDetector.h:

#import <Foundation/Foundation.h>

@interface ProxyDetector : NSObject

-(ProxyDetector *)init;
-(void)detectHttpProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;
-(void)detectHttpsProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;

@end

ProxyDetector.m:

#import "ProxyDetector.h"
#import <SystemConfiguration/SCDynamicStoreCopySpecific.h>
#import <SystemConfiguration/SCSchemaDefinitions.h>
#import <Security/Security.h>

@implementation ProxyDetector

-(ProxyDetector *)init;
{
    if ((self = [super init])) {
        // init
    }
    return self;
}

void detectProxyWithParams(CFStringRef proxyEnableKey, CFStringRef proxyHostNameKey, CFStringRef proxyPortKey, CFTypeRef proxyProtocol, UInt32 proxyProtocolCode, NSString **hostNamePtr, int *portPtr, NSString **usernamePtr, NSString **passwordPtr)
{
    // get general proxy info
    CFDictionaryRef proxyInfoCPtr = SCDynamicStoreCopyProxies(NULL);
    NSDictionary *proxyInfo = (__bridge NSDictionary *) proxyInfoCPtr;
    NSNumber *proxyEnabled = proxyInfo[(__bridge NSString *)proxyEnableKey];

    // prefill null values for data we may not set later
    *usernamePtr = nil;
    *passwordPtr = nil;

    // is it enabled?
    if (![proxyEnabled intValue]) {
        *hostNamePtr = nil;
        *portPtr = 0;
        return;
    }

    // we can get hostname and port number from this info, but not username and password
    *hostNamePtr = proxyInfo[(__bridge NSString *)proxyHostNameKey];
    NSNumber *portNumber = proxyInfo[(__bridge NSString *)proxyPortKey];
    *portPtr = [portNumber intValue];

    // check in the keychain for username and password
    CFArrayRef result = NULL;
    OSStatus status = SecItemCopyMatching(
                              (__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
                                                          (__bridge id)kSecClassInternetPassword, kSecClass,
                                                          kSecMatchLimitAll, kSecMatchLimit,
                                                          kCFBooleanTrue, kSecReturnAttributes,
                                                          proxyProtocol, kSecAttrProtocol,
                                                          nil],
                              (CFTypeRef *) &result
                              );
    if (status != noErr) {
        if (status != errSecItemNotFound) {
            // unexpected error (else, just no password)
            NSString *errorStr = (__bridge NSString *)SecCopyErrorMessageString(status, NULL);
            NSLog(@"Error while trying to find proxy username and password for hostname %@; assuming no password: %@", *hostNamePtr, errorStr);
        }
        return;
    }

    // check what the keychain got us as results
    CFIndex resultCount = CFArrayGetCount(result);
    for (CFIndex resultIndex = 0; resultIndex < resultCount; resultIndex++) {
        NSDictionary *attrs = (NSDictionary *) CFArrayGetValueAtIndex(result, resultIndex);

        // check if the found host matches the host we got earlier
        NSString *host = [attrs objectForKey:(id)kSecAttrServer];
        if (![host isEqualToString:*hostNamePtr])
            continue;

        const char *hostCStr = [host UTF8String];
        NSString *username = [attrs objectForKey:(id)kSecAttrAccount];
        const char *usernameCStr = [username UTF8String];

        // we know the username now, so ask keychain for the password
        UInt32 passwordLength;
        void *passwordData;

        // this may trigger UI interaction to allow the password to be accessed by this app
        status = SecKeychainFindInternetPassword(NULL, // default user keychains
                                                 (UInt32)strlen(hostCStr), hostCStr,
                                                 0, NULL, // no security domain
                                                 (UInt32)strlen(usernameCStr), usernameCStr,
                                                 0, NULL, // no path
                                                 0, // ignore port
                                                 proxyProtocolCode,
                                                 kSecAuthenticationTypeAny,
                                                 &passwordLength, &passwordData, NULL);

        if (status != noErr) {
            // error getting or accessing this password
            NSString *errorStr = (__bridge NSString *)SecCopyErrorMessageString(status, NULL);
            NSLog(@"Error while trying to find proxy username and password for hostname %@; assuming no password: %@", *hostNamePtr, errorStr);

        } else {
            // we got everything we needed
            *usernamePtr = username;
            *passwordPtr = [NSString stringWithUTF8String:passwordData];
            break; // only one valid item in the results here anyway
        }
    }

    CFRelease(result);
}

-(void)detectHttpProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;
{
    detectProxyWithParams(kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort, kSecAttrProtocolHTTPProxy, kSecProtocolTypeHTTPProxy, hostName, port, username, password);
}

-(void)detectHttpsProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;
{
    detectProxyWithParams(kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort, kSecAttrProtocolHTTPSProxy, kSecProtocolTypeHTTPSProxy, hostName, port, username, password);
}

@end

用法示例:

NSString *hostName;
int port;
NSString *username;
NSString *password;

ProxyDetector *proxyDetector = [[ProxyDetector alloc] init];

[proxyDetector detectHttpProxyReturningHostname:&hostName port:&port username:&username password:&password];
if (hostName) {
    if (username) {
        NSLog(@"HTTP proxy with authentication: http://%@:%@@%@:%d", username, password, hostName, port);
    } else {
        NSLog(@"HTTP proxy without authentication: http://%@:%d", hostName, port);
    }
} else {
    NSLog(@"No HTTP proxy");
}

[proxyDetector detectHttpsProxyReturningHostname:&hostName port:&port username:&username password:&password];
if (hostName) {
    if (username) {
        NSLog(@"HTTPS proxy with authentication: http://%@:%@@%@:%d", username, password, hostName, port);
    } else {
        NSLog(@"HTTPS proxy without authentication: http://%@:%d", hostName, port);
    }
} else {
    NSLog(@"No HTTPS proxy");
}

欢迎改进!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM