private void CreateInternal() { // Create Id var randomBytes = new byte[IdPartLengthBytes]; using (var rng = new RNGCryptoServiceProvider()) { rng.GetNonZeroBytes(randomBytes); } byte[] idBytes = new byte[IdPartLengthBytes + IdPrefix.Length]; Buffer.BlockCopy(src: IdPrefix, srcOffset: 0, dst: idBytes, dstOffset: 0, count: IdPrefix.Length); Buffer.BlockCopy(src: randomBytes, srcOffset: 0, dst: idBytes, dstOffset: IdPrefix.Length, count: randomBytes.Length); // Convert to Base32 string. The length of the string is APIKeyV4_IdPartBase64Length string idString = idBytes.ToBase32String().RemoveBase32Padding(); // Create password var passwordString = Guid.NewGuid().ToByteArray().ToBase32String().RemoveBase32Padding(); passwordString = Normalize(passwordString); // No need to remove padding or normalize here.. it's stored in the DB and doesn't need to be pretty var hashedPasswordString = V3Hasher.GenerateHashAsBytes(passwordString).ToBase32String(); IdPart = Normalize(idString); PasswordPart = passwordString; PlaintextApiKey = IdPart + passwordString; HashedApiKey = IdPart + hashedPasswordString; }
/// <summary> /// Creates an ApiKeyV3 from an APIKey V1/V2 format (GUID). /// </summary> public static ApiKeyV3 CreateFromV1V2ApiKey(string plaintextApiKey) { // Since V1/V2/V3 have the same format (Guid), we can use the same parse method if (!TryParse(plaintextApiKey, out ApiKeyV3 apiKeyV3)) { throw new ArgumentException("Invalid format for ApiKey V1/V2"); } apiKeyV3.HashedApiKey = apiKeyV3.IdPart + V3Hasher.GenerateHash(apiKeyV3.PasswordPart); return(apiKeyV3); }
/// <summary> /// Verified this ApiKey with provided hashed ApiKey. /// </summary> public bool Verify(string hashedApiKey) { if (string.IsNullOrWhiteSpace(hashedApiKey) || hashedApiKey.Length != IdAndPasswordHashedLength) { return(false); } string hashedApiKeyIdPart = hashedApiKey.Substring(0, IdPartLength); string hashedApiKeyPasswordPart = hashedApiKey.Substring(IdPartLength); if (!string.Equals(IdPart, Normalize(hashedApiKeyIdPart))) { return(false); } return(V3Hasher.VerifyHash(hashedApiKeyPasswordPart, PasswordPart)); }
/// <summary> /// Verified this ApiKey with provided hashed ApiKey. /// </summary> public bool Verify(string hashedApiKey) { if (string.IsNullOrWhiteSpace(hashedApiKey) || hashedApiKey.Length != IdAndPasswordHashedLength) { return(false); } string hashedApiKeyIdPart = hashedApiKey.Substring(0, IdPartBase32Length); string hashedApiKeyPasswordPart = hashedApiKey.Substring(IdPartBase32Length); if (!string.Equals(IdPart, Normalize(hashedApiKeyIdPart))) { return(false); } // The verification is not case sensitive. This is to maintain the existing behavior that ApiKey authentication is not case-sensitive. return(V3Hasher.VerifyHash(hashedApiKeyPasswordPart.ToUpper().FromBase32String(), PasswordPart)); }
public Credential CreatePasswordCredential(string plaintextPassword) { return(new Credential( LatestPasswordType, V3Hasher.GenerateHash(plaintextPassword))); }