Skip to content

Instantly share code, notes, and snippets.

@BBNicolas
Last active December 10, 2019 20:28
Show Gist options
  • Save BBNicolas/5ab3f1f8c604ebe2e4ecf14c35487495 to your computer and use it in GitHub Desktop.
Save BBNicolas/5ab3f1f8c604ebe2e4ecf14c35487495 to your computer and use it in GitHub Desktop.
Cognito register, validation and login with remember Devices enabled
using System;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Math;
using System.Globalization;
namespace cognito
{
//https://gist.github.com/guywald/7a753e032ca2f979453b7f8aa4fb6569
//https://gist.github.com/KonajuGames/0253adf035d83e3b58a872fb00e4f398
public class AuthenticationHelper
{
private const string InitN = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
public const string NewPasswordRequiredChallengeUserAttributePrefix = "userAttributes.";
private const string DerivedKeyInfo = "Caldera Derived Key";
private const int DerivedKeySize = 16;
private readonly BigInteger _a;
public BigInteger A;
private readonly BigInteger _g;
private readonly BigInteger _k;
public BigInteger N;
private readonly string _userPoolId;
public string verifierDevices { get; private set; }
public string SaltToHashDevices { get; private set; }
public string randomPassword;
public AuthenticationHelper(string userPool)
{
N = new BigInteger(InitN, 16);
_g = new BigInteger("2");
_k = new BigInteger(1, Hash(N.ToByteArray(), _g.ToByteArray()));
var random = new Random();
do
{
_a = new BigInteger(1024, random).Mod(N);
A = _g.ModPow(_a, N);
} while (Equals(A.Mod(N), BigInteger.Zero));
_userPoolId = userPool.Substring(userPool.IndexOf('_') + 1);
}
public byte[] GetPasswordAuthenticationKey(string userId, string userPassword, BigInteger b, BigInteger salt)
{
var u = new BigInteger(1, Hash(A.ToByteArray(), b.ToByteArray()));
var usernamePassword = $"{_userPoolId}{userId}:{userPassword}";
var userIdhash10 = HmacSha256Hash(usernamePassword);
var x = new BigInteger(1, Hash(salt.ToByteArray(), userIdhash10));
var s = Mod(b.Subtract(_k.Multiply(_g.ModPow(x, N))).ModPow(_a.Add(u.Multiply(x)), N), N);
var hmac = new HMACSHA256();
// HKDF From Hkdf.cs
var hkdf = new HKDF(hmac, s.ToByteArray(), u.ToByteArray());
return hkdf.Expand(Encoding.UTF8.GetBytes(DerivedKeyInfo), DerivedKeySize);
}
public static byte[] Hash(params byte[][] blocks)
{
IDigest hash = new Sha256Digest();
var result = new byte[hash.GetDigestSize()];
foreach (var block in blocks)
hash.BlockUpdate(block, 0, block.Length);
hash.DoFinal(result, 0);
return result;
}
public static byte[] HmacSha256Hash(params string[] values)
{
IDigest hash = new Sha256Digest();
var result = new byte[hash.GetDigestSize()];
foreach (var value in values)
{
var bytes = Encoding.ASCII.GetBytes(value);
hash.BlockUpdate(bytes, 0, bytes.Length);
}
hash.DoFinal(result, 0);
return result;
}
private static BigInteger Mod(BigInteger dividend, BigInteger divisor)
{
// Apparently needed for negative dividends, according to AWS SDK for iOS
return divisor.Add(dividend.Remainder(divisor)).Remainder(divisor);
}
public void generateHashDevice(string deviceGroupKey, string username)
{
this.randomPassword = generateRandomString();
var combinedString = $"{deviceGroupKey}{username}:{this.randomPassword}";
var hashedString = HmacSha256Hash(combinedString);
var hexRandom = BitConverter.ToString(randomBytes(16)).Replace("-", string.Empty);
this.SaltToHashDevices = padHex(new BigInteger(hexRandom, 16));
var x = new BigInteger(1, Hash(Encoding.ASCII.GetBytes(this.SaltToHashDevices), hashedString));
var verifierDevicesNotPadded = _g.ModPow(x, N);
this.verifierDevices = padHex(verifierDevicesNotPadded);
}
private string generateRandomString()
{
return Convert.ToBase64String(randomBytes(40));
}
private Byte[] randomBytes(int size)
{
var random = new Random();
Byte[] b = new byte[size];
random.NextBytes(b);
return b;
}
private string padHex(BigInteger bigInt)
{
var hashStr = bigInt.ToString(16);
if (hashStr.Length % 2 == 1)
{
hashStr = $"0{hashStr}";
}
else if ("89ABCDEFabcdef".Contains(hashStr[0].ToString()))
{
hashStr = $"00{hashStr}";
}
return hashStr;
}
public byte[] getSaltToHashDevices_ByteArray()
{
return stringTobyte(this.SaltToHashDevices);
}
public byte[] getVerifierDevices_ByteArray()
{
return stringTobyte(this.verifierDevices);
}
public byte[] stringTobyte(string text)
{
byte[] b = new byte[text.Length / 2];
for (int i = 0; i < text.Length / 2; i++)
{
b[i] = byte.Parse(text[i * 2].ToString() + text[i * 2 + 1].ToString(), NumberStyles.HexNumber);
}
return b;
}
}
}
using Amazon;
using Amazon.CognitoIdentityProvider;
using Amazon.CognitoIdentityProvider.Model;
using Amazon.Runtime;
using Amazon.Util;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace cognito
{
class Cognito
{
public AmazonCognitoIdentityProviderClient client { get; private set; }
private string userPoolId = null;
public string DeviceKey { get; set; } = null;
public string DeviceGroupKey { get; set; } = null;
public string randomPassword { get; set; } = null;
public string AccessToken = null;
public string IdToken = null;
public string RefreshToken = null;
public Cognito(string userPoolId, RegionEndpoint region)
{
this.client = new AmazonCognitoIdentityProviderClient("", "", region);
this.userPoolId = userPoolId;
}
public Cognito(string userPoolId, string DeviceKey, string DeviceGroupKey, RegionEndpoint region)
{
this.client = new AmazonCognitoIdentityProviderClient("", "", region);
this.userPoolId = userPoolId;
this.DeviceKey = DeviceKey;
this.DeviceGroupKey = DeviceGroupKey;
}
public Cognito(string userPoolId, string DeviceKey, string DeviceGroupKey, string RefreshToken, RegionEndpoint region)
{
this.client = new AmazonCognitoIdentityProviderClient("", "", region);
this.userPoolId = userPoolId;
this.DeviceKey = DeviceKey;
this.DeviceGroupKey = DeviceGroupKey;
this.RefreshToken = RefreshToken;
}
public SignUpResponse Register(string clientId, string clientSecret, string password, string email)
{
try
{
SignUpRequest signUpRequest = new SignUpRequest();
signUpRequest.ClientId = clientId;
signUpRequest.SecretHash = SecretHash(email.Replace('@', '_'), clientId, clientSecret);
// User name can not be an email address as the email address is used as an aliase,
// which prevent having multiple users on one email address.
// Loging in with the email address is still possible as only one user per email is allowed.
signUpRequest.Username = email.Replace('@', '_');
signUpRequest.Password = password;
signUpRequest.UserAttributes.Add(AttributeType("email", email));
Task<SignUpResponse> signUpResponseTask = this.client.SignUpAsync(signUpRequest);
signUpResponseTask.Wait();
SignUpResponse response = signUpResponseTask.Result;
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Register User - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public ConfirmSignUpResponse Confirm(string confirmationCode, string clientId, string email, string clientSecret)
{
try
{
ConfirmSignUpRequest confirmSignUpRequest = new ConfirmSignUpRequest();
confirmSignUpRequest.ClientId = clientId;
confirmSignUpRequest.SecretHash = SecretHash(email.Replace('@', '_'), clientId, clientSecret);
confirmSignUpRequest.ConfirmationCode = confirmationCode;
confirmSignUpRequest.Username = email.Replace('@', '_');
Task<ConfirmSignUpResponse> confirmSignUpResponseTask = this.client.ConfirmSignUpAsync(confirmSignUpRequest);
confirmSignUpResponseTask.Wait();
ConfirmSignUpResponse response = confirmSignUpResponseTask.Result;
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Confirm User - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public RespondToAuthChallengeResponse Login(string clientId, string email, string clientSecret, string password)
{
try
{
AuthenticationHelper authenticationHelper = new AuthenticationHelper(userPoolId);
// To use an email adresse as a login the user name given in USERNAME and SecretHash need to be that email
// For both initiateAuthRequest AND respondToAuthRequest
#region First call
InitiateAuthRequest initiateAuthRequest = new InitiateAuthRequest();
initiateAuthRequest.AuthFlow = AuthFlowType.USER_SRP_AUTH;
initiateAuthRequest.AuthParameters.Add("USERNAME", email.Replace('@', '_'));
initiateAuthRequest.AuthParameters.Add("SECRET_HASH", SecretHash(email.Replace('@', '_'), clientId, clientSecret));
initiateAuthRequest.AuthParameters.Add("SRP_A", authenticationHelper.A.ToString(16));
initiateAuthRequest.AuthParameters.Add("DEVICE_KEY", this?.DeviceKey);
initiateAuthRequest.ClientId = clientId;
// First Call (initiateAuthRequest)
Task<InitiateAuthResponse> initiateAuthResponseTask = client.InitiateAuthAsync(initiateAuthRequest);
initiateAuthResponseTask.Wait();
InitiateAuthResponse response = initiateAuthResponseTask.Result;
var userIdForSrp = response.ChallengeParameters["USER_ID_FOR_SRP"];
var userIdForSrpBytes = Encoding.UTF8.GetBytes(userIdForSrp);
var b = new BigInteger(response.ChallengeParameters["SRP_B"], 16);
if (Equals(b.Mod(authenticationHelper.N), BigInteger.Zero))
throw new InvalidOperationException("SRP error, B cannot be zero");
var salt = new BigInteger(response.ChallengeParameters["SALT"], 16);
var key = authenticationHelper.GetPasswordAuthenticationKey(userIdForSrp, password, b, salt);
var user_Pool_Id = userPoolId.Substring(userPoolId.IndexOf('_') + 1);
var userPoolIdBytes = Encoding.UTF8.GetBytes(user_Pool_Id);
var formatTimestamp = AWSSDKUtils.CorrectedUtcNow.ToString("ddd MMM d HH:mm:ss UTC yyyy",
CultureInfo.InvariantCulture);
var keySpec = new KeyParameter(key);
var mac = MacUtilities.GetMac("HMAC-SHA_256");
mac.Init(keySpec);
mac.BlockUpdate(userPoolIdBytes, 0, user_Pool_Id.Length);
mac.BlockUpdate(userIdForSrpBytes, 0, userIdForSrpBytes.Length);
var secretBlock = Convert.FromBase64String(response.ChallengeParameters["SECRET_BLOCK"]);
mac.BlockUpdate(secretBlock, 0, secretBlock.Length);
var dateString = AWSSDKUtils.CorrectedUtcNow.ToString("ddd MMM d HH:mm:ss UTC yyyy",
CultureInfo.InvariantCulture);
var dateBytes = Encoding.UTF8.GetBytes(dateString);
mac.BlockUpdate(dateBytes, 0, dateBytes.Length);
var hmac = new byte[mac.GetMacSize()];
mac.DoFinal(hmac, 0);
var claimSignature = Convert.ToBase64String(hmac);
#endregion
#region Second call
// Second call
RespondToAuthChallengeRequest respondToAuthRequest = new RespondToAuthChallengeRequest();
respondToAuthRequest.ChallengeName = response.ChallengeName;
respondToAuthRequest.ClientId = clientId;
respondToAuthRequest.Session = response.Session;
respondToAuthRequest.ChallengeResponses.Add("PASSWORD_CLAIM_SECRET_BLOCK", response.ChallengeParameters["SECRET_BLOCK"]);
respondToAuthRequest.ChallengeResponses.Add("PASSWORD_CLAIM_SIGNATURE", claimSignature);
respondToAuthRequest.ChallengeResponses.Add("TIMESTAMP", formatTimestamp);
respondToAuthRequest.ChallengeResponses.Add("USERNAME", email.Replace('@', '_'));
respondToAuthRequest.ChallengeResponses.Add("DEVICE_KEY", this?.DeviceKey);
respondToAuthRequest.ChallengeResponses.Add("SECRET_HASH", SecretHash(email.Replace('@', '_'), clientId, clientSecret));
// (respondToAuthRequest)
Task<RespondToAuthChallengeResponse> respondToAuthChallengeResponseTask = client.RespondToAuthChallengeAsync(respondToAuthRequest);
respondToAuthChallengeResponseTask.Wait();
RespondToAuthChallengeResponse authChallengeResponse = respondToAuthChallengeResponseTask.Result;
if (authChallengeResponse.HttpStatusCode == (HttpStatusCode)200)
{
if (authChallengeResponse.ChallengeName == "DEVICE_SRP_AUTH")
{
RespondToAuthChallengeResponse deviceResponse = GetDeviceResponse(clientId, clientSecret, email.Replace('@', '_'));
if (deviceResponse.HttpStatusCode == (HttpStatusCode)200)
{
this.DeviceKey = deviceResponse.AuthenticationResult.NewDeviceMetadata?.DeviceKey;
this.DeviceGroupKey = deviceResponse.AuthenticationResult.NewDeviceMetadata?.DeviceKey;
this.AccessToken = deviceResponse.AuthenticationResult.AccessToken;
this.IdToken = deviceResponse.AuthenticationResult.IdToken;
this.RefreshToken = deviceResponse.AuthenticationResult.RefreshToken;
}
return deviceResponse;
}
else
{
this.DeviceKey = authChallengeResponse.AuthenticationResult.NewDeviceMetadata?.DeviceKey;
this.DeviceGroupKey = authChallengeResponse.AuthenticationResult.NewDeviceMetadata?.DeviceKey;
this.AccessToken = authChallengeResponse.AuthenticationResult.AccessToken;
this.IdToken = authChallengeResponse.AuthenticationResult.IdToken;
this.RefreshToken = authChallengeResponse.AuthenticationResult.RefreshToken;
}
}
return authChallengeResponse;
#endregion
}
catch (Exception error)
{
string errorMessage = " - ERROR in Login User - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public ConfirmDeviceResponse ConfirmDevice(string Device_Name, string DeviceGroupKey, string DeviceKey)
{
try
{
AuthenticationHelper authenticationHelper = new AuthenticationHelper(userPoolId);
authenticationHelper.generateHashDevice(DeviceGroupKey, DeviceKey);
this.randomPassword = authenticationHelper.randomPassword;
ConfirmDeviceRequest confirmDeviceRequest = new ConfirmDeviceRequest();
confirmDeviceRequest.DeviceKey = DeviceKey;
confirmDeviceRequest.AccessToken = AccessToken;
confirmDeviceRequest.DeviceName = Device_Name;
confirmDeviceRequest.DeviceSecretVerifierConfig = new DeviceSecretVerifierConfigType();
confirmDeviceRequest.DeviceSecretVerifierConfig.PasswordVerifier = Convert.ToBase64String(authenticationHelper.getVerifierDevices_ByteArray());
confirmDeviceRequest.DeviceSecretVerifierConfig.Salt = Convert.ToBase64String(authenticationHelper.getSaltToHashDevices_ByteArray());
Task<ConfirmDeviceResponse> confirmDeviceResponseTask = client.ConfirmDeviceAsync(confirmDeviceRequest);
confirmDeviceResponseTask.Wait();
ConfirmDeviceResponse response = confirmDeviceResponseTask.Result;
if (response.HttpStatusCode == (HttpStatusCode)200)
{
this.DeviceGroupKey = DeviceGroupKey;
this.DeviceKey = DeviceKey;
}
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Confirm Device - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public InitiateAuthResponse Refresh(string clientId, string email, string clientSecret, string refreshToken)
{
try
{
// Device has been disabled while it is not working on cognito side.
// Once it is working again we will need to provide the device key when refreshing
//
InitiateAuthRequest initiateAuthRequest = new InitiateAuthRequest();
initiateAuthRequest.AuthFlow = AuthFlowType.REFRESH_TOKEN_AUTH;
// User Name is only needed if AuthFlow is REFRESH_TOKEN
//initiateAuthRequest.AuthParameters.Add("USERNAME", this.TextBox_Login_userName.Text);
initiateAuthRequest.AuthParameters.Add("SECRET_HASH", SecretHash(email.Replace('@', '_'), clientId, clientSecret));
initiateAuthRequest.AuthParameters.Add("REFRESH_TOKEN", refreshToken);
initiateAuthRequest.AuthParameters.Add("DEVICE_KEY", this?.DeviceKey);
initiateAuthRequest.ClientId = clientId;
Task<InitiateAuthResponse> initiateAuthResponseTask = client.InitiateAuthAsync(initiateAuthRequest);
initiateAuthResponseTask.Wait();
InitiateAuthResponse response = initiateAuthResponseTask.Result;
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Refresh User - " + Environment.NewLine;
if (this.DeviceKey == null)
{
errorMessage += "Warning if remember device is enabled the device need to be Confirmed using the ConfirmDevice method" + Environment.NewLine;
}
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public GlobalSignOutResponse Logout()
{
try
{
// At least make cognito forget the access token.
GlobalSignOutRequest globalSignOutRequest = new GlobalSignOutRequest();
globalSignOutRequest.AccessToken = this.AccessToken;
Task<GlobalSignOutResponse> globalSignOutResponseTask = this.client.GlobalSignOutAsync(globalSignOutRequest);
globalSignOutResponseTask.Wait();
GlobalSignOutResponse response = globalSignOutResponseTask.Result;
if (response.HttpStatusCode == (HttpStatusCode)200)
{
AccessToken = null;
IdToken = null;
RefreshToken = null;
}
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Logout - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public GlobalSignOutResponse Logout(string AccessToken)
{
try
{
// At least make cognito forget the access token.
GlobalSignOutRequest globalSignOutRequest = new GlobalSignOutRequest();
globalSignOutRequest.AccessToken = AccessToken;
Task<GlobalSignOutResponse> globalSignOutResponseTask = this.client.GlobalSignOutAsync(globalSignOutRequest);
globalSignOutResponseTask.Wait();
GlobalSignOutResponse response = globalSignOutResponseTask.Result;
if (response.HttpStatusCode == (HttpStatusCode)200)
{
this.AccessToken = null;
this.IdToken = null;
this.RefreshToken = null;
}
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Logout - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
private RespondToAuthChallengeResponse GetDeviceResponse(string clientId, string clientSecret, string email)
{
try
{
AuthenticationHelper authenticationHelper = new AuthenticationHelper(DeviceGroupKey);
RespondToAuthChallengeRequest respondToAuthRequest = new RespondToAuthChallengeRequest();
respondToAuthRequest.ChallengeName = ChallengeNameType.DEVICE_SRP_AUTH;
respondToAuthRequest.ClientId = clientId;
respondToAuthRequest.Session = null;
respondToAuthRequest.ChallengeResponses.Add("USERNAME", email.Replace('@', '_'));
respondToAuthRequest.ChallengeResponses.Add("DEVICE_KEY", this?.DeviceKey);
respondToAuthRequest.ChallengeResponses.Add("SECRET_HASH", SecretHash(email.Replace('@', '_'), clientId, clientSecret));
respondToAuthRequest.ChallengeResponses.Add("SRP_A", authenticationHelper.A.ToString(16));
Task<RespondToAuthChallengeResponse> RespondToAuthChallengeResponseTask = client.RespondToAuthChallengeAsync(respondToAuthRequest);
RespondToAuthChallengeResponseTask.Wait();
RespondToAuthChallengeResponse authChallengeResponse = RespondToAuthChallengeResponseTask.Result;
var challengeParameters = authChallengeResponse.ChallengeParameters;
var serverBValue = new BigInteger(challengeParameters["SRP_B"], 16);
if (Equals(serverBValue.Mod(authenticationHelper.N), BigInteger.Zero))
throw new InvalidOperationException("SRP error, B cannot be zero");
var salt = new BigInteger(challengeParameters["SALT"], 16);
var key = authenticationHelper.GetPasswordAuthenticationKey(DeviceKey, this.randomPassword, serverBValue, salt);
var DeviceGroupKeyBytes = Encoding.UTF8.GetBytes(DeviceGroupKey);
var DeviceKeyBytes = Encoding.UTF8.GetBytes(DeviceKey);
var formatTimestamp = AWSSDKUtils.CorrectedUtcNow.ToString("ddd MMM d HH:mm:ss UTC yyyy",
CultureInfo.InvariantCulture);
var keySpec = new KeyParameter(key);
var mac = MacUtilities.GetMac("HMAC-SHA_256");
mac.Init(keySpec);
mac.BlockUpdate(DeviceGroupKeyBytes, 0, DeviceGroupKey.Length);
mac.BlockUpdate(DeviceKeyBytes, 0, DeviceKeyBytes.Length);
var secretBlock = Convert.FromBase64String(challengeParameters["SECRET_BLOCK"]);
mac.BlockUpdate(secretBlock, 0, secretBlock.Length);
var dateString = AWSSDKUtils.CorrectedUtcNow.ToString("ddd MMM d HH:mm:ss UTC yyyy",
CultureInfo.InvariantCulture);
var dateBytes = Encoding.UTF8.GetBytes(dateString);
mac.BlockUpdate(dateBytes, 0, dateBytes.Length);
var hmac = new byte[mac.GetMacSize()];
mac.DoFinal(hmac, 0);
var claimSignature = Convert.ToBase64String(hmac);
RespondToAuthChallengeRequest respondToAuthRequest_srp_b = new RespondToAuthChallengeRequest();
respondToAuthRequest_srp_b.ChallengeName = authChallengeResponse.ChallengeName;
respondToAuthRequest_srp_b.ClientId = clientId;
respondToAuthRequest_srp_b.Session = authChallengeResponse.Session;
respondToAuthRequest_srp_b.ChallengeResponses.Add("PASSWORD_CLAIM_SECRET_BLOCK", challengeParameters["SECRET_BLOCK"]);
respondToAuthRequest_srp_b.ChallengeResponses.Add("PASSWORD_CLAIM_SIGNATURE", claimSignature);
respondToAuthRequest_srp_b.ChallengeResponses.Add("TIMESTAMP", formatTimestamp);
respondToAuthRequest_srp_b.ChallengeResponses.Add("USERNAME", email.Replace('@', '_'));
respondToAuthRequest_srp_b.ChallengeResponses.Add("DEVICE_KEY", DeviceKey);
respondToAuthRequest_srp_b.ChallengeResponses.Add("SECRET_HASH", SecretHash(email.Replace('@', '_'), clientId, clientSecret));
Task<RespondToAuthChallengeResponse> respondToAuthChallengeResponseTask = client.RespondToAuthChallengeAsync(respondToAuthRequest_srp_b);
respondToAuthChallengeResponseTask.Wait();
RespondToAuthChallengeResponse response = respondToAuthChallengeResponseTask.Result;
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Device Response Challenge - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public ForgotPasswordResponse ForgotPassword(string clientId, string clientSecret, string email)
{
try
{
ForgotPasswordRequest forgotPasswordRequest = new ForgotPasswordRequest();
forgotPasswordRequest.ClientId = clientId;
forgotPasswordRequest.Username = email.Replace('@', '_');
forgotPasswordRequest.SecretHash = SecretHash(email.Replace('@', '_'), clientId, clientSecret);
Task<ForgotPasswordResponse> forgotPasswordResponseTask = client.ForgotPasswordAsync(forgotPasswordRequest);
forgotPasswordResponseTask.Wait();
ForgotPasswordResponse response = forgotPasswordResponseTask.Result;
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Forgot Password - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public ConfirmForgotPasswordResponse ConfirmPassword(string clientId, string clientSecret, string email, string confirmationCode, string newPassword)
{
try
{
ConfirmForgotPasswordRequest confirmForgotPasswordRequest = new ConfirmForgotPasswordRequest();
confirmForgotPasswordRequest.ClientId = clientId;
confirmForgotPasswordRequest.Username = email.Replace('@', '_');
confirmForgotPasswordRequest.SecretHash = SecretHash(email.Replace('@', '_'), clientId, clientSecret);
confirmForgotPasswordRequest.ConfirmationCode = confirmationCode;
confirmForgotPasswordRequest.Password = newPassword;
Task<ConfirmForgotPasswordResponse> ConfirmForgotPasswordResponseTask = client.ConfirmForgotPasswordAsync(confirmForgotPasswordRequest);
ConfirmForgotPasswordResponseTask.Wait();
ConfirmForgotPasswordResponse response = ConfirmForgotPasswordResponseTask.Result;
return response;
}
catch (Exception error)
{
string errorMessage = " - ERROR in Confirm Forgot Password - " + Environment.NewLine;
if (error.Data.Contains("CogntioFail"))
{
error.Data["CogntioFail"] += errorMessage;
}
else
{
error.Data.Add("CogntioFail", errorMessage);
}
throw error;
}
}
public static string SecretHash(string username, string clientId, string clientSecret)
{
return CryptoUtilFactory.CryptoInstance.HMACSign($"{username}{clientId}", clientSecret,
SigningAlgorithm.HmacSHA256);
}
private static AttributeType AttributeType(String Name, String Value)
{
AttributeType att = new AttributeType();
att.Name = Name;
att.Value = Value;
return att;
}
public static double DateTimeToUnixTimestamp(DateTime dateTime)
{
return (TimeZoneInfo.ConvertTimeToUtc(dateTime) - new DateTime(1970, 1, 1)).TotalSeconds;
}
}
}
//https://gist.github.com/charlesportwoodii/7c5cf32e92ee88fec5e8f3270d0b44fc
using System;
using System.IO;
using System.Security.Cryptography;
namespace cognito
{
/// <summary>
/// HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
/// https://tools.ietf.org/html/rfc5869
/// </summary>
public class HKDF
{
private readonly int hashLength;
private readonly HMAC hmac;
private readonly byte[] prk;
/// <summary>
/// Initializes a new instance of the <see cref="HKDF" /> class.
/// </summary>
/// <param name="hmac">The HMAC hash function to use.</param>
/// <param name="ikm">input keying material.</param>
/// <param name="salt">
/// optional salt value (a non-secret random value); if not provided, it is set to a string of
/// HMAC.HashSize/8 zeros.
/// </param>
public HKDF(HMAC hmac, byte[] ikm, byte[] salt = null)
{
this.hmac = hmac;
hashLength = hmac.HashSize / 8;
// now we compute the PRK
hmac.Key = salt ?? new byte[hashLength];
prk = hmac.ComputeHash(ikm);
}
/// <summary>
/// Expands the specified info.
/// </summary>
/// <param name="info">optional context and application specific information (can be a zero-length string)</param>
/// <param name="l">length of output keying material in octets (&lt;= 255*HashLen)</param>
/// <returns>OKM (output keying material) of L octets</returns>
public byte[] Expand(byte[] info, int l)
{
if (info == null) info = new byte[0];
hmac.Key = prk;
var n = (int) Math.Ceiling(l * 1f / hashLength);
var t = new byte[n * hashLength];
using (var ms = new MemoryStream())
{
var prev = new byte[0];
for (var i = 1; i <= n; i++)
{
ms.Write(prev, 0, prev.Length);
if (info.Length > 0) ms.Write(info, 0, info.Length);
ms.WriteByte((byte) (0x01 * i));
prev = hmac.ComputeHash(ms.ToArray());
Array.Copy(prev, 0, t, (i - 1) * hashLength, hashLength);
ms.SetLength(0); //reset
}
}
var okm = new byte[l];
Array.Copy(t, okm, okm.Length);
return okm;
}
}
}
using Amazon;
using Amazon.CognitoIdentityProvider;
using Amazon.CognitoIdentityProvider.Model;
using Amazon.Runtime;
using Amazon.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace cognito
{
class Program
{
static void Main(string[] args)
{
string password = "*******";
string email = "Test@Demo.com";
string clientId = "******************0h8k";
string clientSecret = "******************************mggcg";
string userPoolId = "us-east-1_*******";
Cognito cognito = new Cognito(userPoolId, RegionEndpoint.USEast1);
cognito.Register(clientId, clientSecret, password, email);
string validationCode = Console.ReadLine();
cognito.Confirm(validationCode, clientId, email, clientSecret);
try
{
var response1 = cognito.Login(clientId, email, clientSecret, password);
if (response1.AuthenticationResult.NewDeviceMetadata?.DeviceKey != null && cognito.DeviceKey != null)
{
ConfirmDeviceResponse confirmDeviceResponse = cognito.ConfirmDevice(Environment.MachineName, response1.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey, response1.AuthenticationResult.NewDeviceMetadata.DeviceKey);
}
cognito.Login(clientId, email, clientSecret, password);
}
catch (Exception error)
{
Console.Write(error.ToString());
}
Console.Read();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment