private ICertificatePal CopyWithPrivateKey(SecKeyPair keyPair) { if (keyPair.PrivateKey == null) { // Both Windows and Linux/OpenSSL are unaware if they bound a public or private key. // Here, we do know. So throw if we can't do what they asked. throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); } SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(keyPair.PrivateKey); if (keychain.IsInvalid) { keychain = Interop.AppleCrypto.CreateTemporaryKeychain(); } using (keychain) { SafeSecIdentityHandle identityHandle = Interop.AppleCrypto.X509CopyWithPrivateKey( _certHandle, keyPair.PrivateKey, keychain); AppleCertificatePal newPal = new AppleCertificatePal(identityHandle); newPal.HoldPrivateKey(); return(newPal); } }
public X509ContentType GetCertContentType(ReadOnlySpan <byte> rawData) { const int errSecUnknownFormat = -25257; if (rawData == null || rawData.Length == 0) { // Throw to match Windows and Unix behavior. throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat); } X509ContentType result = X509ContentType.Unknown; AppleCertificatePal.TryDecodePem( rawData, (derData, contentType) => { result = contentType; return(false); }); if (result == X509ContentType.Unknown) { result = AppleCertificatePal.GetDerCertContentType(rawData); } if (result == X509ContentType.Unknown) { // Throw to match Windows and Unix behavior. throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat); } return(result); }
internal static AppleCertificatePal ImportPkcs12(UnixPkcs12Reader.CertAndKey certAndKey) { AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert !; if (certAndKey.Key != null) { AppleCertificateExporter exporter = new AppleCertificateExporter(new TempExportPal(pal), certAndKey.Key); byte[] smallPfx = exporter.Export(X509ContentType.Pkcs12, s_passwordExportHandle) !; SafeSecIdentityHandle identityHandle; SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( smallPfx, X509ContentType.Pkcs12, s_passwordExportHandle, out identityHandle); if (identityHandle.IsInvalid) { identityHandle.Dispose(); return(new AppleCertificatePal(certHandle)); } certHandle.Dispose(); return(new AppleCertificatePal(identityHandle)); } return(pal); }
internal static ICertificatePal ImportPkcs12NonExportable( AppleCertificatePal cert, SafeSecKeyRefHandle privateKey, SafePasswordHandle password, SafeKeychainHandle keychain) { Pkcs12SmallExport exporter = new Pkcs12SmallExport(new TempExportPal(cert), privateKey); byte[] smallPfx = exporter.Export(X509ContentType.Pkcs12, password) !; SafeSecIdentityHandle identityHandle; SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( smallPfx, X509ContentType.Pkcs12, password, keychain, exportable: false, out identityHandle); // On Windows and Linux if a PFX uses a LocalKeyId to bind the wrong key to a cert, the // nonsensical object of "this cert, that key" is returned. // // On macOS, because we can't forge CFIdentityRefs without the keychain, we're subject to // Apple's more stringent matching of a consistent keypair. if (identityHandle.IsInvalid) { identityHandle.Dispose(); return(new AppleCertificatePal(certHandle)); } certHandle.Dispose(); return(new AppleCertificatePal(identityHandle)); }
public static ICertificatePal FromBlob( ReadOnlySpan <byte> rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { return(AppleCertificatePal.FromBlob(rawData, password, keyStorageFlags)); }
public void MoveTo(X509Certificate2Collection collection) { foreach (UnixPkcs12Reader.CertAndKey certAndKey in _pkcs12.EnumerateAll()) { AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert !; SafeSecKeyRefHandle?safeSecKeyRefHandle = ApplePkcs12Reader.GetPrivateKey(certAndKey.Key); using (safeSecKeyRefHandle) { ICertificatePal newPal; // SecItemImport doesn't seem to respect non-exportable import for PKCS#8, // only PKCS#12. // // So, as part of reading this PKCS#12 we now need to write the minimum // PKCS#12 in a normalized form, and ask the OS to import it. if (!_exportable && safeSecKeyRefHandle != null) { newPal = AppleCertificatePal.ImportPkcs12NonExportable( pal, safeSecKeyRefHandle, _password, _keychain); } else { newPal = pal.MoveToKeychain(_keychain, safeSecKeyRefHandle) ?? pal; } X509Certificate2 cert = new X509Certificate2(newPal); collection.Add(cert); } } }
public void MoveTo(X509Certificate2Collection collection) { foreach (UnixPkcs12Reader.CertAndKey certAndKey in _pkcs12.EnumerateAll()) { collection.Add(new X509Certificate2(AppleCertificatePal.ImportPkcs12(certAndKey))); } }
private static SafeSecKeyRefHandle DecodeECPublicKey(ICertificatePal?certificatePal) { const int errSecInvalidKeyRef = -67712; const int errSecUnsupportedKeySize = -67735; if (certificatePal is null) { throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); } AppleCertificatePal applePal = (AppleCertificatePal)certificatePal; SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); // If X509GetPublicKey uses the new SecCertificateCopyKey API it can return an invalid // key reference for unsupported algorithms. This currently happens for the BrainpoolP160r1 // algorithm in the test suite (as of macOS Mojave Developer Preview 4). if (key.IsInvalid) { throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecInvalidKeyRef); } // EccGetKeySizeInBits can fail for two reasons. First, the Apple implementation has changed // and we receive values from API that were not previously handled. In that case the // implementation will need to be adjusted to handle these values. Second, we deliberately // return 0 from the native code to prevent hitting buggy API implementations in Apple code // later. if (Interop.AppleCrypto.EccGetKeySizeInBits(key) == 0) { key.Dispose(); throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnsupportedKeySize); } return(key); }
public void Remove(ICertificatePal cert) { AppleCertificatePal applePal = (AppleCertificatePal)cert; var handle = (SafeKeychainItemHandle?)applePal.IdentityHandle ?? applePal.CertificateHandle; Interop.AppleCrypto.X509StoreRemoveCertificate(handle, _keychainHandle, _readonly); }
private SafeCreateHandle PrepareCertsArray(ICertificatePal cert, X509Certificate2Collection extraStore) { IntPtr[] ptrs = new IntPtr[1 + (extraStore?.Count ?? 0)]; SafeHandle[] safeHandles = new SafeHandle[ptrs.Length]; AppleCertificatePal applePal = (AppleCertificatePal)cert; safeHandles[0] = applePal.CertificateHandle; if (extraStore != null) { for (int i = 0; i < extraStore.Count; i++) { AppleCertificatePal extraCertPal = (AppleCertificatePal)extraStore[i].Pal; safeHandles[i + 1] = extraCertPal.CertificateHandle; } } int idx = 0; bool addedRef = false; try { for (idx = 0; idx < safeHandles.Length; idx++) { SafeHandle handle = safeHandles[idx]; handle.DangerousAddRef(ref addedRef); ptrs[idx] = handle.DangerousGetHandle(); } } catch { // If any DangerousAddRef failed, idx will be on the one that failed, so we'll start off // by subtracing one. for (idx--; idx >= 0; idx--) { safeHandles[idx].DangerousRelease(); } throw; } // Creating the array has the effect of calling CFRetain() on all of the pointers, so the native // resource is safe even if we DangerousRelease=>ReleaseHandle them. SafeCreateHandle certsArray = Interop.CoreFoundation.CFArrayCreate(ptrs, (UIntPtr)ptrs.Length); _extraHandles.Push(certsArray); for (idx = 0; idx < safeHandles.Length; idx++) { safeHandles[idx].DangerousRelease(); } return(certsArray); }
public void Remove(ICertificatePal cert) { if (_readonly) { throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); } AppleCertificatePal applePal = (AppleCertificatePal)cert; Interop.AppleCrypto.X509StoreRemoveCertificate(applePal.CertificateHandle, _keychainHandle); }
public void Add(ICertificatePal cert) { if (_readonly) { throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); } AppleCertificatePal applePal = (AppleCertificatePal)cert; var handle = (SafeKeychainItemHandle?)applePal.IdentityHandle ?? applePal.CertificateHandle; Interop.AppleCrypto.X509StoreAddCertificate(handle, _keychainHandle); }
private static ICertificatePal ImportPkcs12( byte[] rawData, SafePasswordHandle password, bool exportable, SafeKeychainHandle keychain) { using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData)) { reader.Decrypt(password); UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert(); AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert !; SafeSecKeyRefHandle?safeSecKeyRefHandle = ApplePkcs12Reader.GetPrivateKey(certAndKey.Key); AppleCertificatePal?newPal; using (safeSecKeyRefHandle) { // SecItemImport doesn't seem to respect non-exportable import for PKCS#8, // only PKCS#12. // // So, as part of reading this PKCS#12 we now need to write the minimum // PKCS#12 in a normalized form, and ask the OS to import it. if (!exportable && safeSecKeyRefHandle != null) { using (pal) { return(ImportPkcs12NonExportable(pal, safeSecKeyRefHandle, password, keychain)); } } newPal = pal.MoveToKeychain(keychain, safeSecKeyRefHandle); if (newPal != null) { pal.Dispose(); } } // If no new PAL came back, it means we moved the cert, but had no private key. return(newPal ?? pal); } }
protected override byte[] ExportPkcs7() { IntPtr[] certHandles; if (_singleCertPal != null) { certHandles = new[] { ((AppleCertificatePal)_singleCertPal).CertificateHandle.DangerousGetHandle() }; } else if (_certs !.Count > 0) { certHandles = new IntPtr[_certs.Count]; for (int i = 0; i < _certs.Count; i++) { AppleCertificatePal pal = (AppleCertificatePal)_certs[i].Pal; certHandles[i] = pal.CertificateHandle.DangerousGetHandle(); } }
public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, ICertificatePal certificatePal) { AppleCertificatePal applePal = certificatePal as AppleCertificatePal; if (applePal != null) { SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); switch (oid.Value) { case Oids.RsaRsa: return(new RSAImplementation.RSASecurityTransforms(key)); case Oids.DsaDsa: if (key.IsInvalid) { // SecCertificateCopyKey returns null for DSA, so fall back to manually building it. return(DecodeDsaPublicKey(encodedKeyValue, encodedParameters)); } return(new DSAImplementation.DSASecurityTransforms(key)); case Oids.Ecc: return(new ECDsaImplementation.ECDsaSecurityTransforms(key)); } key.Dispose(); } else { switch (oid.Value) { case Oids.RsaRsa: return(DecodeRsaPublicKey(encodedKeyValue)); case Oids.DsaDsa: return(DecodeDsaPublicKey(encodedKeyValue, encodedParameters)); } } throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); }
private byte[] ExportPkcs7() { IntPtr[] certHandles; if (_singleCertPal != null) { certHandles = new[] { ((AppleCertificatePal)_singleCertPal).CertificateHandle.DangerousGetHandle() }; } else { certHandles = new IntPtr[_certs.Count]; for (int i = 0; i < _certs.Count; i++) { AppleCertificatePal pal = (AppleCertificatePal)_certs[i].Pal; certHandles[i] = pal.CertificateHandle.DangerousGetHandle(); } } return(Interop.AppleCrypto.X509ExportPkcs7(certHandles)); }
protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan <char> password) { AppleCertificatePal pal = (AppleCertificatePal)certificatePal; return(pal.ExportPkcs8(password)); }
private ICertificatePal CopyWithPrivateKey(SecKeyPair keyPair) { if (keyPair.PrivateKey == null) { // Both Windows and Linux/OpenSSL are unaware if they bound a public or private key. // Here, we do know. So throw if we can't do what they asked. throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); } SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(keyPair.PrivateKey); // If we're using a key already in a keychain don't add the certificate to that keychain here, // do it in the temporary add/remove in the shim. SafeKeychainHandle cloneKeychain = SafeTemporaryKeychainHandle.InvalidHandle; if (keychain.IsInvalid) { keychain = Interop.AppleCrypto.CreateTemporaryKeychain(); cloneKeychain = keychain; } // Because SecIdentityRef only has private constructors we need to have the cert and the key // in the same keychain. That almost certainly means we're going to need to add this cert to a // keychain, and when a cert that isn't part of a keychain gets added to a keychain then the // interior pointer of "what keychain did I come from?" used by SecKeychainItemCopyKeychain gets // set. That makes this function have side effects, which is not desired. // // It also makes reference tracking on temporary keychains broken, since the cert can // DangerousRelease a handle it didn't DangerousAddRef on. And so CopyWithPrivateKey makes // a temporary keychain, then deletes it before anyone has a chance to (e.g.) export the // new identity as a PKCS#12 blob. // // Solution: Clone the cert, like we do in Windows. SafeSecCertificateHandle tempHandle; { byte[] export = RawData; const bool exportable = false; SafeSecIdentityHandle identityHandle; tempHandle = Interop.AppleCrypto.X509ImportCertificate( export, X509ContentType.Cert, SafePasswordHandle.InvalidHandle, cloneKeychain, exportable, out identityHandle); Debug.Assert(identityHandle.IsInvalid, "identityHandle should be IsInvalid"); identityHandle.Dispose(); Debug.Assert(!tempHandle.IsInvalid, "tempHandle should not be IsInvalid"); } using (keychain) using (tempHandle) { SafeSecIdentityHandle identityHandle = Interop.AppleCrypto.X509CopyWithPrivateKey( tempHandle, keyPair.PrivateKey, keychain); AppleCertificatePal newPal = new AppleCertificatePal(identityHandle); return(newPal); } }
public static ICertificatePal FromBlob( ReadOnlySpan <byte> rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(password != null); X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); if (contentType == X509ContentType.Pkcs7) { // In single mode for a PKCS#7 signed or signed-and-enveloped file we're supposed to return // the certificate which signed the PKCS#7 file. // // X509Certificate2Collection::Export(X509ContentType.Pkcs7) claims to be a signed PKCS#7, // but doesn't emit a signature block. So this is hard to test. // // TODO(2910): Figure out how to extract the signing certificate, when it's present. throw new CryptographicException(SR.Cryptography_X509_PKCS7_NoSigner); } if (contentType == X509ContentType.Pkcs12) { if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) { throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx); } bool exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable; bool persist = (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet; SafeKeychainHandle keychain = persist ? Interop.AppleCrypto.SecKeychainCopyDefault() : Interop.AppleCrypto.CreateTemporaryKeychain(); using (keychain) { AppleCertificatePal ret = ImportPkcs12(rawData, password, exportable, keychain); if (!persist) { // If we used temporary keychain we need to prevent deletion. // on 10.15+ if keychain is unlinked, certain certificate operations may fail. bool success = false; keychain.DangerousAddRef(ref success); if (success) { ret._tempKeychain = keychain; } } return(ret); } } SafeSecIdentityHandle identityHandle; SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( rawData, contentType, SafePasswordHandle.InvalidHandle, SafeTemporaryKeychainHandle.InvalidHandle, exportable: true, out identityHandle); if (identityHandle.IsInvalid) { identityHandle.Dispose(); return(new AppleCertificatePal(certHandle)); } Debug.Fail("Non-PKCS12 import produced an identity handle"); identityHandle.Dispose(); certHandle.Dispose(); throw new CryptographicException(); }
internal static ICertificatePal?FromHandle(IntPtr handle, bool throwOnFail) { return(AppleCertificatePal.FromHandle(handle, throwOnFail)); }
public static ICertificatePal?FromOtherCert(X509Certificate cert) { return(AppleCertificatePal.FromOtherCert(cert)); }
internal TempExportPal(AppleCertificatePal realPal) { _realPal = realPal; }
protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan <char> password) { return(AppleCertificatePal.ExportPkcs8(_privateKey, password)); }
public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { return(AppleCertificatePal.FromFile(fileName, password, keyStorageFlags)); }
public static ILoaderPal FromBlob(ReadOnlySpan <byte> rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { List <ICertificatePal>?certificateList = null; AppleCertificatePal.TryDecodePem( rawData, (derData, contentType) => { certificateList = certificateList ?? new List <ICertificatePal>(); certificateList.Add(AppleCertificatePal.FromDerBlob(derData, contentType, password, keyStorageFlags)); return(true); }); if (certificateList != null) { return(new CertCollectionLoader(certificateList)); } X509ContentType contentType = AppleCertificatePal.GetDerCertContentType(rawData); if (contentType == X509ContentType.Pkcs7) { throw new CryptographicException( SR.Cryptography_X509_PKCS7_Unsupported, new PlatformNotSupportedException(SR.Cryptography_X509_PKCS7_Unsupported)); } if (contentType == X509ContentType.Pkcs12) { ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData); try { reader.Decrypt(password); return(new ApplePkcs12CertLoader(reader, password)); } catch { reader.Dispose(); throw; } } SafeCFArrayHandle certs = Interop.AppleCrypto.X509ImportCollection( rawData, contentType, password); using (certs) { long longCount = Interop.CoreFoundation.CFArrayGetCount(certs); if (longCount > int.MaxValue) { throw new CryptographicException(); } int count = (int)longCount; // Apple returns things in the opposite order from Windows, so read backwards. certificateList = new List <ICertificatePal>(count); for (int i = count - 1; i >= 0; i--) { IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(certs, i); if (handle != IntPtr.Zero) { ICertificatePal?certPal = CertificatePal.FromHandle(handle, throwOnFail: false); if (certPal != null) { certificateList.Add(certPal); } } } } return(new CertCollectionLoader(certificateList)); }
protected override byte[] GetSubjectPublicKeyInfo(X509Certificate2 cert) { AppleCertificatePal pal = (AppleCertificatePal)cert.Pal; return(pal.SubjectPublicKeyInfo); }
public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, ICertificatePal certificatePal) { const int errSecInvalidKeyRef = -67712; const int errSecUnsupportedKeySize = -67735; AppleCertificatePal applePal = certificatePal as AppleCertificatePal; if (applePal != null) { SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); switch (oid.Value) { case Oids.Rsa: Debug.Assert(!key.IsInvalid); return(new RSAImplementation.RSASecurityTransforms(key)); case Oids.Dsa: if (key.IsInvalid) { // SecCertificateCopyKey returns null for DSA, so fall back to manually building it. return(DecodeDsaPublicKey(encodedKeyValue, encodedParameters)); } return(new DSAImplementation.DSASecurityTransforms(key)); case Oids.EcPublicKey: // If X509GetPublicKey uses the new SecCertificateCopyKey API it can return an invalid // key reference for unsupported algorithms. This currently happens for the BrainpoolP160r1 // algorithm in the test suite (as of macOS Mojave Developer Preview 4). if (key.IsInvalid) { throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecInvalidKeyRef); } // EccGetKeySizeInBits can fail for two reasons. First, the Apple implementation has changed // and we receive values from API that were not previously handled. In that case the CoreFX // implementation will need to be adjusted to handle these values. Second, we deliberately // return 0 from the native code to prevent hitting buggy API implementations in Apple code // later. if (Interop.AppleCrypto.EccGetKeySizeInBits(key) == 0) { key.Dispose(); throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnsupportedKeySize); } return(new ECDsaImplementation.ECDsaSecurityTransforms(key)); } key.Dispose(); } else { switch (oid.Value) { case Oids.Rsa: return(DecodeRsaPublicKey(encodedKeyValue)); case Oids.Dsa: return(DecodeDsaPublicKey(encodedKeyValue, encodedParameters)); } } throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); }