public static bool SslCheckHostnameMatch(SafeSslHandle handle, string hostName, DateTime notBefore) { int result; // The IdnMapping converts Unicode input into the IDNA punycode sequence. // It also does host case normalization. The bypass logic would be something // like "all characters being within [a-z0-9.-]+" // // The SSL Policy (SecPolicyCreateSSL) has been verified as not inherently supporting // IDNA as of macOS 10.12.1 (Sierra). If it supports low-level IDNA at a later date, // this code could be removed. // // It was verified as supporting case invariant match as of 10.12.1 (Sierra). string matchName = s_idnMapping.GetAscii(hostName); using (SafeCFDateHandle cfNotBefore = CoreFoundation.CFDateCreate(notBefore)) using (SafeCreateHandle cfHostname = CoreFoundation.CFStringCreateWithCString(matchName)) { result = AppleCryptoNative_SslIsHostnameMatch(handle, cfHostname, cfNotBefore); } switch (result) { case 0: return(false); case 1: return(true); default: Debug.Fail($"AppleCryptoNative_SslIsHostnameMatch returned {result}"); throw new SslException(); } }
internal static byte[] X509ExportPfx(IntPtr[] certHandles, SafePasswordHandle exportPassword) { SafeCreateHandle cfPassphrase = s_emptyExportString; bool releasePassword = false; try { if (!exportPassword.IsInvalid) { exportPassword.DangerousAddRef(ref releasePassword); IntPtr passwordHandle = exportPassword.DangerousGetHandle(); if (passwordHandle != IntPtr.Zero) { cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); } } return(X509Export(X509ContentType.Pkcs12, cfPassphrase, certHandles)); } finally { if (releasePassword) { exportPassword.DangerousRelease(); } if (cfPassphrase != s_emptyExportString) { cfPassphrase.Dispose(); } } }
internal static unsafe void SslCtxSetAlpnProtos(SafeSslHandle ctx, List <SslApplicationProtocol> protocols) { SafeCreateHandle cfProtocolsRefs = null; SafeCreateHandle[] cfProtocolsArrayRef = null; try { if (protocols.Count == 1 && protocols[0] == SslApplicationProtocol.Http2) { cfProtocolsRefs = s_cfAlpnHttp211Protocols; } else if (protocols.Count == 1 && protocols[0] == SslApplicationProtocol.Http11) { cfProtocolsRefs = s_cfAlpnHttp11Protocols; } else if (protocols.Count == 2 && protocols[0] == SslApplicationProtocol.Http2 && protocols[1] == SslApplicationProtocol.Http11) { cfProtocolsRefs = s_cfAlpnHttp211Protocols; } else { // we did not match common case. This is more expensive path allocating Core Foundation objects. cfProtocolsArrayRef = new SafeCreateHandle[protocols.Count]; IntPtr[] protocolsPtr = new System.IntPtr[protocols.Count]; for (int i = 0; i < protocols.Count; i++) { cfProtocolsArrayRef[i] = CoreFoundation.CFStringCreateWithCString(protocols[i].ToString()); protocolsPtr[i] = cfProtocolsArrayRef[i].DangerousGetHandle(); } cfProtocolsRefs = CoreFoundation.CFArrayCreate(protocolsPtr, (UIntPtr)protocols.Count); } int osStatus; int result = SSLSetALPNProtocols(ctx, cfProtocolsRefs, out osStatus); if (result != 1) { throw CreateExceptionForOSStatus(osStatus); } } finally { if (cfProtocolsArrayRef != null) { for (int i = 0; i < cfProtocolsArrayRef.Length; i++) { cfProtocolsArrayRef[i]?.Dispose(); } cfProtocolsRefs?.Dispose(); } } }
internal static SafeCFArrayHandle X509ImportCollection( ReadOnlySpan <byte> bytes, X509ContentType contentType, SafePasswordHandle importPassword) { SafeCreateHandle cfPassphrase = s_emptyExportString; bool releasePassword = false; SafeCFArrayHandle collectionHandle; int osStatus; try { if (!importPassword.IsInvalid) { importPassword.DangerousAddRef(ref releasePassword); IntPtr passwordHandle = importPassword.DangerousGetHandle(); if (passwordHandle != IntPtr.Zero) { cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); } } osStatus = AppleCryptoNative_X509ImportCollection( ref MemoryMarshal.GetReference(bytes), bytes.Length, contentType, cfPassphrase, out collectionHandle); if (osStatus == 0) { return(collectionHandle); } } finally { if (releasePassword) { importPassword.DangerousRelease(); } if (cfPassphrase != s_emptyExportString) { cfPassphrase.Dispose(); } } collectionHandle.Dispose(); throw CreateExceptionForOSStatus(osStatus); }
internal static byte[] SecKeyExport( SafeSecKeyRefHandle key, bool exportPrivate, string password) { SafeCreateHandle exportPassword = exportPrivate ? CoreFoundation.CFStringCreateWithCString(password) : s_nullExportString; int ret; SafeCFDataHandle cfData; int osStatus; try { ret = AppleCryptoNative_SecKeyExport( key, exportPrivate ? 1 : 0, exportPassword, out cfData, out osStatus); } finally { if (exportPassword != s_nullExportString) { exportPassword.Dispose(); } } byte[] exportedData; using (cfData) { if (ret == 0) { throw CreateExceptionForOSStatus(osStatus); } if (ret != 1) { Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}"); throw new CryptographicException(); } exportedData = CoreFoundation.CFGetData(cfData); } return(exportedData); }
public TypedPtr <CFString> CFStringCreateWithCString(IntPtr allocator, byte[] buffer) { return(CoreFoundation.CFStringCreateWithCString(allocator, buffer, CFStringBuiltInEncodings.kCFStringEncodingUTF8)); }
internal static SafeCFArrayHandle X509ImportCollection( byte[] bytes, X509ContentType contentType, SafePasswordHandle importPassword, SafeKeychainHandle keychain, bool exportable) { SafeCreateHandle cfPassphrase = s_nullExportString; bool releasePassword = false; int ret; SafeCFArrayHandle collectionHandle; int osStatus; try { if (!importPassword.IsInvalid) { importPassword.DangerousAddRef(ref releasePassword); IntPtr passwordHandle = importPassword.DangerousGetHandle(); if (passwordHandle != IntPtr.Zero) { cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); } } ret = AppleCryptoNative_X509ImportCollection( bytes, bytes.Length, contentType, cfPassphrase, keychain, exportable ? 1 : 0, out collectionHandle, out osStatus); if (ret == 1) { return(collectionHandle); } } finally { if (releasePassword) { importPassword.DangerousRelease(); } if (cfPassphrase != s_nullExportString) { cfPassphrase.Dispose(); } } collectionHandle.Dispose(); const int SeeOSStatus = 0; const int ImportReturnedEmpty = -2; const int ImportReturnedNull = -3; switch (ret) { case SeeOSStatus: throw CreateExceptionForOSStatus(osStatus); case ImportReturnedNull: case ImportReturnedEmpty: throw new CryptographicException(); default: Debug.Fail($"Unexpected return value {ret}"); throw new CryptographicException(); } }
internal static DerSequenceReader SecKeyExport( SafeSecKeyRefHandle key, bool exportPrivate) { // Apple requires all private keys to be exported encrypted, but since we're trying to export // as parsed structures we will need to decrypt it for the user. const string ExportPassword = "******"; SafeCreateHandle exportPassword = exportPrivate ? CoreFoundation.CFStringCreateWithCString(ExportPassword) : s_nullExportString; int ret; SafeCFDataHandle cfData; int osStatus; try { ret = AppleCryptoNative_SecKeyExport( key, exportPrivate ? 1 : 0, exportPassword, out cfData, out osStatus); } finally { if (exportPassword != s_nullExportString) { exportPassword.Dispose(); } } byte[] exportedData; using (cfData) { if (ret == 0) { throw CreateExceptionForOSStatus(osStatus); } if (ret != 1) { Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}"); throw new CryptographicException(); } exportedData = CoreFoundation.CFGetData(cfData); } DerSequenceReader reader = new DerSequenceReader(exportedData); if (!exportPrivate) { return(reader); } byte tag = reader.PeekTag(); // PKCS#8 defines two structures, PrivateKeyInfo, which starts with an integer, // and EncryptedPrivateKey, which starts with an encryption algorithm (DER sequence). if (tag == (byte)DerSequenceReader.DerTag.Integer) { return(reader); } const byte ConstructedSequence = DerSequenceReader.ConstructedFlag | (byte)DerSequenceReader.DerTag.Sequence; if (tag == ConstructedSequence) { return(ReadEncryptedPkcs8Blob(ExportPassword, reader)); } Debug.Fail($"Data was neither PrivateKey or EncryptedPrivateKey: {tag:X2}"); throw new CryptographicException(); }