I have a SecTrustRef
object from the system that I'd like to evaluate myself. Just calling SecTrustEvaluateAsync
will be sufficient for this job. The problem is, I must evaluate it in a different process as only this other process has access to the keychains where the CA certificates are stored that may cause evaluation to succeed.
The two processes have an IPC link that allows me to exchange arbitrary byte data between them but I don't see any way to easily serialize a SecTrustRef
into byte data and deserialize that data back to an object at the other process. There doesn't seem to be a persistent storage mechanism for SecTrustRef
.
So am I overlooking something important here, or do I really have to get all the certs (SecTrustGetCertificateAtIndex
) and all the policies (SecTrustCopyPolicies
) and serialize these myself?
And if so, how would I serialize a policy?
For the certificate (SecCertificateRef
) it's rather easy, I just call SecCertificateCopyData
and later on SecCertificateCreateWithData
.
But for policies I can only call SecPolicyCopyProperties
on one side and later on SecPolicyCreateWithProperties
, however the later one requires a 2nd parameter, a policyIdentifier
and I see no way to get that value from an existing policy. What am I missing?
Reading through the source of the Security framework, I finally figured it out how to copy a SecPolicyRef
:
SecPolicyCreateWithProperties
wants what it calls a "policyIdentifier". It's a constant like kSecPolicyAppleIPsec
.SecPolicyCreateIPsec
).SecPolicyCreate
(which is private). They end up passing the same identifier value that you passed to SecPolicyCreateWithProperties
._oid
field!The identifier is actually the OID. You can get it either via SecPolicyCopyProperties(policy)
(stored in the dictionary with key kSecPolicyOid
) or via SecPolicyGetOID
(but that returns it as an inconvenient CSSM_OID
). Some of those specialized initializers also use values from the properties dictionary passed to SecPolicyCreateWithProperties
, those should be present in the copied properties dictionary already.
So this gives us:
Serialization:
CFDictionaryRef createSerializedPolicy(SecPolicyRef policy) {
// Already contains all the information needed.
return SecPolicyCopyProperties(policy);
}
Deserialization:
SecPolicyRef createDeserializedPolicy (CFDictionaryRef serialized) {
CFStringRef oid = CFDictionaryGetValue(serialized, kSecPolicyOid);
if (oid == NULL || CFGetTypeID(oid) != CFStringGetTypeID()) {
return NULL;
}
return SecPolicyCreateWithProperties(oid, serialized);
}
To reproduce the original SecTrustRef as closely as possible, the anchors need to be copied as well. There is an internal variable _anchorsOnly
which is set to true
once you set anchors. Unfortunately, there is no way to query this value and I've seen it being false
in trusts passed by NSURLSession
, for example. No idea yet on how to get this value in a public way.
Another problematic bit are the exceptions: if _exceptions
is NULL but you query them via SecTrustCopyExceptions(trust)
, you do get data! And if you assign that to the deserialized trust via SecTrustSetExceptions(trust, exceptions)
you suddenly end up with exceptions that were not there before and can change the evaluation result! (I've seen those suddenly appearing exceptions lead to an evaluation result of "proceed" instead of "recoverable trust failure").