/// <summary> /// Return an encrypted base64 encoded message along with encryption related metadata given a plain text message. /// </summary> /// <param name="inputMessage">The input message in bytes.</param> /// <returns>The encrypted message that will be uploaded to the service.</returns> internal string EncryptMessage(byte[] inputMessage) { CommonUtility.AssertNotNull("inputMessage", inputMessage); if (this.Key == null) { throw new InvalidOperationException(SR.KeyMissingError, null); } CloudQueueEncryptedMessage encryptedMessage = new CloudQueueEncryptedMessage(); EncryptionData encryptionData = new EncryptionData(); encryptionData.EncryptionAgent = new EncryptionAgent(Constants.EncryptionConstants.EncryptionProtocolV1, EncryptionAlgorithm.AES_CBC_256); encryptionData.KeyWrappingMetadata = new Dictionary<string, string>(); #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) #endif { encryptionData.ContentEncryptionIV = myAes.IV; // Wrap always happens locally, irrespective of local or cloud key. So it is ok to call it synchronously. Tuple<byte[], string> wrappedKey = CommonUtility.RunWithoutSynchronizationContext(() => this.Key.WrapKeyAsync(myAes.Key, null /* algorithm */, CancellationToken.None).Result); encryptionData.WrappedContentKey = new WrappedKey(this.Key.Kid, wrappedKey.Item1, wrappedKey.Item2); using (ICryptoTransform encryptor = myAes.CreateEncryptor()) { encryptedMessage.EncryptedMessageContents = Convert.ToBase64String(encryptor.TransformFinalBlock(inputMessage, 0, inputMessage.Length)); } encryptedMessage.EncryptionData = encryptionData; return JsonConvert.SerializeObject(encryptedMessage); } }
private static T ReadAndResolveWithEdmTypeResolver <T>(Dictionary <string, string> entityAttributes, Func <string, string, DateTimeOffset, IDictionary <string, EntityProperty>, string, T> resolver, Func <string, string, string, string, EdmType> propertyResolver, string etag, Type type, OperationContext ctx, bool disablePropertyResolverCache, TableRequestOptions options) { string pk = null; string rk = null; byte[] cek = null; EncryptionData encryptionData = null; DateTimeOffset ts = new DateTimeOffset(); Dictionary <string, EntityProperty> properties = new Dictionary <string, EntityProperty>(); Dictionary <string, EdmType> propertyResolverDictionary = null; HashSet <string> encryptedPropertyDetailsSet = null; if (type != null) { #if WINDOWS_DESKTOP && !WINDOWS_PHONE if (!disablePropertyResolverCache) { propertyResolverDictionary = TableEntity.PropertyResolverCache.GetOrAdd(type, TableOperationHttpResponseParsers.CreatePropertyResolverDictionary); } else { propertyResolverDictionary = TableOperationHttpResponseParsers.CreatePropertyResolverDictionary(type); } #else propertyResolverDictionary = TableOperationHttpResponseParsers.CreatePropertyResolverDictionary(type); #endif } // Decrypt the metadata property value to get the names of encrypted properties so that they can be parsed correctly below. if (options.EncryptionPolicy != null) { string metadataValue = null; string keyPropertyValue = null; if (entityAttributes.TryGetValue(Constants.EncryptionConstants.TableEncryptionPropertyDetails, out metadataValue) && entityAttributes.TryGetValue(Constants.EncryptionConstants.TableEncryptionKeyDetails, out keyPropertyValue)) { EntityProperty propertyDetailsProperty = EntityProperty.CreateEntityPropertyFromObject(metadataValue, EdmType.Binary); EntityProperty keyProperty = EntityProperty.CreateEntityPropertyFromObject(keyPropertyValue, EdmType.String); entityAttributes.TryGetValue(TableConstants.PartitionKey, out pk); entityAttributes.TryGetValue(TableConstants.RowKey, out rk); cek = options.EncryptionPolicy.DecryptMetadataAndReturnCEK(pk, rk, keyProperty, propertyDetailsProperty, out encryptionData); properties.Add(Constants.EncryptionConstants.TableEncryptionPropertyDetails, propertyDetailsProperty); byte[] binaryVal = propertyDetailsProperty.BinaryValue; encryptedPropertyDetailsSet = JsonConvert.DeserializeObject <HashSet <string> >(Encoding.UTF8.GetString(binaryVal, 0, binaryVal.Length)); } else { if (options.RequireEncryption.HasValue && options.RequireEncryption.Value) { throw new StorageException(SR.EncryptionDataNotPresentError, null) { IsRetryable = false }; } } } foreach (KeyValuePair <string, string> prop in entityAttributes) { if (prop.Key == TableConstants.PartitionKey) { pk = (string)prop.Value; } else if (prop.Key == TableConstants.RowKey) { rk = (string)prop.Value; } else if (prop.Key == TableConstants.Timestamp) { ts = DateTimeOffset.Parse(prop.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); if (etag == null) { etag = GetETagFromTimestamp(prop.Value); } } else if (prop.Key == Constants.EncryptionConstants.TableEncryptionKeyDetails) { // This and the following check are required because in JSON no-metadata, the type information for the properties are not returned and users are // not expected to provide a type for them. So based on how the user defined property resolvers treat unknown properties, we might get unexpected results. properties.Add(prop.Key, EntityProperty.CreateEntityPropertyFromObject(prop.Value, EdmType.String)); } else if (prop.Key == Constants.EncryptionConstants.TableEncryptionPropertyDetails) { if (!properties.ContainsKey(Constants.EncryptionConstants.TableEncryptionPropertyDetails)) { // If encryption policy is not set, then add the value as-is to the dictionary. properties.Add(prop.Key, EntityProperty.CreateEntityPropertyFromObject(prop.Value, EdmType.Binary)); } else { // Do nothing. Already handled above. } } else { if (propertyResolver != null) { Logger.LogVerbose(ctx, SR.UsingUserProvidedPropertyResolver); try { EdmType edmType = propertyResolver(pk, rk, prop.Key, prop.Value); Logger.LogVerbose(ctx, SR.AttemptedEdmTypeForTheProperty, prop.Key, edmType); try { CreateEntityPropertyFromObject(properties, encryptedPropertyDetailsSet, prop, edmType); } catch (FormatException ex) { throw new StorageException(string.Format(CultureInfo.InvariantCulture, SR.FailParseProperty, prop.Key, prop.Value, edmType), ex) { IsRetryable = false }; } } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.PropertyResolverThrewError, ex) { IsRetryable = false }; } } else if (type != null) { Logger.LogVerbose(ctx, SR.UsingDefaultPropertyResolver); EdmType edmType; if (propertyResolverDictionary != null) { propertyResolverDictionary.TryGetValue(prop.Key, out edmType); Logger.LogVerbose(ctx, SR.AttemptedEdmTypeForTheProperty, prop.Key, edmType); CreateEntityPropertyFromObject(properties, encryptedPropertyDetailsSet, prop, edmType); } } else { Logger.LogVerbose(ctx, SR.NoPropertyResolverAvailable); CreateEntityPropertyFromObject(properties, encryptedPropertyDetailsSet, prop, EdmType.String); } } } // If encryption policy is set on options, try to decrypt the entity. if (options.EncryptionPolicy != null && encryptionData != null) { properties = options.EncryptionPolicy.DecryptEntity(properties, encryptedPropertyDetailsSet, pk, rk, cek, encryptionData); } return(resolver(pk, rk, ts, properties, etag)); }
private static T ReadAndResolve <T>(ODataEntry entry, Func <string, string, DateTimeOffset, IDictionary <string, EntityProperty>, string, T> resolver, TableRequestOptions options) { string pk = null; string rk = null; byte[] cek = null; DateTimeOffset ts = new DateTimeOffset(); Dictionary <string, EntityProperty> properties = new Dictionary <string, EntityProperty>(); foreach (ODataProperty prop in entry.Properties) { string propName = prop.Name; if (propName == TableConstants.PartitionKey) { pk = (string)prop.Value; } else if (propName == TableConstants.RowKey) { rk = (string)prop.Value; } else if (propName == TableConstants.Timestamp) { ts = new DateTimeOffset((DateTime)prop.Value); } else { properties.Add(propName, EntityProperty.CreateEntityPropertyFromObject(prop.Value)); } } // If encryption policy is set on options, try to decrypt the entity. EntityProperty propertyDetailsProperty; EntityProperty keyProperty; if (options.EncryptionPolicy != null) { if (properties.TryGetValue(Constants.EncryptionConstants.TableEncryptionPropertyDetails, out propertyDetailsProperty) && properties.TryGetValue(Constants.EncryptionConstants.TableEncryptionKeyDetails, out keyProperty)) { // Decrypt the metadata property value to get the names of encrypted properties. EncryptionData encryptionData = null; cek = options.EncryptionPolicy.DecryptMetadataAndReturnCEK(pk, rk, keyProperty, propertyDetailsProperty, out encryptionData); byte[] binaryVal = propertyDetailsProperty.BinaryValue; HashSet <string> encryptedPropertyDetailsSet = JsonConvert.DeserializeObject <HashSet <string> >(Encoding.UTF8.GetString(binaryVal, 0, binaryVal.Length)); properties = options.EncryptionPolicy.DecryptEntity(properties, encryptedPropertyDetailsSet, pk, rk, cek, encryptionData); } else { if (options.RequireEncryption.HasValue && options.RequireEncryption.Value) { throw new StorageException(SR.EncryptionDataNotPresentError, null) { IsRetryable = false }; } } } return(resolver(pk, rk, ts, properties, entry.ETag)); }
/// <summary> /// Returns a plain text message given an encrypted message. /// </summary> /// <param name="inputMessage">The encrypted message.</param> /// <param name="requireEncryption">A value to indicate that the data read from the server should be encrypted.</param> /// <returns>The plain text message bytes.</returns> internal byte[] DecryptMessage(string inputMessage, bool?requireEncryption) { CommonUtility.AssertNotNull("inputMessage", inputMessage); try { CloudQueueEncryptedMessage encryptedMessage = JsonConvert.DeserializeObject <CloudQueueEncryptedMessage>(inputMessage); if (requireEncryption.HasValue && requireEncryption.Value && encryptedMessage.EncryptionData == null) { throw new StorageException(SR.EncryptionDataNotPresentError, null) { IsRetryable = false }; } if (encryptedMessage.EncryptionData != null) { EncryptionData encryptionData = encryptedMessage.EncryptionData; CommonUtility.AssertNotNull("ContentEncryptionIV", encryptionData.ContentEncryptionIV); CommonUtility.AssertNotNull("EncryptedKey", encryptionData.WrappedContentKey.EncryptedKey); // Throw if the encryption protocol on the message doesn't match the version that this client library understands // and is able to decrypt. if (encryptionData.EncryptionAgent.Protocol != Constants.EncryptionConstants.EncryptionProtocolV1) { throw new StorageException(SR.EncryptionProtocolVersionInvalid, null) { IsRetryable = false }; } // Throw if neither the key nor the key resolver are set. if (this.Key == null && this.KeyResolver == null) { throw new StorageException(SR.KeyAndResolverMissingError, null) { IsRetryable = false }; } byte[] contentEncryptionKey = null; // 1. Invoke the key resolver if specified to get the key. If the resolver is specified but does not have a // mapping for the key id, an error should be thrown. This is important for key rotation scenario. // 2. If resolver is not specified but a key is specified, match the key id on the key and and use it. // Calling UnwrapKeyAsync synchronously is fine because for the storage client scenario, unwrap happens // locally. No service call is made. if (this.KeyResolver != null) { IKey keyEncryptionKey = CommonUtility.RunWithoutSynchronizationContext(() => this.KeyResolver.ResolveKeyAsync(encryptionData.WrappedContentKey.KeyId, CancellationToken.None).GetAwaiter().GetResult()); CommonUtility.AssertNotNull("keyEncryptionKey", keyEncryptionKey); contentEncryptionKey = CommonUtility.RunWithoutSynchronizationContext(() => keyEncryptionKey.UnwrapKeyAsync(encryptionData.WrappedContentKey.EncryptedKey, encryptionData.WrappedContentKey.Algorithm, CancellationToken.None).GetAwaiter().GetResult()); } else { if (this.Key.Kid == encryptionData.WrappedContentKey.KeyId) { contentEncryptionKey = CommonUtility.RunWithoutSynchronizationContext(() => this.Key.UnwrapKeyAsync(encryptionData.WrappedContentKey.EncryptedKey, encryptionData.WrappedContentKey.Algorithm, CancellationToken.None).GetAwaiter().GetResult()); } else { throw new StorageException(SR.KeyMismatch, null) { IsRetryable = false }; } } switch (encryptionData.EncryptionAgent.EncryptionAlgorithm) { case EncryptionAlgorithm.AES_CBC_256: #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) #endif { myAes.Key = contentEncryptionKey; myAes.IV = encryptionData.ContentEncryptionIV; byte[] src = Convert.FromBase64String(encryptedMessage.EncryptedMessageContents); using (ICryptoTransform decryptor = myAes.CreateDecryptor()) { return(decryptor.TransformFinalBlock(src, 0, src.Length)); } } default: throw new StorageException(SR.InvalidEncryptionAlgorithm, null) { IsRetryable = false }; } } else { return(Convert.FromBase64String(encryptedMessage.EncryptedMessageContents)); } } catch (JsonException ex) { throw new StorageException(SR.EncryptedMessageDeserializingError, ex) { IsRetryable = false }; } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.DecryptionLogicError, ex) { IsRetryable = false }; } }
private static bool AreEqual(EncryptionData left, EncryptionData right) => left.EncryptionMode.Equals(right.EncryptionMode, StringComparison.InvariantCulture) && AreEqual(left.WrappedContentKey, right.WrappedContentKey) && AreEqual(left.EncryptionAgent, right.EncryptionAgent) && left.ContentEncryptionIV.SequenceEqual(right.ContentEncryptionIV) && AreEqual(left.KeyWrappingMetadata, right.KeyWrappingMetadata);
private static ErrorCode SetupEncryption(AuthenticationToken token, out string errorMsg, ClientPeer peer, InitRequest initRequest) { if (log.IsDebugEnabled) { log.DebugFormat("setting up encryption. p:{0}", peer); } var encryptionDataDict = token.EncryptionData; errorMsg = string.Empty; if (encryptionDataDict == null) { if (log.IsWarnEnabled) { log.WarnFormat(logSetupCountGuard, "AuthOnInitHandler: expected encryption data not provided. appId:{0}/{1}, p:{2}", token.ApplicationId, token.ApplicationVersion, peer); } errorMsg = string.Format(ErrorMessages.InvalidEncryptionData, "expected encryption data not provided"); return(ErrorCode.InvalidEncryptionParameters); } var encryptionData = new EncryptionData(peer.Protocol, encryptionDataDict); if (!encryptionData.IsValid) { if (log.IsWarnEnabled) { log.WarnFormat(logSetupCountGuard, "AuthOnInitHandler: Invalid encryption data. ErrorMsg:{4}. appId:{0}/{1}, data:{2}, p:{3}", token.ApplicationId, token.ApplicationVersion, JsonConvert.SerializeObject(encryptionDataDict), peer, encryptionData.GetErrorMessage()); } errorMsg = string.Format(ErrorMessages.InvalidEncryptionData, encryptionData.GetErrorMessage()); return(ErrorCode.InvalidEncryptionParameters); } var mode = (EncryptionModes)encryptionData.EncryptionMode; try { switch (mode) { case EncryptionModes.PayloadEncryption: case EncryptionModes.PayloadEncryptionWithIV: case EncryptionModes.PayloadEncryptionWithIVHMAC: SetupUserDataEncryptionWithoutDH(encryptionData, peer); break; case EncryptionModes.DatagramEncyption: case EncryptionModes.DatagramEncyptionWithRandomInitialNumbers: case EncryptionModes.DatagramEncyptionGCMWithRandomInitialNumbers: if (peer.NetworkProtocol != NetworkProtocolType.Udp) { errorMsg = ErrorMessages.EncryptionModeMismatch; return(ErrorCode.InvalidEncryptionParameters); } SetupUdpEncryption(encryptionData, peer, initRequest); break; default: { if (log.IsWarnEnabled) { log.WarnFormat(logSetupCountGuard, $"AuthOnInitHandler: Unknown encryption mode: '{mode}'. appId:{0}/{1}, data:{2}, p:{3}", token.ApplicationId, token.ApplicationVersion, JsonConvert.SerializeObject(encryptionDataDict), peer); } errorMsg = string.Format(ErrorMessages.InvalidEncryptionData, $"Unknown Encryption mode {mode}"); return(ErrorCode.InvalidEncryptionParameters); } } } catch (Exception e) { errorMsg = e.ToString(); var msg = string.Format("AuthOnInitHandler: Exception during encryption setup. appId:{0}/{1}, Data: {2}, p:{3}", token.ApplicationId, token.ApplicationVersion, JsonConvert.SerializeObject(encryptionDataDict), peer); log.Error(logExceptionCountGuard, msg, e); return(ErrorCode.InvalidEncryptionParameters); } return(ErrorCode.Ok); }
/// <summary> /// Return a decrypted entity. This method is used for decrypting entity properties. /// </summary> internal Dictionary<string, EntityProperty> DecryptEntity(IDictionary<string, EntityProperty> properties, HashSet<string> encryptedPropertyDetailsSet, string partitionKey, string rowKey, byte[] contentEncryptionKey, EncryptionData encryptionData) { try { Dictionary<string, EntityProperty> decryptedProperties = new Dictionary<string, EntityProperty>(); switch (encryptionData.EncryptionAgent.EncryptionAlgorithm) { case EncryptionAlgorithm.AES_CBC_256: #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) { using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) { using (SHA256Managed sha256 = new SHA256Managed()) #endif { myAes.Key = contentEncryptionKey; foreach (KeyValuePair<string, EntityProperty> kvp in properties) { if (kvp.Key == Constants.EncryptionConstants.TableEncryptionKeyDetails || kvp.Key == Constants.EncryptionConstants.TableEncryptionPropertyDetails) { // Do nothing. Do not add to the result properties. } else if (encryptedPropertyDetailsSet.Contains(kvp.Key)) { byte[] columnIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(string.Join(partitionKey, rowKey, kvp.Key)))); Array.Resize<byte>(ref columnIV, 16); myAes.IV = columnIV; byte[] src = kvp.Value.BinaryValue; using (ICryptoTransform transform = myAes.CreateDecryptor()) { byte[] dest = transform.TransformFinalBlock(src, 0, src.Length); string destString = Encoding.UTF8.GetString(dest, 0, dest.Length); decryptedProperties.Add(kvp.Key, new EntityProperty(destString)); } } else { decryptedProperties.Add(kvp.Key, kvp.Value); } } } } return decryptedProperties; default: throw new StorageException(SR.InvalidEncryptionAlgorithm, null) { IsRetryable = false }; } } catch (JsonException ex) { throw new StorageException(SR.EncryptionMetadataError, ex) { IsRetryable = false }; } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.DecryptionLogicError, ex) { IsRetryable = false }; } }
/// <summary> /// Return an encrypted entity. This method is used for encrypting entity properties. /// </summary> internal Dictionary <string, EntityProperty> EncryptEntity(IDictionary <string, EntityProperty> properties, string partitionKey, string rowKey, Func <string, string, string, bool> encryptionResolver) { CommonUtility.AssertNotNull("properties", properties); // The Key should be set on the policy for encryption. Otherwise, throw an error. if (this.Key == null) { throw new InvalidOperationException(SR.KeyMissingError, null); } EncryptionData encryptionData = new EncryptionData(); encryptionData.EncryptionAgent = new EncryptionAgent(Constants.EncryptionConstants.EncryptionProtocolV1, EncryptionAlgorithm.AES_CBC_256); encryptionData.KeyWrappingMetadata = new Dictionary <string, string>(); encryptionData.KeyWrappingMetadata[Constants.EncryptionConstants.AgentMetadataKey] = Constants.EncryptionConstants.AgentMetadataValue; Dictionary <string, EntityProperty> encryptedProperties = new Dictionary <string, EntityProperty>(); HashSet <string> encryptionPropertyDetailsSet = new HashSet <string>(); #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) { using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) { using (SHA256Managed sha256 = new SHA256Managed()) #endif { encryptionData.ContentEncryptionIV = myAes.IV; // Wrap always happens locally, irrespective of local or cloud key. So it is ok to call it synchronously. Tuple <byte[], string> wrappedKey = CommonUtility.RunWithoutSynchronizationContext(() => this.Key.WrapKeyAsync(myAes.Key, null /* algorithm */, CancellationToken.None).Result); encryptionData.WrappedContentKey = new WrappedKey(this.Key.Kid, wrappedKey.Item1, wrappedKey.Item2); foreach (KeyValuePair <string, EntityProperty> kvp in properties) { if (encryptionResolver != null && encryptionResolver(partitionKey, rowKey, kvp.Key)) { // Throw if users try to encrypt null properties. This could happen in the DynamicTableEntity case // where a user adds a new property as follows - ent.Properties.Add("foo2", null); if (kvp.Value == null) { throw new InvalidOperationException(SR.EncryptingNullPropertiesNotAllowed); } kvp.Value.IsEncrypted = true; } // IsEncrypted is set to true when either the EncryptPropertyAttribute is set on a property or when it is // specified in the encryption resolver or both. if (kvp.Value != null && kvp.Value.IsEncrypted) { // Throw if users try to encrypt non-string properties. if (kvp.Value.PropertyType != EdmType.String) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.UnsupportedPropertyTypeForEncryption, kvp.Value.PropertyType)); } byte[] columnIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(string.Join(partitionKey, rowKey, kvp.Key)))); Array.Resize <byte>(ref columnIV, 16); myAes.IV = columnIV; using (ICryptoTransform transform = myAes.CreateEncryptor()) { // Throw if users try to encrypt null properties. This could happen in the DynamicTableEntity or POCO // case when the property value is null. if (kvp.Value.IsNull) { throw new InvalidOperationException(SR.EncryptingNullPropertiesNotAllowed); } byte[] src = Encoding.UTF8.GetBytes(kvp.Value.StringValue); byte[] dest = transform.TransformFinalBlock(src, 0, src.Length); // Store the encrypted properties as binary values on the service instead of base 64 encoded strings because strings are stored as a sequence of // WCHARs thereby further reducing the allowed size by half. During retrieve, it is handled by the response parsers correctly // even when the service does not return the type for JSON no-metadata. encryptedProperties.Add(kvp.Key, new EntityProperty(dest)); encryptionPropertyDetailsSet.Add(kvp.Key); } } else { encryptedProperties.Add(kvp.Key, kvp.Value); } } // Encrypt the property details set and add it to entity properties. byte[] metadataIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(string.Join(partitionKey, rowKey, Constants.EncryptionConstants.TableEncryptionPropertyDetails)))); Array.Resize <byte>(ref metadataIV, 16); myAes.IV = metadataIV; using (ICryptoTransform transform = myAes.CreateEncryptor()) { byte[] src = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(encryptionPropertyDetailsSet)); byte[] dest = transform.TransformFinalBlock(src, 0, src.Length); encryptedProperties.Add(Constants.EncryptionConstants.TableEncryptionPropertyDetails, new EntityProperty(dest)); } } } // Add the key details to entity properties. encryptedProperties.Add(Constants.EncryptionConstants.TableEncryptionKeyDetails, new EntityProperty(JsonConvert.SerializeObject(encryptionData))); return(encryptedProperties); }
public bool Compare(EncryptionData other) => other.Password == Password && other.KeySalt.CompareArrayItems(KeySalt.ToArray()) && other.IvSalt.CompareArrayItems(IvSalt.ToArray());
public SerializableEncryptionData(EncryptionData encryptionData) { Password = encryptionData.Password; KeySalt = encryptionData.KeySalt.ToList(); IvSalt = encryptionData.IvSalt.ToList(); }
private static async ValueTask <EncryptionData> CallEndpoint(Stream?encrypted, Stream?decrypted, string endpoint) { using HttpContent content = new StringContent(JsonSerializer.Serialize(await EncryptionData.From(encrypted, decrypted))); content.Headers.ContentType.MediaType = "application/json"; HttpResponseMessage response = await Client.PostAsync(endpoint, content); return(JsonSerializer.Deserialize <EncryptionData>(await response.Content.ReadAsStringAsync(), SerializerOptions)); }
private static async Task UpdateClientsideKeyEncryptionKeyInternal( BlobClient client, UpdateClientSideKeyEncryptionKeyOptions options, bool async, CancellationToken cancellationToken) { // argument validation Argument.AssertNotNull(client, nameof(client)); ClientSideEncryptionOptions operationEncryptionOptions = options?.EncryptionOptionsOverride ?? client.ClientSideEncryption ?? throw new ArgumentException($"{nameof(ClientSideEncryptionOptions)} are not configured on this client and none were provided for the operation."); Argument.AssertNotNull(operationEncryptionOptions.KeyEncryptionKey, nameof(ClientSideEncryptionOptions.KeyEncryptionKey)); Argument.AssertNotNull(operationEncryptionOptions.KeyResolver, nameof(ClientSideEncryptionOptions.KeyResolver)); Argument.AssertNotNull(operationEncryptionOptions.KeyWrapAlgorithm, nameof(ClientSideEncryptionOptions.KeyWrapAlgorithm)); using (client.ClientConfiguration.Pipeline.BeginLoggingScope(nameof(BlobClient))) { client.ClientConfiguration.Pipeline.LogMethodEnter( nameof(BlobBaseClient), message: $"{nameof(Uri)}: {client.Uri}\n" + $"{nameof(options.Conditions)}: {options?.Conditions}"); DiagnosticScope scope = client.ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(BlobClient)}.{nameof(UpdateClientSideKeyEncryptionKey)}"); try { // hold onto etag, metadata, encryptiondata BlobProperties getPropertiesResponse = await client.GetPropertiesInternal(options?.Conditions, async, cancellationToken).ConfigureAwait(false); ETag etag = getPropertiesResponse.ETag; IDictionary <string, string> metadata = getPropertiesResponse.Metadata; EncryptionData encryptionData = BlobClientSideDecryptor.GetAndValidateEncryptionDataOrDefault(metadata) ?? throw new InvalidOperationException("Resource has no client-side encryption key to rotate."); // rotate keywrapping byte[] newWrappedKey = await WrapKeyInternal( await UnwrapKeyInternal( encryptionData, operationEncryptionOptions.KeyResolver, async, cancellationToken).ConfigureAwait(false), operationEncryptionOptions.KeyWrapAlgorithm, operationEncryptionOptions.KeyEncryptionKey, async, cancellationToken).ConfigureAwait(false); // set new wrapped key info and reinsert into metadata encryptionData.WrappedContentKey = new KeyEnvelope { EncryptedKey = newWrappedKey, Algorithm = operationEncryptionOptions.KeyWrapAlgorithm, KeyId = operationEncryptionOptions.KeyEncryptionKey.KeyId }; metadata[Constants.ClientSideEncryption.EncryptionDataKey] = EncryptionDataSerializer.Serialize(encryptionData); // update blob ONLY IF ETAG MATCHES (do not take chances encryption info is now out of sync) BlobRequestConditions modifiedRequestConditions = BlobRequestConditions.CloneOrDefault(options?.Conditions) ?? new BlobRequestConditions(); modifiedRequestConditions.IfMatch = etag; await client.SetMetadataInternal(metadata, modifiedRequestConditions, async, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { client.ClientConfiguration.Pipeline.LogException(ex); scope.Failed(ex); throw; } finally { client.ClientConfiguration.Pipeline.LogMethodExit(nameof(BlobBaseClient)); scope.Dispose(); } } }
public NWSEncryptionTests() { Address address = new Address(); address.Name = "My STORE"; address.StreetAddress1 = "1 MY STREET"; address.City = "MYTOWN"; address.PostalCode = "90210"; address.State = "KY"; address.Country = "USA"; AcceptorConfig acceptorConfig = new AcceptorConfig(); acceptorConfig.Address = address; // data code values acceptorConfig.CardDataInputCapability = CardDataInputCapability.ContactlessEmv_ContactEmv_MagStripe_KeyEntry; acceptorConfig.TerminalOutputCapability = TerminalOutputCapability.Printing_Display; // hardware software config values acceptorConfig.HardwareLevel = "34"; acceptorConfig.SoftwareLevel = "21205710"; // pos configuration values acceptorConfig.SupportsPartialApproval = true; acceptorConfig.SupportsShutOffAmount = true; acceptorConfig.SupportsReturnBalance = true; acceptorConfig.SupportsDiscoverNetworkReferenceId = true; acceptorConfig.SupportsAvsCnvVoidReferrals = true; acceptorConfig.SupportedEncryptionType = EncryptionType.TEP2; // gateway config NetworkGatewayConfig config = new NetworkGatewayConfig(NetworkGatewayType.NWS); config.ServiceUrl = "test.txns-c.secureexchange.net"; config.PrimaryPort = 15031; config.SecondaryEndpoint = "test.txns-e.secureexchange.net"; config.SecondaryPort = 15031; config.CompanyId = "SPSA"; config.TerminalId = "NWSDOTNET01"; config.AcceptorConfig = acceptorConfig; config.EnableLogging = true; config.StanProvider = StanGenerator.GetInstance(); config.BatchProvider = BatchProvider.GetInstance(); ServicesContainer.ConfigureService(config); // AMEX //card = TestCards.AmexManualEncrypted(); ////cardWithCvn = TestCards.AmexManualEncrypted(); ////cardWithCvn.Cvn = "9072488"; //track = TestCards.AmexSwipeEncrypted(); // DISCOVER card = TestCards.DiscoverManualEncrypted(); //cardWithCvn = TestCards.DiscoverManualEncrypted(); //cardWithCvn.Cvn = "703"; track = TestCards.DiscoverSwipeEncryptedV2(); // MASTERCARD //card = TestCards.MasterCardManualEncrypted(); ////cardWithCvn = TestCards.MasterCardManualEncrypted(); ////cardWithCvn.Cvn = "7803754"; //track = TestCards.MasterCardSwipeEncryptedV2(); // VISA //card = TestCards.VisaManualEncrypted(true, true); ////cardWithCvn = TestCards.VisaManualEncrypted(); ////cardWithCvn.Cvn = "7803754"; //track = TestCards.VisaSwipeEncryptedV2(); // DEBIT debit = new DebitTrackData(); debit.Value = ";6090001234567891=2112120000000000001?"; debit.PinBlock = "62968D2481D231E1A504010024A00014"; debit.EncryptionData = EncryptionData.Version2("/wECAQEEAoFGAgEH4gcOTDT6jRZwb3NAc2VjdXJlZXhjaGFuZ2UubmV0m+/d4SO9TEshhRGUUQzVBrBvP/Os1qFx+6zdQp1ejjUCoDmzoUMbil9UG73zBxxTOy25f3Px0p8joyCh8PEWhADz1BkROJT3q6JnocQE49yYBHuFK0obm5kqUcYPfTY09vPOpmN+wp45gJY9PhkJF5XvPsMlcxX4/JhtCshegz4AYrcU/sFnI+nDwhy295BdOkVN1rn00jwCbRcE900kj3UsFfyc", "2"); }
private async void btnNext_Click(object sender, EventArgs e) { try { if (isConfirmUser) { //Do for confirm user if (isValidConfirmUser()) { lblWait.Visible = true; string username = confirmUser1.txtUserName.Text.Trim(); oUser = await user.SelectUser(username); if (oUser != null) { //Show the security question lblWait.Visible = false; //Set the question securityQuestion1.lblQuestion.Text = GetQuestion(oUser.Question); securityQuestion1.BringToFront(); isConfirmUser = false; isQuestion = true; return; } else { lblWait.Visible = false; MessageBox.Show("Invalid Username. You are not a registered user", "Invalid User", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } if (isQuestion) { //Do for question. if (isValidQuestion()) { lblWait.Visible = true; string userAnswer = securityQuestion1.txtAnswer.Text.Trim().ToLower(); if (EncryptionData.ValidateEncryptedData(userAnswer, oUser.Answer)) { //Move to the next step lblWait.Visible = false; isQuestion = false; isPassword = true; userPassword1.BringToFront(); btnNext.Text = "Change"; return; } else { lblWait.Visible = false; MessageBox.Show("Invalid Answer. Please provide a valid answer.", "Invalid Answer", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } if (isPassword) { //Change the user's password if (isValidPassword()) { string newPassword = userPassword1.txtPassword.Text.Trim(); oUser.Password = newPassword; if (await user.ChangePassword(oUser)) { Logger.WriteToFile(oUser.FullName, "successfully changed password"); MessageBox.Show("Password has been successfully changed", "Change Password", MessageBoxButtons.OK, MessageBoxIcon.Information); this.Close(); } else { MessageBox.Show("Unable to change password", "Please try again", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } } } catch (Exception ex) { MessageBox.Show($"Sorry an error occured. \n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
internal byte[] DecryptMetadataAndReturnCEK(string partitionKey, string rowKey, EntityProperty encryptionKeyProperty, EntityProperty propertyDetailsProperty, out EncryptionData encryptionData, out bool isJavaV1) { // Throw if neither the key nor the resolver are set. if (this.Key == null && this.KeyResolver == null) { throw new StorageException(SR.KeyAndResolverMissingError, null) { IsRetryable = false }; } try { encryptionData = JsonConvert.DeserializeObject <EncryptionData>(encryptionKeyProperty.StringValue); EncryptionData encryptionDataCopy = encryptionData; // This is necessary because you cannot use "out" variables in a lambda. CommonUtility.AssertNotNull("ContentEncryptionIV", encryptionData.ContentEncryptionIV); CommonUtility.AssertNotNull("EncryptedKey", encryptionData.WrappedContentKey.EncryptedKey); // Throw if the encryption protocol on the entity doesn't match the version that this client library understands // and is able to decrypt. if (encryptionData.EncryptionAgent.Protocol != Constants.EncryptionConstants.EncryptionProtocolV1) { throw new StorageException(SR.EncryptionProtocolVersionInvalid, null) { IsRetryable = false }; } isJavaV1 = (encryptionData.EncryptionAgent.Protocol == Constants.EncryptionConstants.EncryptionProtocolV1) && ((encryptionData.KeyWrappingMetadata == null) || (encryptionData.KeyWrappingMetadata.ContainsKey(Constants.EncryptionConstants.AgentMetadataKey) && encryptionData.KeyWrappingMetadata[Constants.EncryptionConstants.AgentMetadataKey].Contains("Java"))); byte[] contentEncryptionKey = null; // 1. Invoke the key resolver if specified to get the key. If the resolver is specified but does not have a // mapping for the key id, an error should be thrown. This is important for key rotation scenario. // 2. If resolver is not specified but a key is specified, match the key id on the key and and use it. // Calling UnwrapKeyAsync synchronously is fine because for the storage client scenario, unwrap happens // locally. No service call is made. if (this.KeyResolver != null) { IKey keyEncryptionKey = CommonUtility.RunWithoutSynchronizationContext(() => this.KeyResolver.ResolveKeyAsync(encryptionDataCopy.WrappedContentKey.KeyId, CancellationToken.None).Result); CommonUtility.AssertNotNull("keyEncryptionKey", keyEncryptionKey); contentEncryptionKey = CommonUtility.RunWithoutSynchronizationContext(() => keyEncryptionKey.UnwrapKeyAsync(encryptionDataCopy.WrappedContentKey.EncryptedKey, encryptionDataCopy.WrappedContentKey.Algorithm, CancellationToken.None).Result); } else { if (this.Key.Kid == encryptionData.WrappedContentKey.KeyId) { contentEncryptionKey = CommonUtility.RunWithoutSynchronizationContext(() => this.Key.UnwrapKeyAsync(encryptionDataCopy.WrappedContentKey.EncryptedKey, encryptionDataCopy.WrappedContentKey.Algorithm, CancellationToken.None).Result); } else { throw new StorageException(SR.KeyMismatch, null) { IsRetryable = false }; } } // Decrypt the property details set and add it to entity properties. #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) { using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) { using (SHA256Managed sha256 = new SHA256Managed()) #endif { // Here we are correcting for a bug in Java's v1 encryption. // Java v1 constructed the IV as PK + RK + column name. Other libraries use RK + PK + column name. string IVString = isJavaV1 ? string.Concat(partitionKey, rowKey, Constants.EncryptionConstants.TableEncryptionPropertyDetails) : string.Concat(rowKey, partitionKey, Constants.EncryptionConstants.TableEncryptionPropertyDetails); byte[] metadataIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(IVString))); Array.Resize <byte>(ref metadataIV, 16); myAes.IV = metadataIV; myAes.Key = contentEncryptionKey; using (ICryptoTransform transform = myAes.CreateDecryptor()) { byte[] src = propertyDetailsProperty.BinaryValue; propertyDetailsProperty.BinaryValue = transform.TransformFinalBlock(src, 0, src.Length); } } } return(contentEncryptionKey); } catch (JsonException ex) { throw new StorageException(SR.EncryptionMetadataError, ex) { IsRetryable = false }; } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.DecryptionLogicError, ex) { IsRetryable = false }; } }
/// <summary> /// Return a decrypted entity. This method is used for decrypting entity properties. /// </summary> internal Dictionary <string, EntityProperty> DecryptEntity(IDictionary <string, EntityProperty> properties, HashSet <string> encryptedPropertyDetailsSet, string partitionKey, string rowKey, byte[] contentEncryptionKey, EncryptionData encryptionData, bool isJavav1) { try { Dictionary <string, EntityProperty> decryptedProperties = new Dictionary <string, EntityProperty>(); switch (encryptionData.EncryptionAgent.EncryptionAlgorithm) { case EncryptionAlgorithm.AES_CBC_256: #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) { using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) { using (SHA256Managed sha256 = new SHA256Managed()) #endif { myAes.Key = contentEncryptionKey; foreach (KeyValuePair <string, EntityProperty> kvp in properties) { if (kvp.Key == Constants.EncryptionConstants.TableEncryptionKeyDetails || kvp.Key == Constants.EncryptionConstants.TableEncryptionPropertyDetails) { // Do nothing. Do not add to the result properties. } else if (encryptedPropertyDetailsSet.Contains(kvp.Key)) { byte[] columnIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(isJavav1 ? string.Concat(partitionKey, rowKey, kvp.Key) : string.Concat(rowKey, partitionKey, kvp.Key)))); Array.Resize <byte>(ref columnIV, 16); myAes.IV = columnIV; byte[] src = kvp.Value.BinaryValue; using (ICryptoTransform transform = myAes.CreateDecryptor()) { byte[] dest = transform.TransformFinalBlock(src, 0, src.Length); string destString = Encoding.UTF8.GetString(dest, 0, dest.Length); decryptedProperties.Add(kvp.Key, new EntityProperty(destString)); } } else { decryptedProperties.Add(kvp.Key, kvp.Value); } } } } return(decryptedProperties); default: throw new StorageException(SR.InvalidEncryptionAlgorithm, null) { IsRetryable = false }; } } catch (JsonException ex) { throw new StorageException(SR.EncryptionMetadataError, ex) { IsRetryable = false }; } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.DecryptionLogicError, ex) { IsRetryable = false }; } }
public static IDictionary <string, EntityProperty> DecryptEntityProperties( IDictionary <string, EntityProperty> properties, TableRequestOptions options, ResourceResponse <Document> response) { // If encryption policy is set on options, try to decrypt the entity. EntityProperty propertyDetailsProperty; EntityProperty keyProperty; byte[] cek = null; string pk = response.Resource.GetPropertyValue <string>(StellarConstants.PartitionKeyPropertyName); string rk = response.Resource.Id; if (properties.TryGetValue(Constants.EncryptionConstants.TableEncryptionPropertyDetails, out propertyDetailsProperty) && properties.TryGetValue(Constants.EncryptionConstants.TableEncryptionKeyDetails, out keyProperty)) { // Decrypt the metadata property value to get the names of encrypted properties. EncryptionData encryptionData = null; bool isJavaV1 = false; // Convert the base 64 encoded TableEncryptionPropertyDetails string stored in DocumentDB to binary propertyDetailsProperty = EntityProperty.CreateEntityPropertyFromObject(propertyDetailsProperty.StringValue, EdmType.Binary); cek = options.EncryptionPolicy.DecryptMetadataAndReturnCEK(pk, rk, keyProperty, propertyDetailsProperty, out encryptionData, out isJavaV1); byte[] binaryVal = propertyDetailsProperty.BinaryValue; HashSet <string> encryptedPropertyDetailsSet; encryptedPropertyDetailsSet = EntityHelpers.ParseEncryptedPropertyDetailsSet(isJavaV1, binaryVal); // The encrypted properties are stored in DocumentDB as base 64 encoded strings. Convert them to binary. Dictionary <string, EntityProperty> binEncryptedProperties = new Dictionary <string, EntityProperty>(); foreach (KeyValuePair <string, EntityProperty> kvp in properties) { if (encryptedPropertyDetailsSet.Contains(kvp.Key)) { EntityProperty binEncryptedProperty = EntityProperty.CreateEntityPropertyFromObject(kvp.Value.StringValue, EdmType.Binary); binEncryptedProperties[kvp.Key] = binEncryptedProperty; } } foreach (KeyValuePair <string, EntityProperty> kvp in binEncryptedProperties) { properties[kvp.Key] = binEncryptedProperties[kvp.Key]; } properties = options.EncryptionPolicy.DecryptEntity(properties, encryptedPropertyDetailsSet, pk, rk, cek, encryptionData, isJavaV1); } else { if (options.RequireEncryption.HasValue && options.RequireEncryption.Value) { throw new StorageException(SR.EncryptionDataNotPresentError, null) { IsRetryable = false }; } } return(properties); }
internal byte[] DecryptMetadataAndReturnCEK(string partitionKey, string rowKey, EntityProperty encryptionKeyProperty, EntityProperty propertyDetailsProperty, out EncryptionData encryptionData) { // Throw if neither the key nor the resolver are set. if (this.Key == null && this.KeyResolver == null) { throw new StorageException(SR.KeyAndResolverMissingError, null) { IsRetryable = false }; } try { encryptionData = JsonConvert.DeserializeObject<EncryptionData>(encryptionKeyProperty.StringValue); CommonUtility.AssertNotNull("ContentEncryptionIV", encryptionData.ContentEncryptionIV); CommonUtility.AssertNotNull("EncryptedKey", encryptionData.WrappedContentKey.EncryptedKey); // Throw if the encryption protocol on the entity doesn't match the version that this client library understands // and is able to decrypt. if (encryptionData.EncryptionAgent.Protocol != Constants.EncryptionConstants.EncryptionProtocolV1) { throw new StorageException(SR.EncryptionProtocolVersionInvalid, null) { IsRetryable = false }; } byte[] contentEncryptionKey = null; // 1. Invoke the key resolver if specified to get the key. If the resolver is specified but does not have a // mapping for the key id, an error should be thrown. This is important for key rotation scenario. // 2. If resolver is not specified but a key is specified, match the key id on the key and and use it. // Calling UnwrapKeyAsync synchronously is fine because for the storage client scenario, unwrap happens // locally. No service call is made. if (this.KeyResolver != null) { IKey keyEncryptionKey = this.KeyResolver.ResolveKeyAsync(encryptionData.WrappedContentKey.KeyId, CancellationToken.None).Result; CommonUtility.AssertNotNull("keyEncryptionKey", keyEncryptionKey); contentEncryptionKey = keyEncryptionKey.UnwrapKeyAsync(encryptionData.WrappedContentKey.EncryptedKey, encryptionData.WrappedContentKey.Algorithm, CancellationToken.None).Result; } else { if (this.Key.Kid == encryptionData.WrappedContentKey.KeyId) { contentEncryptionKey = this.Key.UnwrapKeyAsync(encryptionData.WrappedContentKey.EncryptedKey, encryptionData.WrappedContentKey.Algorithm, CancellationToken.None).Result; } else { throw new StorageException(SR.KeyMismatch, null) { IsRetryable = false }; } } // Decrypt the property details set and add it to entity properties. #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) { using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) { using (SHA256Managed sha256 = new SHA256Managed()) #endif { byte[] metadataIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(string.Join(partitionKey, rowKey, Constants.EncryptionConstants.TableEncryptionPropertyDetails)))); Array.Resize<byte>(ref metadataIV, 16); myAes.IV = metadataIV; myAes.Key = contentEncryptionKey; using (ICryptoTransform transform = myAes.CreateDecryptor()) { byte[] src = propertyDetailsProperty.BinaryValue; propertyDetailsProperty.BinaryValue = transform.TransformFinalBlock(src, 0, src.Length); } } } return contentEncryptionKey; } catch (JsonException ex) { throw new StorageException(SR.EncryptionMetadataError, ex) { IsRetryable = false }; } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.DecryptionLogicError, ex) { IsRetryable = false }; } }
public NWSDebitTests() { Address address = new Address { Name = "My STORE", StreetAddress1 = "1 MY STREET", City = "MYTOWN", PostalCode = "90210", State = "KY", Country = "USA" }; AcceptorConfig acceptorConfig = new AcceptorConfig { Address = address, // data code values CardDataInputCapability = CardDataInputCapability.ContactlessEmv_ContactEmv_MagStripe_KeyEntry, TerminalOutputCapability = TerminalOutputCapability.Printing_Display, CardHolderAuthenticationCapability = CardHolderAuthenticationCapability.PIN, //CardHolderAuthenticationEntity = CardHolderAuthenticationEntity.AuthorizingAgent, //OperatingEnvironment = OperatingEnvironment.OnPremises_CardAcceptor_Unattended, // hardware software config values HardwareLevel = "34", SoftwareLevel = "21205710", // pos configuration values SupportsPartialApproval = true, SupportsShutOffAmount = true, SupportsReturnBalance = true, SupportsDiscoverNetworkReferenceId = true, SupportsAvsCnvVoidReferrals = true }; // gateway config NetworkGatewayConfig config = new NetworkGatewayConfig(NetworkGatewayType.NWS) { ServiceUrl = "test.txns-c.secureexchange.net", PrimaryPort = 15031, SecondaryEndpoint = "test.txns-e.secureexchange.net", SecondaryPort = 15031, CompanyId = "SPSA", TerminalId = "NWSDOTNET01", UniqueDeviceId = "0001", AcceptorConfig = acceptorConfig, EnableLogging = true, StanProvider = StanGenerator.GetInstance(), BatchProvider = BatchProvider.GetInstance() }; ServicesContainer.ConfigureService(config); // with merchant type config.MerchantType = "5542"; ServicesContainer.ConfigureService(config, "ICR"); // forced timeout config.ForceGatewayTimeout = true; ServicesContainer.ConfigureService(config, "timeout"); // debit card track = new DebitTrackData { Value = "4355567063338=2012101HJNw/ewskBgnZqkL", PinBlock = "62968D2481D231E1A504010024A00014", EncryptionData = EncryptionData.Version2("/wECAQEEAoFGAgEH4gcOTDT6jRZwb3NAc2VjdXJlZXhjaGFuZ2UubmV0m+/d4SO9TEshhRGUUQzVBrBvP/Os1qFx+6zdQp1ejjUCoDmzoUMbil9UG73zBxxTOy25f3Px0p8joyCh8PEWhADz1BkROJT3q6JnocQE49yYBHuFK0obm5kqUcYPfTY09vPOpmN+wp45gJY9PhkJF5XvPsMlcxX4/JhtCshegz4AYrcU/sFnI+nDwhy295BdOkVN1rn00jwCbRcE900kj3UsFfyc", "2") }; }
/// <summary> /// Return an encrypted entity. This method is used for encrypting entity properties. /// </summary> internal Dictionary<string, EntityProperty> EncryptEntity(IDictionary<string, EntityProperty> properties, string partitionKey, string rowKey, Func<string, string, string, bool> encryptionResolver) { CommonUtility.AssertNotNull("properties", properties); // The Key should be set on the policy for encryption. Otherwise, throw an error. if (this.Key == null) { throw new InvalidOperationException(SR.KeyMissingError, null); } EncryptionData encryptionData = new EncryptionData(); encryptionData.EncryptionAgent = new EncryptionAgent(Constants.EncryptionConstants.EncryptionProtocolV1, EncryptionAlgorithm.AES_CBC_256); encryptionData.KeyWrappingMetadata = new Dictionary<string, string>(); Dictionary<string, EntityProperty> encryptedProperties = new Dictionary<string, EntityProperty>(); HashSet<string> encryptionPropertyDetailsSet = new HashSet<string>(); #if WINDOWS_DESKTOP && !WINDOWS_PHONE using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) { using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) #else using (AesManaged myAes = new AesManaged()) { using (SHA256Managed sha256 = new SHA256Managed()) #endif { encryptionData.ContentEncryptionIV = myAes.IV; // Wrap always happens locally, irrespective of local or cloud key. So it is ok to call it synchronously. Tuple<byte[], string> wrappedKey = this.Key.WrapKeyAsync(myAes.Key, null /* algorithm */, CancellationToken.None).Result; encryptionData.WrappedContentKey = new WrappedKey(this.Key.Kid, wrappedKey.Item1, wrappedKey.Item2); foreach (KeyValuePair<string, EntityProperty> kvp in properties) { if (encryptionResolver != null && encryptionResolver(partitionKey, rowKey, kvp.Key)) { // Throw if users try to encrypt null properties. This could happen in the DynamicTableEntity case // where a user adds a new property as follows - ent.Properties.Add("foo2", null); if (kvp.Value == null) { throw new InvalidOperationException(SR.EncryptingNullPropertiesNotAllowed); } kvp.Value.IsEncrypted = true; } // IsEncrypted is set to true when either the EncryptPropertyAttribute is set on a property or when it is // specified in the encryption resolver or both. if (kvp.Value != null && kvp.Value.IsEncrypted) { // Throw if users try to encrypt non-string properties. if (kvp.Value.PropertyType != EdmType.String) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.UnsupportedPropertyTypeForEncryption, kvp.Value.PropertyType)); } byte[] columnIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(string.Join(partitionKey, rowKey, kvp.Key)))); Array.Resize<byte>(ref columnIV, 16); myAes.IV = columnIV; using (ICryptoTransform transform = myAes.CreateEncryptor()) { // Throw if users try to encrypt null properties. This could happen in the DynamicTableEntity or POCO // case when the property value is null. if (kvp.Value.IsNull) { throw new InvalidOperationException(SR.EncryptingNullPropertiesNotAllowed); } byte[] src = Encoding.UTF8.GetBytes(kvp.Value.StringValue); byte[] dest = transform.TransformFinalBlock(src, 0, src.Length); // Store the encrypted properties as binary values on the service instead of base 64 encoded strings because strings are stored as a sequence of // WCHARs thereby further reducing the allowed size by half. During retrieve, it is handled by the response parsers correctly // even when the service does not return the type for JSON no-metadata. encryptedProperties.Add(kvp.Key, new EntityProperty(dest)); encryptionPropertyDetailsSet.Add(kvp.Key); } } else { encryptedProperties.Add(kvp.Key, kvp.Value); } } // Encrypt the property details set and add it to entity properties. byte[] metadataIV = sha256.ComputeHash(CommonUtility.BinaryAppend(encryptionData.ContentEncryptionIV, Encoding.UTF8.GetBytes(string.Join(partitionKey, rowKey, Constants.EncryptionConstants.TableEncryptionPropertyDetails)))); Array.Resize<byte>(ref metadataIV, 16); myAes.IV = metadataIV; using (ICryptoTransform transform = myAes.CreateEncryptor()) { byte[] src = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(encryptionPropertyDetailsSet)); byte[] dest = transform.TransformFinalBlock(src, 0, src.Length); encryptedProperties.Add(Constants.EncryptionConstants.TableEncryptionPropertyDetails, new EntityProperty(dest)); } } } // Add the key details to entity properties. encryptedProperties.Add(Constants.EncryptionConstants.TableEncryptionKeyDetails, new EntityProperty(JsonConvert.SerializeObject(encryptionData))); return encryptedProperties; }
private static void SetupUserDataEncryptionWithoutDH(EncryptionData encryptionData, ClientPeer peer) { #pragma warning disable 618 peer.SetupPayloadEncryption(encryptionData.EncryptionSecret); #pragma warning restore 618 }