Access Internet With Uiwebview

ios中http 和https 协议的访问

http://www.cnblogs.com/stan0714/archive/2012/03/21/2409872.html

最近做个项目,开始采用的是HTTP协议实现客户端和服务器端的交互,后来需要改成HTTPS协议。在修改的过程中发现了一些问题,解决方案如下:

HTTP:

    //初始化URL String
    NSString *urlString =[NSString stringWithFormat:@"https://127.0.0.1/default.aspx?USER=%@",@"111"];

    //可修改的URL Request,设定URL和Method
    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"GET"];

    //初始化Response,Error。发送异步Request,取得Response及Error,并对Response进行UTF8解码。
    NSHTTPURLResponse* urlResponse = nil;
    NSError *error = [[NSError alloc] init];
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
    NSMutableString *result = [[NSMutableString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

    NSLog(@"The result string is :%@",result);

HTTPS

//事件触发 启动Request
{ 
    NSString *urlString =[NSString stringWithFormat:@"https://127.0.0.1/default.aspx?USER=%@",@"111"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]        cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5];
    //设置请求方式为get
    [request setHTTPMethod:@"GET"];
    //添加用户会话id
    [request addValue:@"text/html" forHTTPHeaderField:@"Content-Type"];
    //连接发送请求
    finished = false;
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    //堵塞线程,等待结束
    while(!finished) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
} 

//收到Response
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response 
{
}

//完成Load
- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{
    //[_waitingDialog dismissWithClickedButtonIndex:0 animated:NO];
    [connection release];
}

//出错
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{ 
}

//是否用存储
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection{
    return NO;
}

//下面两段是重点,要服务器端单项HTTPS 验证,iOS 客户端忽略证书验证。
//返回是否能得到认证
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
} 

//得到“挑战应答模式”认证的挑战
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {    
    NSLog(@"didReceiveAuthenticationChallenge %@ %zd", [[challenge protectionSpace] authenticationMethod], (ssize_t) [challenge previousFailureCount]);

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
        [[challenge sender]  useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
        [[challenge sender]  continueWithoutCredentialForAuthenticationChallenge: challenge];
    }
} 
    NSLog(@"get the whole response");
    //[receivedData setLength:0];
}

//处理数据 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{ 
}

大家也可以参考下面一篇文章

关于在UIwebView中访问HTTPS站点的几种方法

iPhone应用程序 HTTPS服务器连接教程

http://mobile.51cto.com/iphone-278138.htm

iPhone应用程序 HTTPS服务器连接教程是我们要一起来学习的内容。你是否也想让自己的 iPhone 应用程序连接 https 服务器呢?下面我就介绍一下其使用方法。

通常使用 Objective-C 的 NSURLConnection 连接有证明书的 https 服务器时会出现验证错误,我们可以使用私有API — setAllowsAnyHTTPSCertificate:forHost 来解决这个问题。如果是 Cocoa 的应用程序应该是没有什么问题,但是用在 iPhone 上,很可能过不了 App Store 的审查。

所以这里我们使用 libcurl 来完成在 iphone 上连接 https 服务器。

准备

编译 openssl

连接 https 的前提是要有 OpenSSL。你可以参考 这里(http://www.therareair.com/2009/01/01/tutorial-how-to-compile-openssl-for-the-iphone/) 来为 iPhone 编译 OpenSSL 静态库。最终得到下面两个静态库文件。

libcrypto.a
libssl.a

编译 libcurl

接下来我们下载/编译 libcurl。下载展开后,按照下面配置(根据实际情况更改你的SDK目录,版本)。

./configure --prefix=$HOME/tmp/iphonelib/curl \  
    --host=arm-apple-darwin --disable-shared --with-random=/dev/urandom \  
    CC=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc \  
    CFLAGS="-arch armv6 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/
                 iPhoneOS3.0.sdk -I$HOME/tmp/iphonelib/openssl/include -L$HOME/tmp/iphonelib/openssl/lib" \  
    CPP=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/cpp \  
    AR=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar

如果最后输出下面的内容,说明可以编译支持 https 的 libcurl 了。

SSL support: enabled (OpenSSL)

接下来

make
make install

编译结果输出到 ~/tmp/iphonelib/curl/lib 下的 libcurl.a。

使用

添加到工程中,group and files -> libcurl -> (…).a

另外,由于 openssl 中使用了 zlib,所以还需要在工程中加入链接开关。(该库被包含在iPhone中,不需要重新编译) 在连接中追加 -lz。

information of target curl
-> build
-> link
-> other link option: -lz

最后,添加编译所需的头文件路径。

information of target curl
-> search path
-> header search path

比如,编译 libcurl 时的头文件的路径 ~/tmp/iphonelib/curl/include 。

代码例子下来,让我们看看在程序中使用 libcurl 的例子。下面的例子在 AppDelegate.m 中实现。

#import "AppDelegate.h"  
#include <curl/curl.h> 
@implementation AppDelegate  
-(void)applicationDidFinishLaunching:(UIApplication *)application {  
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
    // Override point for customization after application launch  
    [window makeKeyAndVisible];  
    CURL *curl;  
    CURLcode res;  
    curl = curl_easy_init();  
    if (curl) {  
        curl_easy_setopt(curl, CURLOPT_URL, "https://twitter.com/");  
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);  

        res = curl_easy_perform(curl);  
        if (0 != res) {  
            fprintf(stderr, "curl error: %d\n", res);  
        }  
        curl_easy_cleanup(curl);  
    }  
}  
-(void)dealloc {  
    [window release];  
    [super dealloc];  
}  
@end

编译运行,可以用调试工具得到取得的html。

在模拟器中使用 libcurl

上面介绍的都是在设备上运行的例子,如果要在模拟器上使用,由于处理器结构不一样,需要重新编译 openssl 和 curl 静态库。编译的时候,只要将 SDK 的路径由 iPhoneOS.platform ⇒ iPhoneSimulator.platform,编译开关 -arch armv6 ⇒ -arch i386 就可以了。只是编译的文件名最好和iphone上用的区别开来,如下所示:

libcrypto_simulator.a
libssl_simulator.a
libcurl_simulator.a

又或者不改变库的名称,而是增加新的编译目标。

小结:iPhone应用程序 HTTPS服务器连接教程的内容介绍我那了,希望本文对你有所帮助!

使用服务器信任证书,访问https服务器

博客分类: iphone
AppleSecurityHTML
参考ASIHHPRequest开源项目中的ClientCertificateTests.m源码。
链接:https://github.com/pokeb/asi-http-request/blob/master/Classes/Tests/ClientCertificateTests.m
以及:http://developer.apple.com/library/mac/#documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html

代码

+ (void)testClientCertificate {  
    NSURL *httpsUrl = [NSURL URLWithString:@"https://xxxxxx.xx.xx"];  

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:httpsUrl];  

    SecIdentityRef identity = NULL;  
    SecTrustRef trust = NULL;  

        //绑定证书,证书放在Resources文件夹中  
    NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];  
    [HttpsTestViewController extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];  

    request = [ASIHTTPRequest requestWithURL:httpsUrl];  

    [request setClientCertificateIdentity:identity];  
    [request setValidatesSecureCertificate:NO];  
    [request startSynchronous];  

    error = [request error];  
    if (!error) {  
        NSString *response = [request responseString];  
        NSLog(@"response is : %@",response);  
    } else {  
        NSLog(@"Failed to save to data store: %@", [error localizedDescription]);  
        NSLog(@"%@",[error userInfo]);  
    }  
}  

+ (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {  
    OSStatus securityError = errSecSuccess;  

    CFStringRef password = CFSTR("xxxxxx"); //证书密码  
    const void *keys[] =   { kSecImportExportPassphrase };  
        const void *values[] = { password };  

    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys,values, 1,NULL, NULL);   

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);  
    //securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)optionsDictionary,&items);  
    securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,optionsDictionary,&items);   

    if (securityError == 0) {   
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);  
        const void *tempIdentity = NULL;  
        tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);  
        *outIdentity = (SecIdentityRef)tempIdentity;  
        const void *tempTrust = NULL;  
        tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);  
        *outTrust = (SecTrustRef)tempTrust;  
    } else {  
        NSLog(@"Failed with error code %d",(int)securityError);  
        return NO;  
    }  
    return YES;  
}

项目中,要添加Security.framework。

关于在UIwebView中访问HTTPS站点的几种方法

这两天一直在研究如何用UIWebView访问HTTPS站点,试过很多方法,但都有这样那样的缺陷,下面简单分享一下,希望各位提点意见:

1。调用私有API
最简单,也最危险的方法,调用 setAllowsAnyHTTPSCertificate:forHost ,后果怎么样就不用我说了吧。

2. libCurl
这是一个开源项目,用C语言写的URL转换库,是基于于openssl的,可以支持目前大部分的URL,包括DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, TELNET 和 TFTP,而且用这个不会使用苹果禁止的私有API,所以可以顺利通过App Store的审核。 但是有一个问题,就是要自己编译libcurl和openssl 的两个静态库,其中的配置比较麻烦,不熟悉在unix环境下编译静态库的朋友们可能就会皱眉头了。这有两个链接,可以参考 :

http://www.therareair.com/2009/01/01/tutorial-how-to-compile-openssl-for-the-iphone/
http://www.yifeiyang.net/iphone-web-development-skills-of-the-article-5-https-server-using-libcurl-to-connect/

编译成功后,将libcurl引入工程,再加上下面这些代码,就可以顺利访问HTTPS站点了

/* 调用libcurl 访问HTTPS站点 **
c*t char *urlstring=[homeUrl cStringUsingEncoding:NSUTF8StringEncoding];
CURL *curl;
CURLcode res;
char* buffer;
int buflen=1024;
size_t* n;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://10.20.7.236/login.aspx?ReturnUrl=%2f");
//curl_easy_setopt(curl, CURLOPT_URL, urlstring);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);

res = curl_easy_perform(curl);

if (0 != res) {
fprintf(stderr, "curl error: %d\n", res);
}
//res=curl_easy_recv(curl, buffer, buflen, n);
//printf("%s",buffer);
curl_easy_cleanup(curl);
}
*/
通过联机调试在我的ipod上成功的访问到了HTTPS站点,在控制台中可以看到网页的内容,剩下的就是如何把这些内容存到一个字符数组里面,再通过调用UIwebView的loadHTMLString方法,就可以显示内容了, 不过最后这一步我还没有找到方法,希望有兴趣的朋友可以帮我解决这个问题。

3。ASIHTTPRequest
这个开源项目也是流行很久了, 但是一般都是用来替代NSURLConnection,其实里面的功能还是挺强大的,只是对于HTTPS访问这一块我有点不太满意,
里面有一个方法,是可以直接忽略证书验证的,

disabling secure certificate validation

You may wish to use this for testing purposes if you have a self-signed secure certificate. I recommend purchasing a certificate from a trusted certificate authority and leaving certificate validation turned on for production applicati*.

[request setValidatesSecureCertificate:NO];

但是这样做有一点危险,如果是放在企业环境里部署的话,那外面的人都可以访问内部的网站了,SSL这一层就形同虚设。所以我又找了一下,发现他还有另一个方法,可以设置客户端的证书:

Client certificates support
If your server requires the use of client certificates, as of v1.8 it is now possible to send them with your request.

// Will send the certificate attached to the identity (identity is a SecIdentityRef)
[request setClientCertificateIdentity:identity];

// Add an additional certificate (where cert is a SecCertificateRef)
[request setClientCertificates:[NSArray arrayWithObject:(id)cert]];
There is a helper function in 'ClientCertificateTests.m' in the iPhone / iPad sample app that can create a SecIdentityRef from PKCS12 data (this function only works on iOS).

这个方法需要把p12文件放进工程里面,然后调用[ClientCertificateTest extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data]; 来获取identity
,这个方法是这样的

(BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data

{
OSStatus securityError = errSecSuccess;

NSDictionary *opti*Dictionary = [NSDictionary dictionaryWithObject:@"" forKey:(id)kSecImportExportPassphrase];

CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)opti*Dictionary,&items);

if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
c*t void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
c*t void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
} else {
NSLog(@"Failed with error code %d",(int)securityError);
return NO;
}
return YES;
}
这个方法需要引用到Security.framework这个框架(我第一次没有引用,结果报错,我想官方的东西怎么还有错呢,郁闷了好久才发现是这个框架没有引用,晕死)。之后应该就是通过[ASIHTTPRequest startSynchronous] 可以访问HTTPS网站了。 为什么说是应该呢? 因为我也没有试出来,呵呵 ```可能是证书的问题,我在extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data 这个方法里获取到的securityError 为-26275 ,在网上找了半天也没找出个结果来,所以也只能暂时搁置了。

-(void) viewHomePage{
NSURL *url=[NSURL URLWithString:homeUrl];

NSBundle *bundle = [NSBundle mainBundle]; //取得mainBundle
NSString *plistPath = [bundle pathForResource:@"Root" ofType:@"plist"]; //取得文件路径
// 或可以写成
// NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"文件名" ofType:@"plist"];
//读取到一个NSDictionary
NSArray *array=[[NSArray alloc]initWithContentsOfFile:plistPath];

NSDictionary *dictionary = [array objectAtIndex:1];
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
[defaults registerDefaults:dictionary];

BOOL needAuthentication=[defaults boolForKey:@"needAuth"];

SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ioa.bingosoft.net" ofType:@"pfx"]];
[SenchaShellViewController extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];

//** 调用 ASIHTTPRequest 访问HTTPS站点
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

[request setClientCertificateIdentity:identity];
//[request setValidatesSecureCertificate:NO];
//[request startSynchronous];
[request setValidatesSecureCertificate:needAuthentication];
[request startSynchronous];

NSError *error = [request error];
if (!error) {
[self.viewer loadHTMLString:[request resp*eString] baseURL: [request url]];
}
else {
NSLog(@"Error: %@", error );
}

4。 官方API
最后一个是官方的API, 按理说这个应该放到最前面,因为是官方的嘛,但是实际上我一次都没有试成功过,所以有点怀疑他的真实性,不过也许有人成功了,所以也拿上来讨论一下。
简单来说通过NSurlconnection的这两个委托方法来忽略认证,关于Certificate Authentication是有官方文档的,但是官方文档太长了,我没有心思看完,所以还没有深入研究下去。
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtecti*pace:(NSURLProtecti*pace *)protecti*pace {
return [protecti*pace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protecti*pace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
//if ([trustedHosts containsObject:challenge.protecti*pace.host])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protecti*pace.serverTrust]
forAuthenticationChallenge:challenge];

[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}