/// <summary> /// Constructs a key chain by decrypting bytes returned by a previous /// call to <see cref="Encrypt" />. /// </summary> /// <param name="key">The <see cref="SymmetricKey" /> to be used for decrypting.</param> /// <param name="encrypted">The encrypted key chain.</param> /// <exception cref="CryptographicException">Thrown if the decrypted key chain is malformed.</exception> public KeyChain(SymmetricKey key, byte[] encrypted) { try { using (var ms = new EnhancedMemoryStream(Crypto.Decrypt(encrypted, key))) { if (ms.ReadInt32() != Magic) { throw new Exception(); } int count; count = ms.ReadInt32(); keys = new Dictionary <string, string>(count); for (int i = 0; i < count; i++) { Add(ms.ReadString16()); } } } catch (Exception e) { throw new CryptographicException("Key chain is malformed.", e); } }
public void EnhancedMemoryStream_Int32() { var es = new EnhancedMemoryStream(); es.WriteInt32(0); es.WriteInt32(65121); es.WriteInt32(0x12345678); es.Seek(0, SeekOrigin.Begin); Assert.AreEqual(0, es.ReadInt32()); Assert.AreEqual(65121, es.ReadInt32()); Assert.AreEqual(0x12345678, es.ReadInt32()); }
/// <summary> /// Decrypts authentication <see cref="Credentials" /> from a /// byte array using the specified public asymmetric private key and /// algorithm. /// </summary> /// <param name="encrypted">The encrypted credential bytes.</param> /// <param name="algorithm">The encryption algorithm.</param> /// <param name="key">The private key.</param> /// <returns>The decrypted <see cref="Credentials" />.</returns> /// <remarks> /// <note> /// The current implementation supports only the "RSA" provider. /// </note> /// </remarks> /// <exception cref="SecurityException">Thrown if the credentials are corrupt.</exception> public static Credentials DecryptCredentials(byte[] encrypted, string algorithm, string key) { try { var decrypted = Decrypt(algorithm, key, encrypted); using (var ms = new EnhancedMemoryStream(decrypted)) { string realm; string account; string password; if (ms.ReadInt32() != Crypto.CredentialMagic) { throw new SecurityException(Crypto.CorruptCredentialsMsg); } realm = ms.ReadString16(); account = ms.ReadString16(); password = ms.ReadString16(); return(new Credentials(realm, account, password)); } } catch (Exception e) { throw new SecurityException(Crypto.CorruptCredentialsMsg, e); } }
/// <summary> /// Decrypts data encrypted using <see cref="Encrypt(SymmetricKey,byte[],int)" />. /// </summary> /// <param name="symmetricKey">The symmetric algorithm arguments.</param> /// <param name="cipherText">The encrypted data.</param> /// <returns>The decrypted result.</returns> public static byte[] Decrypt(SymmetricKey symmetricKey, byte[] cipherText) { EnhancedMemoryStream input = new EnhancedMemoryStream(cipherText); EnhancedMemoryStream ms = new EnhancedMemoryStream(cipherText.Length); BlockDecryptor decryptor = null; try { // Read the header fields if (input.ReadInt32() != Magic) { throw new CryptographicException(BadFormatMsg); } if (input.ReadInt32() != 0) { throw new CryptographicException("Unsupported secure data format version."); } decryptor = new BlockDecryptor(symmetricKey); // Decrypt the contents ms.WriteBytesNoLen(decryptor.Decrypt(input.ReadBytes32())); ms.Position = 0; if (ms.ReadInt32() != Magic) { throw new CryptographicException("Secure data content is corrupt."); } ms.Position += 8; // Skip over the salt return(ms.ReadBytes32()); } finally { if (decryptor != null) { decryptor.Dispose(); } input.Close(); ms.Close(); } }
/// <summary> /// Called when the server receives an unserialized request. /// </summary> /// <param name="requestBytes">The serialized request.</param> private void OnRequest(byte[] requestBytes) { try { using (var input = new EnhancedMemoryStream(requestBytes)) { int typeCode = input.ReadInt32(); SharedMemMessage request; SharedMemMessage response; if (typeCode < 0) { request = new SharedMemErrorMessage(); } else { request = messageFactory.Create(typeCode); } request.InternalReadFrom(input); request.ReadFrom(input); try { response = requestHandler(request); if (response == null) { throw new NullReferenceException("Server request handler returned a NULL response message."); } } catch (Exception e) { response = new SharedMemErrorMessage(); response.InternalError = string.Format("{0}: {1}", e.GetType().FullName, e.Message); } response.InternalRequestId = request.InternalRequestId; response.InternalClientInbox = request.InternalClientInbox; using (var output = new EnhancedMemoryStream(response.SerializedCapacityHint)) { output.WriteInt32(response.TypeCode); response.InternalWriteTo(output); response.WriteTo(output); // This call is synchronous but should execute very quickly (microseconds). outbox.Send(response.InternalClientInbox, output.ToArray()); } } } catch (Exception e) { SysLog.LogException(e); } }
/// <summary> /// Called when the client receives a response. /// </summary> /// <param name="responseBytes">The serialized response.</param> private void OnResponse(byte[] responseBytes) { try { using (var input = new EnhancedMemoryStream(responseBytes)) { int typeCode = input.ReadInt32(); SharedMemMessage response; PendingOperation operation; if (typeCode < 0) { response = new SharedMemErrorMessage(); } else { response = messageFactory.Create(typeCode); } response.InternalReadFrom(input); response.ReadFrom(input); lock (syncLock) { if (!pendingOperations.TryGetValue(response.InternalRequestId, out operation)) { // The response received does not correlate to a pending operation // (probably due to the client side timing it out). We'll just // ignore it. return; } } if (response.InternalError != null) { operation.Tcs.TrySetException(new SharedMemException(response.InternalError)); } else { operation.Tcs.TrySetResult(response); } } } catch (Exception e) { SysLog.LogException(e); } }
/// <summary> /// Verifies request arguments against a signature using a shared <see cref="SymmetricKey" /> /// and the current time. /// </summary> /// <param name="sharedKey">The shared <see cref="SymmetricKey" />.</param> /// <param name="signature">The base-64 encoded signature.</param> /// <param name="args">The request argument collection.</param> /// <param name="signatureKey"> /// The name of the signature key within the event arguments or <c>null</c> /// if the signature is not present. /// </param> /// <param name="graceInterval">Specifies the time period used for verifying the signature request time.</param> /// <returns><c>true</c> if the signature is valid.</returns> /// <remarks> /// <para> /// <paramref name="graceInterval" /> is used when comparing the request time (UTC) embedded /// in the signature with the current machine time (UTC). Request times within the range of: /// </para> /// <code language="none"> /// DateTime.UtcNow - graceInterval <= requestTime <= DateTime.UtcNow + graceInterval /// </code> /// <para> /// will be considered to be valid. Request times outside this range will be invalid. /// Larger grace intervals provide help avoid the problems of incorrect system times or /// long request delivery delays but at the price of the increased exposure to replay /// attacks. /// </para> /// <note> /// If the signature is present in the arguments, then the <paramref name="signatureKey" /> <b>must</b> /// be passed as its name or else the verification will always fail. The reason for this is /// that the hash was originally computed before the signature was added so the hash will be /// different if it includes the signature. /// </note> /// </remarks> public static bool TryVerify(SymmetricKey sharedKey, string signature, ArgCollection args, string signatureKey, TimeSpan graceInterval) { try { byte[] encrypted = Convert.FromBase64String(signature); byte[] decrypted; byte[] requestHash; byte[] hash; DateTime requestTime; DateTime now; decrypted = Crypto.Decrypt(encrypted, sharedKey); if (decrypted.Length != Size) { return(false); } using (var ms = new EnhancedMemoryStream(decrypted)) { ms.Position = 8; if (ms.ReadInt32() != Magic) { return(false); } requestTime = new DateTime(ms.ReadInt64()); requestHash = ms.ReadBytes(SHA1Hasher.DigestSize); } now = DateTime.UtcNow; if (!Helper.Within(requestTime, now, graceInterval)) { return(false); } hash = ComputeHash(args, signatureKey); return(Helper.ArrayEquals(requestHash, hash)); } catch { return(false); } }
/// <summary> /// Decrypts the password change parameters encrypted by <see cref="EncryptPasswordChange" />. /// </summary> /// <param name="encryptedPasswords">The encryoted password change parameters.</param> /// <param name="originalPassword">Returns as the original password.</param> /// <param name="newPassword">Returns as the new password.</param> /// <remarks> /// <note> /// This method is designed to provide a low level of security during development /// and testing, when the overhead of configuring SSL certificates is not worth /// the trouble. Do not rely on this method as your only mechanism for securing /// credentials during transmission in production environments. /// </note> /// </remarks> /// <exception cref="SecurityException">Thrown if the parameters are corrupt.</exception> public static void DecryptPasswordChange(byte[] encryptedPasswords, out string originalPassword, out string newPassword) { try { var decrypted = DecryptWithSalt8(encryptedPasswords, credentialsKey); using (var ms = new EnhancedMemoryStream(decrypted)) { if (ms.ReadInt32() != CredentialMagic) { throw new SecurityException(CorruptCredentialsMsg); } originalPassword = ms.ReadString16(); newPassword = ms.ReadString16(); } } catch (Exception e) { throw new SecurityException(CorruptCredentialsMsg, e); } }
/// <summary> /// Decrypts a byte array encrypted using <see cref="Encrypt(string ,byte[],string,int,int,out SymmetricKey)" />. /// </summary> /// <param name="rsaKey">The decrypting RSA key as XML or as a secure key container name.</param> /// <param name="cipherText">The encrypted data.</param> /// <param name="symmetricKey">Returns as the symmetric encryption algorithm arguments.</param> /// <returns>The decrypted data.</returns> /// <exception cref="CryptographicException">Thrown is the encrypted data block is incorrectly formatted.</exception> /// <remarks> /// Note that applications should take some care to ensure that the <paramref name="symmetricKey" /> /// value return is disposed so that the symmetric encryption key will be cleared. /// </remarks> public static byte[] Decrypt(string rsaKey, byte[] cipherText, out SymmetricKey symmetricKey) { EnhancedMemoryStream input = new EnhancedMemoryStream(cipherText); EnhancedMemoryStream ms = new EnhancedMemoryStream(cipherText.Length); BlockDecryptor decryptor = null; byte[] symKey; byte[] symIV; string algorithm; try { // Read the header fields if (input.ReadInt32() != Magic) { throw new CryptographicException(BadFormatMsg); } if (input.ReadInt32() != 0) { throw new CryptographicException("Unsupported secure data format version."); } // Decrypt the encryption info ms.WriteBytesNoLen(AsymmetricCrypto.Decrypt(CryptoAlgorithm.RSA, rsaKey, input.ReadBytes16())); ms.Position = 0; algorithm = ms.ReadString16(); symKey = ms.ReadBytes16(); symIV = ms.ReadBytes16(); symmetricKey = new SymmetricKey(algorithm, symKey, symIV); decryptor = new BlockDecryptor(algorithm, symKey, symIV); // Decrypt the contents ms.SetLength(0); ms.WriteBytesNoLen(decryptor.Decrypt(input.ReadBytes32())); ms.Position = 0; if (ms.ReadInt32() != Magic) { throw new CryptographicException("Secure data content is corrupt."); } ms.Position += 8; // Skip over the salt return(ms.ReadBytes32()); } finally { if (decryptor != null) { decryptor.Dispose(); } input.Close(); ms.Close(); } }