Skip to content

Commit

Permalink
Add name of corrupted certificate to CryptographicException on Mac
Browse files Browse the repository at this point in the history
* Add name of corrupted certificate to CryptographicException on Mac

When trying to create a CertificateData out of raw X509 byte array it might throw if the data is corrupted. The existing exception message does not provide any information which might help the user identify the corrupted certificate and fix it.

This change, makes a native call to SecCertificateCopySubjectSummary which will provide a human readable summary of the certificate, and will generate an exception message using this string.

Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
  • Loading branch information
odhanson and bartonjs committed Jun 21, 2021
1 parent 44bb2ad commit fb2e61e
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ internal static partial class AppleCrypto
out SafeCFDataHandle cfDataOut,
out int pOSStatus);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_X509GetSubjectSummary(
SafeSecCertificateHandle cert,
out SafeCFStringHandle cfSubjectSummaryOut);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_X509GetPublicKey(SafeSecCertificateHandle cert, out SafeSecKeyRefHandle publicKey, out int pOSStatus);

Expand Down Expand Up @@ -72,6 +77,31 @@ internal static byte[] X509GetRawData(SafeSecCertificateHandle cert)
}
}

internal static string? X509GetSubjectSummary(SafeSecCertificateHandle cert)
{
SafeCFStringHandle subjectSummary;

int ret = AppleCryptoNative_X509GetSubjectSummary(
cert,
out subjectSummary);

using (subjectSummary)
{
if (ret == 1)
{
return CoreFoundation.CFStringToString(subjectSummary);
}
}

if (ret == 0)
{
return null;
}

Debug.Fail($"Unexpected return value {ret}");
throw new CryptographicException();
}

internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentityHandle identity)
{
SafeSecKeyRefHandle key;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ static const Entry s_cryptoAppleNative[] =
DllImportEntry(AppleCryptoNative_GetOSStatusForChainStatus)
DllImportEntry(AppleCryptoNative_X509ChainSetTrustAnchorCertificates)
DllImportEntry(AppleCryptoNative_Pbkdf2)
DllImportEntry(AppleCryptoNative_X509GetSubjectSummary)
};

EXTERN_C const void* CryptoAppleResolveDllImport(const char* name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,15 @@ int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDa
*pOSStatus = *ppDataOut == NULL ? errSecParam : noErr;
return (*pOSStatus == noErr);
}

int32_t AppleCryptoNative_X509GetSubjectSummary(SecCertificateRef cert, CFStringRef* ppSummaryOut)
{
if (ppSummaryOut != NULL)
*ppSummaryOut = NULL;

if (cert == NULL || ppSummaryOut == NULL)
return kErrorBadInput;

*ppSummaryOut = SecCertificateCopySubjectSummary(cert);
return (*ppSummaryOut != NULL);
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,13 @@ ppDataOut: Receives a CFDataRef with the exported blob
pOSStatus: Receives the result of SecItemExport
*/
PALEXPORT int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDataOut, int32_t* pOSStatus);

/*
Extract a string that contains a human-readable summary of the contents of the certificate
Returns 1 on success, 0 on failure, any other value indicates invalid state.
Output:
ppSummaryOut: Receives a CFDataRef with the exported blob
*/
PALEXPORT int32_t AppleCryptoNative_X509GetSubjectSummary(SecCertificateRef cert, CFStringRef* ppSummaryOut);
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,26 @@ private void EnsureCertData()
return;

Debug.Assert(!_certHandle.IsInvalid);
_certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle));
string? subjectSummary = Interop.AppleCrypto.X509GetSubjectSummary(_certHandle);

try
{
_certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle));
}
catch (CryptographicException e)
{
if (subjectSummary is null)
{
throw;
}

string message = SR.Format(
SR.Cryptography_X509_CertificateCorrupted,
subjectSummary);

throw new CryptographicException(message, e);
}

_readCertData = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@
<data name="Cryptography_X509_NoOrMismatchedPemKey" xml:space="preserve">
<value>The key contents do not contain a PEM, the content is malformed, or the key does not match the certificate.</value>
</data>
<data name="Cryptography_X509_CertificateCorrupted" xml:space="preserve">
<value>Certificate '{0}' is corrupted.</value>
</data>
<data name="InvalidOperation_EnumNotStarted" xml:space="preserve">
<value>Enumeration has not started. Call MoveNext.</value>
</data>
Expand Down

0 comments on commit fb2e61e

Please sign in to comment.