public static bool ToSecretariumKey(this X509Certificate2 x509, string password, out ScpConfig.SecretariumKeyConfig config)
        {
            config = null;

            var publicKey = x509.GetECDsaPublicKey() as ECDsaCng;

            if (publicKey == null)
            {
                return(false);
            }
            if (publicKey.HashAlgorithm != CngAlgorithm.Sha256 || publicKey.KeySize != 256 || publicKey.Key.Algorithm != CngAlgorithm.ECDsaP256)
            {
                return(false);
            }
            var publicKeyRaw = publicKey.ExportPublicKeyRaw();

            if (publicKeyRaw == null)
            {
                return(false);
            }

            var privateKey = x509.GetECDsaPrivateKey() as ECDsaCng;

            if (privateKey == null)
            {
                return(false);
            }
            var privatKeyRaw = privateKey.ExportPrivateKeyRaw();

            if (privatKeyRaw == null)
            {
                return(false);
            }

            // Built pkcs8 manually because privateKey.Key.ForceExport(CngKeyBlobFormat.Pkcs8PrivateBlob) return 165 bytes instead of 138. TODO investigate
            var privatKeyPkcs8 = ByteHelper.Combine(
                new byte[] { 48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32 },
                privatKeyRaw,
                new byte[] { 161, 68, 3, 66, 0, 4 },
                publicKeyRaw
                );

            var salt          = ByteHelper.GetRandom(32);
            var iv            = ByteHelper.GetRandom(12);
            var strongPwd     = ByteHelper.Combine(salt, password.ToBytes()).HashSha256();
            var keys          = ByteHelper.Combine(new byte[] { 4 }, publicKeyRaw, privatKeyPkcs8);
            var encryptedKeys = AESGCMHelper.AesGcmEncrypt(keys, strongPwd, iv);

            config = new ScpConfig.SecretariumKeyConfig
            {
                iv   = iv.ToBase64String(false),
                salt = salt.ToBase64String(false),
                keys = encryptedKeys.ToBase64String(false)
            };

            return(true);
        }
Exemple #2
0
        public static unsafe byte[] ForceExport(this CngKey key, CngKeyBlobFormat format)
        {
            if ((key.ExportPolicy & CngExportPolicies.AllowPlaintextExport) != 0)
            {
                return(key.Export(format));
            }

            // The key is not exportable, lets hack. Thanks @bartonjs
            // https://stackoverflow.com/questions/57269726/x509certificate2-import-with-ncrypt-allow-plaintext-export-flag
            // https://stackoverflow.com/questions/55236230/export-private-key-pkcs8-of-cng-rsa-certificate-with-oldschool-net

            string blobType = "PKCS8_PRIVATEKEY";

            try
            {
                byte[] exported;

                fixed(byte *oidPtr = CryptNativeHelpers.PKCS12_3DES_OID)
                {
                    var salt      = ByteHelper.GetRandom(CryptNativeHelpers.NCrypt.PbeParams.RgbSaltSize);
                    var pbeParams = new CryptNativeHelpers.NCrypt.PbeParams();

                    pbeParams.Params.iIterations = 1;
                    pbeParams.Params.cbSalt      = salt.Length;
                    Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length);

                    var buffers = stackalloc CryptNativeHelpers.NCrypt.NCryptBuffer[3];

                    buffers[0] = new CryptNativeHelpers.NCrypt.NCryptBuffer
                    {
                        BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsSecret,
                        cbBuffer   = 0,
                        pvBuffer   = IntPtr.Zero,
                    };
                    buffers[1] = new CryptNativeHelpers.NCrypt.NCryptBuffer
                    {
                        BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsAlgOid,
                        cbBuffer   = CryptNativeHelpers.PKCS12_3DES_OID.Length,
                        pvBuffer   = (IntPtr)oidPtr,
                    };
                    buffers[2] = new CryptNativeHelpers.NCrypt.NCryptBuffer
                    {
                        BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsAlgParam,
                        cbBuffer   = sizeof(CryptNativeHelpers.NCrypt.PbeParams),
                        pvBuffer   = (IntPtr)(&pbeParams),
                    };
                    var desc = new CryptNativeHelpers.NCrypt.NCryptBufferDesc
                    {
                        cBuffers  = 3,
                        pBuffers  = (IntPtr)buffers,
                        ulVersion = 0,
                    };

                    if (CryptNativeHelpers.NCrypt.NCryptExportKey(key.Handle, IntPtr.Zero, blobType, ref desc, null, 0, out int bytesNeeded, 0) != 0)
                    {
                        return(null);
                    }

                    exported = new byte[bytesNeeded];
                    if (CryptNativeHelpers.NCrypt.NCryptExportKey(key.Handle, IntPtr.Zero, blobType, ref desc, exported, exported.Length, out bytesNeeded, 0) != 0)
                    {
                        return(null);
                    }
                }

                fixed(char *keyNamePtr = key.KeyName)
                fixed(byte *blobPtr = exported)
                {
                    var buffers = stackalloc CryptNativeHelpers.NCrypt.NCryptBuffer[2];

                    buffers[0] = new CryptNativeHelpers.NCrypt.NCryptBuffer
                    {
                        BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsSecret,
                        cbBuffer   = 0,
                        pvBuffer   = IntPtr.Zero,
                    };
                    buffers[1] = new CryptNativeHelpers.NCrypt.NCryptBuffer
                    {
                        BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsName,
                        cbBuffer   = checked (2 * (key.KeyName.Length + 1)),
                        pvBuffer   = new IntPtr(keyNamePtr),
                    };
                    var desc = new CryptNativeHelpers.NCrypt.NCryptBufferDesc
                    {
                        cBuffers  = 2,
                        pBuffers  = (IntPtr)buffers,
                        ulVersion = 0,
                    };

                    SafeNCryptKeyHandle keyHandle;

                    if (CryptNativeHelpers.NCrypt.NCryptImportKey(key.ProviderHandle, IntPtr.Zero, blobType, ref desc, out keyHandle, new IntPtr(blobPtr), exported.Length,
                                                                  CryptNativeHelpers.NCrypt.NCryptImportFlags.NCRYPT_OVERWRITE_KEY_FLAG | CryptNativeHelpers.NCrypt.NCryptImportFlags.NCRYPT_DO_NOT_FINALIZE_FLAG) != 0)
                    {
                        keyHandle.Dispose();
                        return(null);
                    }

                    using (keyHandle)
                        using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None))
                        {
                            cngKey.SetProperty(new CngProperty("Export Policy", BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport), CngPropertyOptions.Persist));

                            if (CryptNativeHelpers.NCrypt.NCryptFinalizeKey(keyHandle, 0) != 0)
                            {
                                return(null);
                            }

                            return(cngKey.Export(format));
                        }
                }
            }
            catch (Exception)
            {
                return(null);
            }
        }