What is an HTTPS certificate? How do you authenticate it? With these questions in mind, simply implement the validation process
Here are some operations of HTTPS before data transfer, as shown in the figure below:
Here’s a summary of the key steps in the flowchart above:
1, authentication network request security:
The server returns a public key digital certificate before establishing the actual data transfer. Here the client needs to make a judgment call in the URLSession authentication challenge method callback to decide whether to proceed with the request. The proxy method is as follows:
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
Copy the code
When URLSession makes HTTPS network requests, the authentication permission of the request will be exposed through the proxy method. Whether to trust and continue to establish a connection can be performed according to specific rules (such as the self-license). Only HTTPS requests go through the proxy method, HTTP does not perform callback. This is why iOS promotes HTTPS.
2. If the authentication passes, encrypt and decrypt the final symmetric encryption key through the public-private key asymmetric encryption mode:
Sounds a bit around, the first step is based on public key digital certificate trust, then, to generate a symmetric encryption keys used to request data (symmetric encryption faster), asymmetric encryption, using the public key in encrypted private key by the server, get the key, so, really set up as a key to unlock add, data transmission.
Next, simulate how the public key certificate is trusted
createThe public key. Der 及 Certificate of the cer.file
Enter the following commands on the terminal:
Pem 1024 // Obtain the certificate. Cer openssl req -new -key private_key.pem -out rsacertreq.csr CSR -signkey private_key.pem -out rsacert. CRT // Convert the. CRT certificate to the. Cer certificate. CRT -out rsacert. cer -outform der // Obtain the public key. Der openssl x509 -outform der -in rsaCert.crt -out public_key.derCopy the code
There will be some simple information input during the process. There are no special requirements here. The directory after the file is created is shown as follows:
Drag the. Cer certificate and the public key. Der certificate to the project:
Cer certificate to verify that the public key. Der can be trusted
- (void)trustIsVaild {// Obtain all cer certificates in the project (required certificates for HTTPS network authentication) NSArray * Paths = [[NSBundle mainBundle] pathsForResourcesOfType:@"cer" inDirectory:@"."]; // Save all cer certificates in the project (and set them as authentication anchors later) NSMutableArray *pinnedCertificates = [NSMutableArray array]; for (NSString *path in paths) { NSData *certificateData = [NSData dataWithContentsOfFile:path]; [pinnedCertificates addObject:( __bridge_transfer id)SecCertificateCreateWithData(NULL, ( __bridge CFDataRef)certificateData)]; } // Obtain the public key digital certificate for the project (returned by the server in HTTPS authentication challenges) NSString * publicKeyPath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"]; NSData *derData = [[NSData alloc] initWithContentsOfFile:publicKeyPath]; / / certificates resources SecCertificateRef myCertificate = SecCertificateCreateWithData (kCFAllocatorDefault, ( __bridge CFDataRef)derData); SecPolicyRef myPolicy = SecPolicyCreateBasicX509(); SecTrustRef myTrust; / / SecTrust assignment OSStatus status = SecTrustCreateWithCertificates (myCertificate myPolicy, & myTrust); If (status == noErr) {if (status == noErr) {if (status == noErr) {if (status == noErr) {// Set the certificate anchor point. Will not be trusted) SecTrustSetAnchorCertificates (myTrust, (__bridge CFArrayRef) pinnedCertificates); SecTrustResultType result; if (SecTrustEvaluate(myTrust, & result) = = 0) {/ / / / kSecTrustResultProceed kSecTrustResultUnspecified implicit trust to carry on the if ((result = = KSecTrustResultUnspecified | | result = = kSecTrustResultProceed)) {NSLog (@ "trusted certificate"); } else {NSLog(@" untrusted certificate "); }} else {NSLog(@" untrusted certificate initialization failed "); }}}Copy the code
Run as follows:
By the way, output the console without setting the certificate anchor:
If (status = = noErr) {/ / don't set the anchor / / SecTrustSetAnchorCertificates (myTrust, (__bridge CFArrayRef) pinnedCertificates); SecTrustResultType result; if (SecTrustEvaluate(myTrust, & result) = = 0) {if ((result = = kSecTrustResultUnspecified | | result = = kSecTrustResultProceed)) {NSLog (@ "trusted certificate"); } else {NSLog(@" untrusted certificate "); }} else {NSLog(@" untrusted certificate initialization failed "); }}Copy the code
At this point, if the public key certificate is trusted, then the next step is to specify a symmetric encryption session key, which is encrypted with the public key, sent to the server, and decrypted with the corresponding private key for symmetric encryption of future data transmission.
Therefore, the mobile terminal needs to store the. Cer certificate file generated by the server when performing custom certificate authentication.
The authentication mode in AFNetworking is relatively complicated, because the authentication challenge callback of URLSession allows programmers to enable it unconditionally. Therefore, AFNetworking adds several custom authentication modes based on the default authentication behavior:
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {unconditional open AFSSLPinningModePublicKey AFSSLPinningModeNone, / /, / / id-based content AFSSLPinningModeCertificate, / / certification };Copy the code
And before that, AFNetworking passed
@property (readwrite, nonatomic, copy) AFURLSessionTaskAuthenticationChallengeBlock authenticationChallengeHandler;
Copy the code
Expose custom authentication logic and processing results to external closures.
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
BOOL evaluateServerTrust = NO;
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil;
//AFNetworking 暴露给程序员自定义处理入口
if (self.authenticationChallengeHandler) {
id result = self.authenticationChallengeHandler(....);
... (解析处理结果)
}
...(证书认证处理代码)
//最后调用 completionHandler 继续执行操作
if (completionHandler) {
completionHandler(disposition, credential);
}
}
Copy the code
Disposition: Can be set to continue to challenge (NSURLSessionAuthChallengeUseCredential) or interrupt authentication challenge (NSURLSessionAuthChallengeCancelAuthenticationChallenge)
Credential: Assign directly if the certificate is authenticated,
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
Copy the code
Otherwise it is nil
Here is a brief overview of the certificate trust logic, and I won’t go over the AFNetworking source code. Don’t laugh at bad code.