SecCertificateRef: How to get the certificate information? - iphone

I have a certificate (SecCertificateRef), I can check if it's valid and I can extract a "summary" using SecCertificateCopySubjectSummary.
What is the "summary" exactly? I don't understand the term "A string that contains a human-readable summary of the contents of the certificate." in the Apple documentation. I think, they mean the "CN" in the certificate, correct?
Is there any method to get the clear X509-information out of SecCertificateRef? Does a cast to a keychain-object help?
I want to have something like this and I am especially focussed on the "CN" to compare it with the URL I submitted to avoid man-in-the-middle attacks. (Or any better ideas?)
That is what I want to have:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: md5WithRSAEncryption
Issuer: C=XY, ST=Austria, L=Graz, O=TrustMe Ltd, OU=Certificate Authority, CN=CA/Email=ca#trustme.dom
Validity
Not Before: Oct 29 17:39:10 2000 GMT
Not After : Oct 29 17:39:10 2001 GMT
Subject: C=DE, ST=Austria, L=Vienna, O=Home, OU=Web Lab, CN=anywhere.com/Email=xyz#anywhere.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:c4:40:4c:6e:14:1b:61:36:84:24:b2:61:c0:b5:
d7:e4:7a:a5:4b:94:ef:d9:5e:43:7f:c1:64:80:fd:
9f:50:41:6b:70:73:80:48:90:f3:58:bf:f0:4c:b9:
90:32:81:59:18:16:3f:19:f4:5f:11:68:36:85:f6:
1c:a9:af:fa:a9:a8:7b:44:85:79:b5:f1:20:d3:25:
7d:1c:de:68:15:0c:b6:bc:59:46:0a:d8:99:4e:07:
50:0a:5d:83:61:d4:db:c9:7d:c3:2e:eb:0a:8f:62:
8f:7e:00:e1:37:67:3f:36:d5:04:38:44:44:77:e9:
f0:b4:95:f5:f9:34:9f:f8:43
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
email:xyz#anywhere.com
Netscape Comment:
mod_ssl generated test server certificate
Netscape Cert Type:
SSL Server
Signature Algorithm: md5WithRSAEncryption
12:ed:f7:b3:5e:a0:93:3f:a0:1d:60:cb:47:19:7d:15:59:9b:
3b:2c:a8:a3:6a:03:43:d0:85:d3:86:86:2f:e3:aa:79:39:e7:
82:20:ed:f4:11:85:a3:41:5e:5c:8d:36:a2:71:b6:6a:08:f9:
cc:1e:da:c4:78:05:75:8f:9b:10:f0:15:f0:9e:67:a0:4e:a1:
4d:3f:16:4c:9b:19:56:6a:f2:af:89:54:52:4a:06:34:42:0d:
d5:40:25:6b:b0:c0:a2:03:18:cd:d1:07:20:b6:e5:c5:1e:21:
44:e7:c5:09:d2:d5:94:9d:6c:13:07:2f:3b:7c:4c:64:90:bf:
ff:8e

I couldn't wait for an answer to the bounty, so I found a solution myself. As others said, Security.framework doesn't give you a way to get this information, so you need to ask OpenSSL to parse the certificate data for you:
#import <openssl/x509.h>
// ...
NSData *certificateData = (NSData *) SecCertificateCopyData(certificate);
const unsigned char *certificateDataBytes = (const unsigned char *)[certificateData bytes];
X509 *certificateX509 = d2i_X509(NULL, &certificateDataBytes, [certificateData length]);
NSString *issuer = CertificateGetIssuerName(certificateX509);
NSDate *expiryDate = CertificateGetExpiryDate(certificateX509);
Where CertificateGetIssuerName and CertificateGetExpiryDate are as follows:
static NSString * CertificateGetIssuerName(X509 *certificateX509)
{
NSString *issuer = nil;
if (certificateX509 != NULL) {
X509_NAME *issuerX509Name = X509_get_issuer_name(certificateX509);
if (issuerX509Name != NULL) {
int nid = OBJ_txt2nid("O"); // organization
int index = X509_NAME_get_index_by_NID(issuerX509Name, nid, -1);
X509_NAME_ENTRY *issuerNameEntry = X509_NAME_get_entry(issuerX509Name, index);
if (issuerNameEntry) {
ASN1_STRING *issuerNameASN1 = X509_NAME_ENTRY_get_data(issuerNameEntry);
if (issuerNameASN1 != NULL) {
unsigned char *issuerName = ASN1_STRING_data(issuerNameASN1);
issuer = [NSString stringWithUTF8String:(char *)issuerName];
}
}
}
}
return issuer;
}
static NSDate *CertificateGetExpiryDate(X509 *certificateX509)
{
NSDate *expiryDate = nil;
if (certificateX509 != NULL) {
ASN1_TIME *certificateExpiryASN1 = X509_get_notAfter(certificateX509);
if (certificateExpiryASN1 != NULL) {
ASN1_GENERALIZEDTIME *certificateExpiryASN1Generalized = ASN1_TIME_to_generalizedtime(certificateExpiryASN1, NULL);
if (certificateExpiryASN1Generalized != NULL) {
unsigned char *certificateExpiryData = ASN1_STRING_data(certificateExpiryASN1Generalized);
// ASN1 generalized times look like this: "20131114230046Z"
// format: YYYYMMDDHHMMSS
// indices: 01234567890123
// 1111
// There are other formats (e.g. specifying partial seconds or
// time zones) but this is good enough for our purposes since
// we only use the date and not the time.
//
// (Source: http://www.obj-sys.com/asn1tutorial/node14.html)
NSString *expiryTimeStr = [NSString stringWithUTF8String:(char *)certificateExpiryData];
NSDateComponents *expiryDateComponents = [[NSDateComponents alloc] init];
expiryDateComponents.year = [[expiryTimeStr substringWithRange:NSMakeRange(0, 4)] intValue];
expiryDateComponents.month = [[expiryTimeStr substringWithRange:NSMakeRange(4, 2)] intValue];
expiryDateComponents.day = [[expiryTimeStr substringWithRange:NSMakeRange(6, 2)] intValue];
expiryDateComponents.hour = [[expiryTimeStr substringWithRange:NSMakeRange(8, 2)] intValue];
expiryDateComponents.minute = [[expiryTimeStr substringWithRange:NSMakeRange(10, 2)] intValue];
expiryDateComponents.second = [[expiryTimeStr substringWithRange:NSMakeRange(12, 2)] intValue];
NSCalendar *calendar = [NSCalendar currentCalendar];
expiryDate = [calendar dateFromComponents:expiryDateComponents];
[expiryDateComponents release];
}
}
}
return expiryDate;
}
I only actually needed the issuer's organization name and the expiry date for my purposes, so that's all the code I've included below. But, based on this you should be able to figure out the rest by reading the x509.h header file.
Edit:
Here's how to get the certificate. I haven't put any error handling, etc. You'll want to check trustResult, err, etc., for example.
NSURLAuthenticationChallenge *challenge;
SecTrustResultType trustResult;
SecTrustRef trust = challenge.protectionSpace.serverTrust;
OSStatus err = SecTrustEvaluate(trust, &trustResult);
SecCertificateRef certificate = SecGetLeafCertificate(trust); // See Apple docs for implementation of SecGetLeafCertificate

better just use SecCertificateCopyCommonName to get CN to compare to your required hostname.

You were right Michael, iOS won't give you the API to do a full job on a X.509 certificates. Thankfully it will give you access to the actual (ASN.1) encoded certificate data. From there you can do your own decoding (not much fun) or delegate it to an existing library, like you did with OpenSSL.
Here's my version that uses the .NET framework. It's mean to be used by MonoTouch developers (and MonoMac developers too) who needs to interoperate with SecCertificateRef within their applications.
public void Show (SecCertificate sc)
{
// get the SecCertificate "raw", i.e. ASN.1 encoded, data
byte[] data = sc.DerData.ToArray<byte> ();
// the build the managed X509Certificate2 from it
X509Certificate2 cer = new X509Certificate2 (data);
// to get all properties / methods available in .NET (pretty exhaustive)
Console.WriteLine ("SubjectName: {0}", cer.Subject);
Console.WriteLine ("IssuerName: {0}", cer.Issuer);
Console.WriteLine ("NotBefore: {0}", cer.NotBefore);
Console.WriteLine ("NotAfter: {0}", cer.NotAfter);
Console.WriteLine ("SerialNumber: {0}", cer.SerialNumber);
// ...
}

If for some reason you want to do this without OpenSSL one can use the apple extraction keys. The first one will extract (just) the Subject and Issuer (there are more kSecOIDX509's for most other things, like expiry dates) and pass them for printing.
+(NSString*)stringFromCerificateWithLongwindedDescription:(SecCertificateRef) certificateRef {
if (certificateRef == NULL)
return #"";
CFStringRef commonNameRef;
OSStatus status;
if ((status=SecCertificateCopyCommonName(certificateRef, &commonNameRef)) != errSecSuccess) {
NSLog(#"Could not extract name from cert: %#",
SecCopyErrorMessageString(status, NULL));
return #"Unreadable cert";
};
CFStringRef summaryRef = SecCertificateCopySubjectSummary(certificateRef);
if (summaryRef == NULL)
summaryRef = CFRetain(commonNameRef);
CFErrorRef error;
const void *keys[] = { kSecOIDX509V1SubjectName, kSecOIDX509V1IssuerName };
const void *labels[] = { "Subject", "Issuer" };
CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks);
CFDictionaryRef vals = SecCertificateCopyValues(certificateRef, keySelection,&error);
NSMutableString *longDesc = [[NSMutableString alloc] init];
for(int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) {
CFDictionaryRef dict = CFDictionaryGetValue(vals, keys[i]);
CFArrayRef values = CFDictionaryGetValue(dict, kSecPropertyKeyValue);
if (values == NULL)
continue;
[longDesc appendFormat:#"%s:%#\n\n", labels[i], [NSString stringFromDNwithSubjectName:values]];
}
CFRelease(vals);
CFRelease(summaryRef);
CFRelease(commonNameRef);
return longDesc;
}
The second function is an over the top try to extract anything you can get your mittens on:
+(NSString *)stringFromDNwithSubjectName:(CFArrayRef)array {
NSMutableString * out = [[NSMutableString alloc] init];
const void *keys[] = { kSecOIDCommonName, kSecOIDEmailAddress, kSecOIDOrganizationalUnitName, kSecOIDOrganizationName, kSecOIDLocalityName, kSecOIDStateProvinceName, kSecOIDCountryName };
const void *labels[] = { "CN", "E", "OU", "O", "L", "S", "C", "E" };
for(int i = 0; i < NVOID(keys); i++) {
for (CFIndex n = 0 ; n < CFArrayGetCount(array); n++) {
CFDictionaryRef dict = CFArrayGetValueAtIndex(array, n);
if (CFGetTypeID(dict) != CFDictionaryGetTypeID())
continue;
CFTypeRef dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel);
if (!CFEqual(dictkey, keys[i]))
continue;
CFStringRef str = (CFStringRef) CFDictionaryGetValue(dict, kSecPropertyKeyValue);
[out appendFormat:#"%s=%# ", labels[i], (__bridge NSString*)str];
}
}
return [NSString stringWithString:out];
}

I don't believe there is a public API to do this on iOS. On OSX there are a number of SecCertificate APIs to pick apart the X.509 information.

FYI, assuming you're using HTTPS, checking the CN yourself is mostly useless, because the OS already checks to make sure the name is present in the cert. You're more likely to want to check the public key (for key pinning), which you can get from the trust object without touching the certificate directly.
If the public key matches the previous key, then either the site is legit or somebody has thoroughly compromised the site.

Related

iOS: RSA Encrypt using public key (with modulus and exponent)

I'm trying to RSA encrypt an NSData using a public key. The public key is in this format:
<RSAKeyValue>
<Modulus>yOTe0L1/NcbXdZYwliS82MiTE8VD5WD23S4RDsdbJOFzCLbsyb4d+K1M5fC+xDfCkji1zQjPiiiToZ7JSj/2ww==</Modulus>
<Exponent>AWAB</Exponent>
</RSAKeyValue>
After extracting the modulus and exponent from the XML string, how do I get a SecKeyRef out of those to be used as publicKey in the method below?
+ (NSString *)encryptRSA:(NSString *)plainTextString key:(SecKeyRef)publicKey
{
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
uint8_t *cipherBuffer = malloc(cipherBufferSize);
uint8_t *nonce = (uint8_t *)[plainTextString UTF8String];
SecKeyEncrypt(publicKey,
kSecPaddingOAEP,
nonce,
strlen( (char*)nonce ),
&cipherBuffer[0],
&cipherBufferSize);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
return [encryptedData base64EncodedString];
}
I can't seem to find a definite answer anywhere.
Wow, no wonder it's so hard to find an answer to this. I spent 2 days down the crypto-rabbit hole, and it's not pretty.
The easy way
Use Chilkat iOS RSA Library. One major downside: cost $189! :O
The hard way
Parse the XML, use SCZ-BasicEncodingRules-iOS to generate a public key data out of the modulus and exponent. If that works, create a dummy keychain using that public key (follow sample code here), extract the public key now in SecKeyRef format and pass it to the encryptRSA method in the question. Finally, cleanup, remove the dummy keychain. Sounds good in theory, but I have never tested this thoroughly, if you do, let me know!
I have used the below method for encryption using public key without using any third party libs, guess it may help who is looking for the same after they implemented it just as I did :D
+(NSString *)encryptRSA:(NSString *)plainTextString key:(SecKeyRef)publicKey
{
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
uint8_t *cipherBuffer = malloc(cipherBufferSize);
uint8_t *nonce = (uint8_t *)[plainTextString UTF8String];
SecKeyEncrypt(publicKey,
kSecPaddingPKCS1,
nonce,
strlen( (char*)nonce ),
&cipherBuffer[0],
&cipherBufferSize);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
return [encryptedData base64EncodedStringWithOptions:0];
}
I think this will help u!
you can create a java file like fellow:
this java funtion will generate a public key to base64String
public static RSAPublicKey getPublicKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus,16);
BigInteger b2 = new BigInteger(exponent,16);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String encodePublicKey(byte[] encoded) throws Exception{
BASE64Encoder base64Encoder= new BASE64Encoder();
String s=base64Encoder.encode(encoded);
return s;
u should use like :encodePublicKey(publicKey.getEncoded());
Got it!

how to add FileAttachment annotations of pdf

Hello i am using this library for implementation of annotation but facing one issue of attachment of image it provide PdfFileSpec objective c++ function i tried to convert this function to objective c but getting error in below image
class PODOFO_DOC_API PdfFileSpec : public PdfElement {
public:
PdfFileSpec( const char* pszFilename, bool bEmbedd, PdfDocument* pParent );
PdfFileSpec( const char* pszFilename, bool bEmbedd, PdfVecObjects* pParent );
/* Petr P. Petrov 17 September 2009*/
/** Embeds the file in memory from "data" buffer under "pszFileName" fie name.
*/
PdfFileSpec( const char* pszFilename, const unsigned char* data, ptrdiff_t size, PdfVecObjects* pParent);
PdfFileSpec( PdfObject* pObject );
/** \returns the filename of this file specification.
* if no general name is available
* it will try the Unix, Mac and DOS keys too.
*/
const PdfString & GetFilename() const;
+(void)createFreeTextAnnotationOnPage:(NSInteger)pageIndex doc:(PdfMemDocument*)aDoc rect:(CGRect)aRect borderWidth:(double)bWidth title:(NSString*)title content:(NSString*)content bOpen:(Boolean)bOpen color:(UIColor*)color {
PoDoFo::PdfMemDocument *doc = (PoDoFo::PdfMemDocument *) aDoc;
PoDoFo::PdfPage* pPage = doc->GetPage(pageIndex);
if (! pPage) {
// couldn't get that page
return;
}
PoDoFo::PdfAnnotation* anno;
PoDoFo::EPdfAnnotation type= PoDoFo::ePdfAnnotation_Text;
PoDoFo::PdfRect rect;
rect.SetBottom(aRect.origin.y);
rect.SetLeft(aRect.origin.x);
rect.SetHeight(aRect.size.height);
rect.SetWidth(aRect.size.width);
NSData *data=UIImagePNGRepresentation([UIImage imageNamed:#"Add_button.png"]);
anno = pPage->CreateAnnotation(type , rect);
NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
PoDoFo::PdfString sTitle([title UTF8String]);
PoDoFo::PdfString sContent([content UTF8String]);
PoDoFo::PdfFileSpec data1([myString UTF8String], true, doc);
}
I don't see why you are using reinterpret_cast, and it's certainly being misused in the construction of data1.
Have you simply tried:
PoDoFo::PdfString sTitle([title UTF8String]);
PoDoFo::PdfString sContent([content UTF8String]);
PoDoFo::PdfFileSpec data1([myString UTF8String], true, doc);

Send multiple messages in Photoshop SDK

I am using the Photoshop Connection SDK to get my iPad app connected to Photoshop.
(The SDK with all sample iOS projects can be downloaded here if you want to look closer: http://www.adobe.com/devnet/photoshop/sdk.html)
While I have it working, its hard for me to solve my issue as I don't entirely understand the networking between the iPad and Photoshop. So here is my issue:
NSString *s1 = [NSString stringWithUTF8String:"app.activeDocument.layers[0].name;"];
NSData *dataToSend = [s1 dataUsingEncoding:NSUTF8StringEncoding];
[self sendJavaScriptMessage:dataToSend];
artLayerName_transaction = transaction_id -1;
There is a little snippet of code, to send a message asking for the name of layer at index 0. That works great. However, let's say I try and send the same message straight after that but for index 1 as well.
Both messages are sent, but only one returns its string. The string is returned here:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent;
If you see the example projects its just the same, each message with its own transaction id as well. The method goes through a bunch of decrypting and things to receive that string and then reaches this:
NSString *string = [[NSString alloc] initWithBytes:received_data length:received_length encoding:NSUTF8StringEncoding];
if (content != 1)
{
if (transaction == artLayerName_transaction)
{
[self processLayerName:string];
needOutput = NO;
}
}
I've out the full method at the bottom for full analysing.
It checks that its receiving the specific message so I can then take the result (string, my layer name) and do what I like with it. However when I try and send more than one message with the same transaction id I only get one of the two results. In the code above, string gives me both my layer names, but the if statement is only called once.
Is there a known way around sending several messages at once? I have tried getting an array back rather than several strings without luck as well.
Apparently I need to modify the code to accept more then message. However I don't really understand the code well enough, so please also explain any of the principals behind it.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent
{
NSInputStream * istream;
switch(streamEvent)
{
case NSStreamEventHasBytesAvailable:;
UInt8 buffer[1024];
unsigned int actuallyRead = 0;
istream = (NSInputStream *)aStream;
if (!dataBuffer)
{
dataBuffer = [[NSMutableData alloc] initWithCapacity:2048];
}
actuallyRead = [istream read:buffer maxLength:1024];
[dataBuffer appendBytes:buffer length:actuallyRead];
// see if we have enough to process, loop over messages in buffer
while( YES )
{
// Did we read the header yet?
if ( packetBodySize == -1 )
{
// Do we have enough bytes in the buffer to read the header?
if ( [dataBuffer length] >= sizeof(int) ) {
// extract length
memcpy(&packetBodySize, [dataBuffer bytes], sizeof(int));
packetBodySize = ntohl( packetBodySize ); // size is in network byte order
// remove that chunk from buffer
NSRange rangeToDelete = {0, sizeof(int)};
[dataBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
}
else {
// We don't have enough yet. Will wait for more data.
break;
}
}
// We should now have the header. Time to extract the body.
if ( [dataBuffer length] >= ((NSUInteger) packetBodySize) )
{
// We now have enough data to extract a meaningful packet.
const int kPrologLength = 16;
char *buffer = (char *)[dataBuffer bytes];
// if incoming message is color change, then don't display message
BOOL needOutput = YES;
// fetch the communication status
unsigned long com_status = *((unsigned long *)(buffer + 0));
com_status = ntohl( com_status );
// decrypt the message
size_t decryptedLength = (size_t) packetBodySize - 4; // don't include com status
int skip_message = 0;
if (com_status == 0 && sCryptorRef)
{
PSCryptorStatus decryptResult = EncryptDecrypt (sCryptorRef, false, buffer+4, decryptedLength, buffer+4, decryptedLength, &decryptedLength);
if (kCryptorSuccess != decryptResult)
{
// failed to decrypt. Ingore messageg and disconnect
skip_message = 1;
[self logMessage:#"ERROR: Decryption failed. Wrong password.\n" clearLine:NO];
}
}
else
{
if (com_status != 0)
[self logMessage:#"ERROR: Problem with communication, possible wrong password.\n" clearLine:NO];
if (!sCryptorRef)
[self logMessage:#"ERROR: Cryptor Ref is NULL, possible reason being that password was not supplied or password binding function failed.\n" clearLine:NO];
}
// Interpret encrypted section
if (!skip_message)
{
// version, 32 bit unsigned int, network byte order
unsigned long protocol_version = *((unsigned long *)(buffer + 4));
protocol_version = ntohl( protocol_version );
if (protocol_version != 1)
{
// either the message is corrupted or the protocol is newer.
[self logMessage:#"Incoming protocol version is different the expected. (or the message is corrupted.) Not processing.\n" clearLine:NO];
skip_message = 1;
}
if (!skip_message)
{
// transaction, 32 bit unsigned int, network byte order
unsigned long transaction = *((unsigned long *)(buffer + 8));
transaction = ntohl( transaction );
// content type, 32 bit unsigned int, network byte order
unsigned long content = *((unsigned long *)(buffer + 12));
content = ntohl( content );
unsigned char *received_data = (unsigned char *)(buffer+kPrologLength);
int received_length = (decryptedLength-(kPrologLength-4));
if (content == 3) // image data
{
// process image data
unsigned char image_type = *((unsigned char *)received_data);
[self logMessage:#"Incoming data is IMAGE. Skipping\n" clearLine:NO];
if (image_type == 1) // JPEG
{
[self logMessage:#"By the way, incoming image is JPEG\n" clearLine:NO];
}
else if (image_type == 2) // Pixmap
{
[self logMessage:#"By the way, incoming image is Pixmap\n" clearLine:NO];
}
else
{
[self logMessage:#"Unknown image type\n" clearLine:NO];
}
}
else
{
// Set the response string
NSString *string = [[NSString alloc] initWithBytes:received_data length:received_length encoding:NSUTF8StringEncoding];
//NSLog(#"string: %#\n id:%li", string, transaction);
// see if this is a response we're looking for
if (content != 1)
{
if (transaction == foregroundColor_subscription || transaction == foregroundColor_transaction)
{
[self processForegroundChange:string];
needOutput = NO;
}
if (transaction == backgroundColor_subscription || transaction == backgroundColor_transaction)
{
[self processBackgroundChange:string];
needOutput = NO;
}
if (transaction == tool_transaction)
{
[self processToolChange:string];
needOutput = NO;
}
if (transaction == artLayerName_transaction)
{
[self processLayerName:string];
needOutput = NO;
}
}
//Tells me about every event thats happened (spammy tech nonsence, no good for user log)
//if (needOutput) [self logMessage:string clearLine:NO];
}
}
}
// Remove that chunk from buffer
NSRange rangeToDelete = {0, packetBodySize};
[dataBuffer replaceBytesInRange:rangeToDelete withBytes:NULL length:0];
// We have processed the packet. Resetting the state.
packetBodySize = -1;
}
else
{
// Not enough data yet. Will wait.
break;
}
}
break;
case NSStreamEventEndEncountered:;
[self closeStreams];
[self logMessage:[NSString stringWithFormat: #"%# End encountered, closing stream.\n", outputMessage.text] clearLine:NO];
break;
case NSStreamEventHasSpaceAvailable:
case NSStreamEventErrorOccurred:
case NSStreamEventOpenCompleted:
case NSStreamEventNone:
default:
break;
}
}
EDIT:
While this has worked, it has bumped me into another issue. I looked at the psconnection sample and saw what you were talking about, however I haven't been using the full framework but have it connect like in the other sample projects. I very simply increase my transaction id by 1 every time it comes to process a layer name such as in the code above.
Like this:
if (transaction == docName_transaction)
{
docName_transaction++;
[self processDocName:string];
}
This works for the most part, however if I do this to another transaction id at the same time I get overlap. Meaning I end up processing the result for one id at the wrong time. Say I am getting both the total number of docs and the name of each one.
So I have two if statements like above, but I end up processing the total docs in both if statements and I can't see how to atop this overlap. Its fairly important to be able to receive multiple messages at once.
I just downloaded samples, and as far as i can see, you should use unique transaction IDs for transactions that are waiting for response.
I mean if you send layer_name request with transactionID 1, you cannot use 1, until it's response is received.
So better case will be, store your transactionID's in a Dictionary (transactionID as key), and message type as value.
just like:
transactions[transaction_id] = #"layername";
and when you receive response:
use:
transactions[transaction_id] to get the message type (ex: layername) behave according to this.
You can also put some other details to transactions dictionary (you can put a dictionary containing all information, which command, on which object, etc)

Importing a client certificate into the iPhone's keychain

I am writing an application that is communicating with a server which requires the client to authenticate itself using a client certificate. I need to extract the certificate from a .p12 file in the application bundle and add it to the application keychain.
I've been trying to figure out how to get it working from Apple's "Certificate, Key, and Trust Services Tasks for iOS", but to me it seems incomplete and does not specify how I add anything to the keychain(?).
I am quite lost and any help is appriciated, thanks in advance!
"Certificate, Key, and Trust Services Tasks for iOS" does contain sufficient information to extract certificate from a .p12 file.
from listing 2-1 demonstrate how you can extract SecIdentityRef
from listing 2-2 second line (// 1) shows how you can copy
SecCertificateRef out of SecIdentityRef.
example loading p12 file, extract certificate, install to keychain.
(error handling and memory management was not included)
NSString * password = #"Your-P12-File-Password";
NSString * path = [[NSBundle mainBundle]
pathForResource:#"Your-P12-File" ofType:#"p12"];
// prepare password
CFStringRef cfPassword = CFStringCreateWithCString(NULL,
password.UTF8String,
kCFStringEncodingUTF8);
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { cfPassword };
CFDictionaryRef optionsDictionary
= CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1,
NULL, NULL);
// prepare p12 file content
NSData * fileContent = [[NSData alloc] initWithContentsOfFile:path];
CFDataRef cfDataOfFileContent = (__bridge CFDataRef)fileContent;
// extract p12 file content into items (array)
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus status = errSecSuccess;
status = SecPKCS12Import(cfDataOfFileContent,
optionsDictionary,
&items);
// TODO: error handling on status
// extract identity
CFDictionaryRef yourIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(yourIdentityAndTrust,
kSecImportItemIdentity);
SecIdentityRef yourIdentity = (SecIdentityRef)tempIdentity;
// get certificate from identity
SecCertificateRef yourCertificate = NULL;
status = SecIdentityCopyCertificate(yourIdentity, &yourCertificate);
// at last, install certificate into keychain
const void *keys2[] = { kSecValueRef, kSecClass };
const void *values2[] = { yourCertificate, kSecClassCertificate };
CFDictionaryRef dict
= CFDictionaryCreate(kCFAllocatorDefault, keys2, values2,
2, NULL, NULL);
status = SecItemAdd(dict, NULL);
// TODO: error handling on status

Parsing email "Received:" headers

We need to parse Received: email headers according to RFC 5321. We need to extract domains or IPs through which the mail has traversed. Also, we need to figure out if an IP is an internal IP.
Is there already a library which can help out, preferably in C\C++?
For example:
Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123])
by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597;
Fri, 12 Jul 2002 16:11:20 -0400 (EDT)
We need to extract the "by" server.
The format used by 'Received' lines is defined in RFC 2821, and regex can't parse it.
(You can try anyway, and for a limited subset of headers produced by known software you might succeed, but when you attach this to the range of strange stuff found in real-world mail it will fail.)
Use an existing RFC 2821 parser and you should be OK, but otherwise you should expect failure, and write the software to cope with it. Don't base anything important like a security system around it.
We need to extract the "by" server.
'from' is more likely to be of use. The hostname given in a 'by' line is as seen by the host itself, so there is no guarantee it will be a publically resolvable FQDN. And of course you don't tend to get valid (TCP-Info) there.
There is a Perl Received module which is a fork of the SpamAssassin code. It returns a hash for a Received header with the relevant information. For example
{ ip => '64.12.136.4',
id => '875522',
by => 'xxx.com',
helo => 'imo-m01.mx.aol.com' }
vmime should be fine, moreless any mail library will allow you to do that.
You'll want to use Regular Expressions possibly
(?<=by).*(?=with)
This will give you pilot01.cl.msu.edu (8.10.2/8.10.2)
Edit:
I find it amusing that this was modded down when it actually gets what the OP asked for.
C#:
string header = "Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123]) by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597; Fri, 12 Jul 2002 16:11:20 -0400 (EDT)";
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(#"(?<=by).*(?=with)");
System.Text.RegularExpressions.Match m = r.Match(header);
Console.WriteLine(m.Captures[0].Value);
Console.ReadKey();
I didnt claim that it was complete, but am wondering if the person that gave it a -1 even tried. Meh..
You can use regular expressions. It would look like this(not tested):
#include <regex.h>
regex_t *re = malloc(sizeof(regex_t));
const char *restr = "by ([A-Za-z.]+) \(([^\)]*)\)";
check(regcomp(re, restr, REG_EXTENDED | REG_ICASE), "regcomp");
size_t nmatch = 1;
regmatch_t *matches = malloc(sizeof(regmatch_t) * nmatch);
int ret = regexec(re, YOUR_STRING, nmatch, matches, 0);
check(ret != 0, "regexec");
int size;
size = matches[2].rm_eo - matches[2].rm_so;
char *host = malloc(sizeof(char) * size);
strncpy(host, YOUR_STRING + matches[2].rm_so, size );
host[size] = '\0';
size = matches[3].rm_eo - matches[3].rm_so;
char *ip = malloc(sizeof(char) * size);
strncpy(ip, YOUR_STRING + matches[3].rm_so, size );
ip[size] = '\0';
check is a macro to help you figure out if there are any problems:
#define check(condition, description) if (condition) { fprintf(stdout, "%s:%i - %s - %s\n", __FILE__, __LINE__, description, strerror(errno)); exit(1); }
typedef struct mailHeaders{
char name[100];
char value[2000];
}mailHeaders;
int header_count = 0;
mailHeaders headers[30]; // A struct to hold the name value pairs
char *GetMailHeader(char *name)
{
char *value = NULL;;
int i;
for(i=0;i<header_count;i++){
if(strcmp(name,headers[i].name) == 0){
value = headers[i].value;
break;
}
}
return(value);
}
void ReadMail(void)
{
//Loop through the email message line by line to separate the headers. Then save the name value pairs to a linked list or struct.
char *Received = NULL // Received header
char *mail = NULL; // Buffer that has the email message.
char *line = NULL; // A line of text in the email.
char *name = NULL; // Header name
char *value = NULL; // Header value
int index = -1; // Header index
memset(&headers,'\0',sizeof(mailHeaders));
line = strtok(mail,"\n");
while(line != NULL)
{
if(*line == '\t') // Tabbed headers
{
strcat(headers[index].value,line); // Concatenate the tabbed values
}
else
{
name = line;
value = strchr(line,':'); // Split the name value pairs.
if(value != NULL)
{
*value='\0'; // NULL the colon
value++; // Move the pointer past the NULL character to separate the name and value
index++;
strcpy(headers[index].name,name); // Copy the name to the data structure
strcpy(headers[index].value,value); // Copy the value to the data structure
}
}
if(*line == '\r') // End of headers
break;
line = strtok(NULL,"\n"); // Get next header
header_count = index;
}
Received = GetMailHeader("Received");
}
It is not difficult to parse such headers, even manually line-by-line. A regex could help there by looking at by\s+(\w)+\(. For C++, you could try that library or that one.
Have you considered using regular expressions?
Here is a list of internal, non-routable address ranges.