예제 #1
0
            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);
            }
예제 #2
0
        internal static AppleCertificatePal 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));
        }
예제 #3
0
 public void MoveTo(X509Certificate2Collection collection)
 {
     foreach (UnixPkcs12Reader.CertAndKey certAndKey in _pkcs12.EnumerateAll())
     {
         collection.Add(new X509Certificate2(AppleCertificatePal.ImportPkcs12(certAndKey)));
     }
 }
예제 #4
0
 internal static partial ICertificatePal FromBlob(
     ReadOnlySpan <byte> rawData,
     SafePasswordHandle password,
     X509KeyStorageFlags keyStorageFlags)
 {
     return(AppleCertificatePal.FromBlob(rawData, password, keyStorageFlags));
 }
예제 #5
0
 internal static partial ICertificatePal FromFile(
     string fileName,
     SafePasswordHandle password,
     X509KeyStorageFlags keyStorageFlags)
 {
     return(AppleCertificatePal.FromFile(fileName, password, keyStorageFlags));
 }
        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);
        }
예제 #7
0
            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);
                    }
                }
            }
예제 #8
0
            public void MoveTo(X509Certificate2Collection collection)
            {
                long longCount = Interop.CoreFoundation.CFArrayGetCount(_collectionHandle);

                if (longCount > int.MaxValue)
                {
                    throw new CryptographicException();
                }

                int count = (int)longCount;

                // Apple returns things in the opposite order from Windows, so read backwards.
                for (int i = count - 1; i >= 0; i--)
                {
                    IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(_collectionHandle, i);

                    if (handle != IntPtr.Zero)
                    {
                        ICertificatePal?certPal = AppleCertificatePal.FromHandle(handle, throwOnFail: false);

                        if (certPal != null)
                        {
                            X509Certificate2 cert = new X509Certificate2(certPal);
                            collection.Add(cert);
                        }
                    }
                }
            }
예제 #9
0
            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);
            }
예제 #10
0
            public void Remove(ICertificatePal cert)
            {
                AppleCertificatePal applePal = (AppleCertificatePal)cert;

                var handle = (SafeKeychainItemHandle?)applePal.IdentityHandle ?? applePal.CertificateHandle;

                Interop.AppleCrypto.X509StoreRemoveCertificate(handle, _keychainHandle, _readonly);
            }
예제 #11
0
            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);
            }
예제 #12
0
        private static AppleCertificatePal ImportPkcs12(
            ReadOnlySpan <byte> rawData,
            SafePasswordHandle password,
            bool exportable,
            SafeKeychainHandle keychain)
        {
            using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData))
            {
                reader.Decrypt(password, ephemeralSpecified: false);

                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);
            }
        }
예제 #13
0
 protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan <char> password)
 {
     return(AppleCertificatePal.ExportPkcs8(_privateKey, password));
 }
예제 #14
0
 internal static partial ICertificatePal FromHandle(IntPtr handle)
 {
     return(AppleCertificatePal.FromHandle(handle, true) !);
 }
예제 #15
0
        internal static partial 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));
            }

            bool            ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet);
            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, ephemeralSpecified);
                    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 = AppleCertificatePal.FromHandle(handle, throwOnFail: false);

                        if (certPal != null)
                        {
                            certificateList.Add(certPal);
                        }
                    }
                }
            }

            return(new CertCollectionLoader(certificateList));
        }
 internal TempExportPal(AppleCertificatePal realPal)
 {
     _realPal = realPal;
 }
예제 #17
0
 internal static partial ICertificatePal FromOtherCert(X509Certificate copyFrom)
 {
     return(AppleCertificatePal.FromOtherCert(copyFrom));
 }
예제 #18
0
        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();
        }
        private ICertificatePal CopyWithPrivateKey(SafeSecKeyRefHandle?privateKey)
        {
            if (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(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,
                        privateKey,
                        keychain);

                    AppleCertificatePal newPal = new AppleCertificatePal(identityHandle);
                    return(newPal);
                }
        }