using GostCryptography.Base; using GostCryptography.Config; using GostCryptography.Gost_28147_89; using GostCryptography.Properties; using GostCryptography.Reflection; using System; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.Security.Policy; using System.Xml; namespace GostCryptography.Xml { sealed class GostEncryptedXmlImpl : EncryptedXml { public GostEncryptedXmlImpl(ProviderType providerType) { ProviderType = providerType; } public GostEncryptedXmlImpl(ProviderType providerType, XmlDocument document) : base(document) { ProviderType = providerType; } public GostEncryptedXmlImpl(ProviderType providerType, XmlDocument document, Evidence evidence) : base(document, evidence) { ProviderType = providerType; } public ProviderType ProviderType { get; } public new void AddKeyNameMapping(string keyName, object keyObject) { if (string.IsNullOrEmpty(keyName)) { throw ExceptionUtility.ArgumentNull(nameof(keyName)); } if (keyObject == null) { throw ExceptionUtility.ArgumentNull(nameof(keyObject)); } if (keyObject is GostAsymmetricAlgorithm) { this.GetKeyNameMapping().Add(keyName, keyObject); } else { base.AddKeyNameMapping(keyName, keyObject); } } [SecuritySafeCritical] public new EncryptedData Encrypt(XmlElement element, X509Certificate2 certificate) { if (element == null || certificate == null || !certificate.IsGost()) { return base.Encrypt(element, certificate); } var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm(); var encryptionKey = new Gost_28147_89_SymmetricAlgorithm(publicKey.ProviderType); var encryptedKey = new EncryptedKey(); encryptedKey.KeyInfo.AddClause(new KeyInfoX509Data(certificate)); encryptedKey.EncryptionMethod = new EncryptionMethod(publicKey.KeyExchangeAlgorithm); encryptedKey.CipherData.CipherValue = EncryptKey(encryptionKey, publicKey); var encryptedData = new EncryptedData { Type = XmlEncElementUrl, EncryptionMethod = new EncryptionMethod(encryptionKey.AlgorithmName) }; encryptedData.KeyInfo.AddClause(new KeyInfoEncryptedKey(encryptedKey)); encryptedData.CipherData.CipherValue = EncryptData(element, encryptionKey, false); return encryptedData; } public static byte[] EncryptKey(Gost_28147_89_SymmetricAlgorithmBase sessionKey, GostAsymmetricAlgorithm publicKey) { if (sessionKey == null) { throw ExceptionUtility.ArgumentNull(nameof(sessionKey)); } if (publicKey == null) { throw ExceptionUtility.ArgumentNull(nameof(publicKey)); } var formatter = publicKey.CreateKeyExchangeFormatter(); return formatter.CreateKeyExchangeData(sessionKey); } public static byte[] EncryptKey(Gost_28147_89_SymmetricAlgorithmBase sessionKey, Gost_28147_89_SymmetricAlgorithmBase sharedKey, GostKeyExchangeExportMethod exportMethod) { if (sessionKey == null) { throw ExceptionUtility.ArgumentNull(nameof(sessionKey)); } if (sharedKey == null) { throw ExceptionUtility.ArgumentNull(nameof(sharedKey)); } return sharedKey.EncodePrivateKey(sessionKey, exportMethod); } public override byte[] GetDecryptionIV(EncryptedData encryptedData, string symmetricAlgorithmUri) { if (encryptedData == null) { throw ExceptionUtility.ArgumentNull(nameof(encryptedData)); } if (symmetricAlgorithmUri == null) { if (encryptedData.EncryptionMethod == null) { return base.GetDecryptionIV(encryptedData, null); } symmetricAlgorithmUri = encryptedData.EncryptionMethod.KeyAlgorithm; } if (Gost_28147_89_SymmetricAlgorithm.AlgorithmNameValue.Equals(symmetricAlgorithmUri, StringComparison.OrdinalIgnoreCase)) { var iv = new byte[8]; Buffer.BlockCopy(this.GetCipherValue(encryptedData.CipherData), 0, iv, 0, iv.Length); return iv; } return base.GetDecryptionIV(encryptedData, symmetricAlgorithmUri); } public override SymmetricAlgorithm GetDecryptionKey(EncryptedData encryptedData, string symmetricAlgorithmUri) { if (encryptedData == null) { throw ExceptionUtility.ArgumentNull(nameof(encryptedData)); } SymmetricAlgorithm decryptionKey = null; if (encryptedData.KeyInfo != null) { EncryptedKey encryptedKey = null; foreach (var keyInfo in encryptedData.KeyInfo) { // Извлечение ключа по имени if (keyInfo is KeyInfoName) { var keyName = ((KeyInfoName)keyInfo).Value; var keyAlgorithm = this.GetKeyNameMapping()[keyName]; if (keyAlgorithm == null) { var nsManager = new XmlNamespaceManager(this.GetDocument().NameTable); nsManager.AddNamespace("enc", XmlEncNamespaceUrl); var encryptedKeyNodes = this.GetDocument().SelectNodes("//enc:EncryptedKey", nsManager); if (encryptedKeyNodes != null) { foreach (XmlElement encryptedKeyNode in encryptedKeyNodes) { var currentEncryptedKey = new EncryptedKey(); currentEncryptedKey.LoadXml(encryptedKeyNode); if ((currentEncryptedKey.CarriedKeyName == keyName) && (currentEncryptedKey.Recipient == Recipient)) { encryptedKey = currentEncryptedKey; break; } } } } else { decryptionKey = (SymmetricAlgorithm)keyAlgorithm; } break; } // Извлечение ключа по ссылке if (keyInfo is KeyInfoRetrievalMethod) { var idValue = CryptographyXmlUtils.ExtractIdFromLocalUri(((KeyInfoRetrievalMethod)keyInfo).Uri); var idElement = GetIdElement(this.GetDocument(), idValue); if (idElement != null) { encryptedKey = new EncryptedKey(); encryptedKey.LoadXml(idElement); } break; } // Ключ в готовом виде if (keyInfo is KeyInfoEncryptedKey) { encryptedKey = ((KeyInfoEncryptedKey)keyInfo).EncryptedKey; break; } } if (decryptionKey == null && encryptedKey != null) { if (symmetricAlgorithmUri == null) { if (encryptedData.EncryptionMethod == null) { throw ExceptionUtility.CryptographicException(Resources.XmlMissingAlgorithm); } symmetricAlgorithmUri = encryptedData.EncryptionMethod.KeyAlgorithm; } decryptionKey = DecryptEncryptedKeyClass(encryptedKey, symmetricAlgorithmUri); } } return decryptionKey; } [SecuritySafeCritical] private SymmetricAlgorithm DecryptEncryptedKeyClass(EncryptedKey encryptedKey, string symmetricAlgorithmUri) { if (encryptedKey == null) { throw ExceptionUtility.ArgumentNull(nameof(encryptedKey)); } SymmetricAlgorithm decryptionKey = null; if (encryptedKey.KeyInfo != null) { foreach (var keyInfo in encryptedKey.KeyInfo) { // Извлечение ключа по имени if (keyInfo is KeyInfoName) { var keyName = ((KeyInfoName)keyInfo).Value; var keyAlgorithm = this.GetKeyNameMapping()[keyName]; if (keyAlgorithm != null) { if (keyAlgorithm is SymmetricAlgorithm) { decryptionKey = DecryptKeyClass(encryptedKey.CipherData.CipherValue, (SymmetricAlgorithm)keyAlgorithm, symmetricAlgorithmUri, encryptedKey.EncryptionMethod.KeyAlgorithm); } else if (keyAlgorithm is RSA) { var useOaep = (encryptedKey.EncryptionMethod != null) && (encryptedKey.EncryptionMethod.KeyAlgorithm == XmlEncRSAOAEPUrl); decryptionKey = DecryptKeyClass(encryptedKey.CipherData.CipherValue, (RSA)keyAlgorithm, useOaep, symmetricAlgorithmUri); } else if (keyAlgorithm is GostAsymmetricAlgorithm) { decryptionKey = DecryptKeyClass(encryptedKey.CipherData.CipherValue, (GostAsymmetricAlgorithm)keyAlgorithm); } } break; } // Извлечение ключа из сертификата if (keyInfo is KeyInfoX509Data) { var certificates = CryptographyXmlUtils.BuildBagOfCertsDecryption((KeyInfoX509Data)keyInfo); foreach (var certificate in certificates) { var privateKey = certificate.GetPrivateKeyAlgorithm(); if (privateKey is RSA) { var useOaep = (encryptedKey.EncryptionMethod != null) && (encryptedKey.EncryptionMethod.KeyAlgorithm == XmlEncRSAOAEPUrl); decryptionKey = DecryptKeyClass(encryptedKey.CipherData.CipherValue, (RSA)privateKey, useOaep, symmetricAlgorithmUri); } else if (privateKey is GostAsymmetricAlgorithm) { decryptionKey = DecryptKeyClass(encryptedKey.CipherData.CipherValue, (GostAsymmetricAlgorithm)privateKey); } } break; } // Извлечение ключа по ссылке if (keyInfo is KeyInfoRetrievalMethod) { var idValue = CryptographyXmlUtils.ExtractIdFromLocalUri(((KeyInfoRetrievalMethod)keyInfo).Uri); var idElement = GetIdElement(this.GetDocument(), idValue); if (idElement != null) { var secondEncryptedKey = new EncryptedKey(); secondEncryptedKey.LoadXml(idElement); decryptionKey = DecryptEncryptedKeyClass(secondEncryptedKey, symmetricAlgorithmUri); } break; } // Ключ в готовом виде if (keyInfo is KeyInfoEncryptedKey) { var secondEncryptedKey = ((KeyInfoEncryptedKey)keyInfo).EncryptedKey; var symmetricAlgorithm = DecryptEncryptedKeyClass(secondEncryptedKey, symmetricAlgorithmUri); if (symmetricAlgorithm != null) { decryptionKey = DecryptKeyClass(encryptedKey.CipherData.CipherValue, symmetricAlgorithm, symmetricAlgorithmUri, encryptedKey.EncryptionMethod.KeyAlgorithm); } break; } } } return decryptionKey; } private static SymmetricAlgorithm DecryptKeyClass(byte[] keyData, SymmetricAlgorithm algorithm, string symmetricAlgorithmUri, string encryptionKeyAlgorithm) { if (keyData == null) { throw ExceptionUtility.ArgumentNull(nameof(keyData)); } if (algorithm == null) { throw ExceptionUtility.ArgumentNull(nameof(algorithm)); } SymmetricAlgorithm decryptionKey = null; if (algorithm is Gost_28147_89_SymmetricAlgorithmBase gost28147) { if (string.Equals(encryptionKeyAlgorithm, GostEncryptedXml.XmlEncGostKeyExportUrl, StringComparison.OrdinalIgnoreCase)) { decryptionKey = gost28147.DecodePrivateKey(keyData, GostKeyExchangeExportMethod.GostKeyExport); } if (string.Equals(encryptionKeyAlgorithm, GostEncryptedXml.XmlEncGostCryptoProKeyExportUrl, StringComparison.OrdinalIgnoreCase)) { decryptionKey = gost28147.DecodePrivateKey(keyData, GostKeyExchangeExportMethod.CryptoProKeyExport); } } else { var decryptionKeyBytes = DecryptKey(keyData, algorithm); if (decryptionKeyBytes != null) { decryptionKey = (SymmetricAlgorithm)GostCryptoConfig.CreateFromName(symmetricAlgorithmUri); decryptionKey.Key = decryptionKeyBytes; } } if (decryptionKey == null) { throw ExceptionUtility.CryptographicException(Resources.XmlMissingAlgorithm); } return decryptionKey; } private static SymmetricAlgorithm DecryptKeyClass(byte[] keyData, RSA algorithm, bool useOaep, string symmetricAlgorithmUri) { if (keyData == null) { throw ExceptionUtility.ArgumentNull(nameof(keyData)); } if (algorithm == null) { throw ExceptionUtility.ArgumentNull(nameof(algorithm)); } SymmetricAlgorithm decryptionKey = null; var decryptionKeyBytes = DecryptKey(keyData, algorithm, useOaep); if (decryptionKeyBytes != null) { decryptionKey = (SymmetricAlgorithm)GostCryptoConfig.CreateFromName(symmetricAlgorithmUri); decryptionKey.Key = decryptionKeyBytes; } if (decryptionKey == null) { throw ExceptionUtility.CryptographicException(Resources.XmlMissingAlgorithm); } return decryptionKey; } public static SymmetricAlgorithm DecryptKeyClass(byte[] keyData, GostAsymmetricAlgorithm privateKey) { if (keyData == null) { throw ExceptionUtility.ArgumentNull(nameof(keyData)); } if (privateKey == null) { throw ExceptionUtility.ArgumentNull(nameof(privateKey)); } var deformatter = privateKey.CreateKeyExchangeDeformatter(); var decryptionKey = deformatter.DecryptKeyExchangeAlgorithm(keyData); return decryptionKey; } } }