How Do I Avoid SSL Certificates during Development for a WCF endpoint that will be secured during Production - wcf

We are developing a number of WCF services. requests will cross a domain boundry; that is, the clients are running in one domain and the servers handling the requests are in a different (production) domain. I know how to secure this link with SSL and certificates. We will aks the users for their usernames and passwords on the production domain and pass those in the SOAP headers.
My problem is what to do during development and "beta" testing. I know that I can get a temporary certificate and use that during development. I am wondering what my alternatives to this approach are. What have others done in this situation?
Update: I am not sure that I have gotten a "good" answer to my question. I am part of a large team (50+) of developers. The organization is fairly agile. Any one of the developers could end up working on the project that is using WCF. In fact several of the other projects are doing something similar but for different web sites and services. What I was looking for was a way that I could have anyone come in and work on this particular project for a few days without having to jump through a number of hoops. Installing the development certificate is one of those hoops. I fully understand that "dogfooding" the WCF structure during development is the best practice. Most the answers gave that as the answer. I wanted to know what, if anything, made sense that was other than "get a test certificate (or two) and install it on all of the developer boxes."
Jon

UPDATE: We actually use the much simpler Keith Brown solution instead of this now, see the source code he has provided. Advantage: No unmanaged code to maintain.
If you still want to see how to do it using C/C++, read on...
Only recommended for use in Development of course, not production, but there is a low-friction way to generate X.509 certificates (without resorting to makecert.exe).
If you have access to CryptoAPI on Windows, the idea is that you use CryptoAPI calls to generate RSA public and private keys, sign and encode a new X.509 certificate, put it in a memory-only certificate store, and then use PFXExportCertStore() to generate the .pfx bytes which you can then pass to X509Certificate2 constructor.
Once you have an X509Certificate2 instance, you can set it as a property of the appropriate WCF objects, and things just start working.
I have some example code I've written, no guarantees of any sort of course, and you'll need a bit of C experience to write the bits that have to be unmanaged (it would be a lot more painful to write the P/Invoke for all CryptoAPI calls than to have that part be in C/C++).
Example C# code that uses the unmanaged helper function:
public X509Certificate2 GenerateSelfSignedCertificate(string issuerCommonName, string keyPassword)
{
int pfxSize = -1;
IntPtr pfxBufferPtr = IntPtr.Zero;
IntPtr errorMessagePtr = IntPtr.Zero;
try
{
if (!X509GenerateSelfSignedCertificate(KeyContainerName, issuerCommonName, keyPassword, ref pfxSize, ref pfxBufferPtr, ref errorMessagePtr))
{
string errorMessage = null;
if (errorMessagePtr != IntPtr.Zero)
{
errorMessage = Marshal.PtrToStringUni(errorMessagePtr);
}
throw new ApplicationException(string.Format("Failed to generate X.509 server certificate. {0}", errorMessage ?? "Unspecified error."));
}
if (pfxBufferPtr == IntPtr.Zero)
{
throw new ApplicationException("Failed to generate X.509 server certificate. PFX buffer not initialized.");
}
if (pfxSize <= 0)
{
throw new ApplicationException("Failed to generate X.509 server certificate. PFX buffer size invalid.");
}
byte[] pfxBuffer = new byte[pfxSize];
Marshal.Copy(pfxBufferPtr, pfxBuffer, 0, pfxSize);
return new X509Certificate2(pfxBuffer, keyPassword);
}
finally
{
if (pfxBufferPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(pfxBufferPtr);
}
if (errorMessagePtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(errorMessagePtr);
}
}
}
The X509GenerateSelfSignedCertificate function implementation could go something like this (you need WinCrypt.h):
BOOL X509GenerateSelfSignedCertificate(LPCTSTR keyContainerName, LPCTSTR issuerCommonName, LPCTSTR keyPassword, DWORD *pfxSize, BYTE **pfxBuffer, LPTSTR *errorMessage)
{
// Constants
#define CERT_DN_ATTR_COUNT 1
#define SIZE_SERIALNUMBER 8
#define EXPIRY_YEARS_FROM_NOW 2
#define MAX_COMMON_NAME 8192
#define MAX_PFX_SIZE 65535
// Declarations
HCRYPTPROV hProv = NULL;
BOOL result = FALSE;
// Sanity
if (pfxSize != NULL)
{
*pfxSize = -1;
}
if (pfxBuffer != NULL)
{
*pfxBuffer = NULL;
}
if (errorMessage != NULL)
{
*errorMessage = NULL;
}
if (keyContainerName == NULL || _tcslen(issuerCommonName) <= 0)
{
SetOutputErrorMessage(errorMessage, _T("Key container name must not be NULL or an empty string."));
return FALSE;
}
if (issuerCommonName == NULL || _tcslen(issuerCommonName) <= 0)
{
SetOutputErrorMessage(errorMessage, _T("Issuer common name must not be NULL or an empty string."));
return FALSE;
}
if (keyPassword == NULL || _tcslen(keyPassword) <= 0)
{
SetOutputErrorMessage(errorMessage, _T("Key password must not be NULL or an empty string."));
return FALSE;
}
// Start generating
USES_CONVERSION;
if (CryptAcquireContext(&hProv, keyContainerName, MS_DEF_RSA_SCHANNEL_PROV, PROV_RSA_SCHANNEL, CRYPT_MACHINE_KEYSET) ||
CryptAcquireContext(&hProv, keyContainerName, MS_DEF_RSA_SCHANNEL_PROV, PROV_RSA_SCHANNEL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
{
HCRYPTKEY hKey = NULL;
// Generate 1024-bit RSA keypair.
if (CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | RSA1024BIT_KEY, &hKey))
{
DWORD pkSize = 0;
PCERT_PUBLIC_KEY_INFO pkInfo = NULL;
// Export public key for use by certificate signing.
if (CryptExportPublicKeyInfo(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, NULL, &pkSize) &&
(pkInfo = (PCERT_PUBLIC_KEY_INFO)LocalAlloc(0, pkSize)) &&
CryptExportPublicKeyInfo(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, pkInfo, &pkSize))
{
CERT_RDN_ATTR certDNAttrs[CERT_DN_ATTR_COUNT];
CERT_RDN certDN[CERT_DN_ATTR_COUNT] = {{1, &certDNAttrs[0]}};
CERT_NAME_INFO certNameInfo = {CERT_DN_ATTR_COUNT, &certDN[0]};
DWORD certNameSize = -1;
BYTE *certNameData = NULL;
certDNAttrs[0].dwValueType = CERT_RDN_UNICODE_STRING;
certDNAttrs[0].pszObjId = szOID_COMMON_NAME;
certDNAttrs[0].Value.cbData = (DWORD)(_tcslen(issuerCommonName) * sizeof(WCHAR));
certDNAttrs[0].Value.pbData = (BYTE*)T2W((LPTSTR)issuerCommonName);
// Encode issuer name into certificate name blob.
if (CryptEncodeObject(X509_ASN_ENCODING, X509_NAME, &certNameInfo, NULL, &certNameSize) &&
(certNameData = (BYTE*)LocalAlloc(0, certNameSize)) &&
CryptEncodeObject(X509_ASN_ENCODING, X509_NAME, &certNameInfo, certNameData, &certNameSize))
{
CERT_NAME_BLOB issuerName;
CERT_INFO certInfo;
SYSTEMTIME systemTime;
FILETIME notBefore;
FILETIME notAfter;
BYTE serialNumber[SIZE_SERIALNUMBER];
DWORD certSize = -1;
BYTE *certData = NULL;
issuerName.cbData = certNameSize;
issuerName.pbData = certNameData;
// Certificate should be valid for a decent window of time.
ZeroMemory(&certInfo, sizeof(certInfo));
GetSystemTime(&systemTime);
systemTime.wYear -= 1;
SystemTimeToFileTime(&systemTime, &notBefore);
systemTime.wYear += EXPIRY_YEARS_FROM_NOW;
SystemTimeToFileTime(&systemTime, &notAfter);
// Generate a throwaway serial number.
if (CryptGenRandom(hProv, SIZE_SERIALNUMBER, serialNumber))
{
certInfo.dwVersion = CERT_V3;
certInfo.SerialNumber.cbData = SIZE_SERIALNUMBER;
certInfo.SerialNumber.pbData = serialNumber;
certInfo.SignatureAlgorithm.pszObjId = szOID_RSA_MD5RSA;
certInfo.Issuer = issuerName;
certInfo.NotBefore = notBefore;
certInfo.NotAfter = notAfter;
certInfo.Subject = issuerName;
certInfo.SubjectPublicKeyInfo = *pkInfo;
// Now sign and encode it.
if (CryptSignAndEncodeCertificate(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, NULL, &certSize) &&
(certData = (BYTE*)LocalAlloc(0, certSize)) &&
CryptSignAndEncodeCertificate(hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, (LPVOID)&certInfo, &(certInfo.SignatureAlgorithm), NULL, certData, &certSize))
{
HCERTSTORE hCertStore = NULL;
// Open a new temporary store.
if ((hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL)))
{
PCCERT_CONTEXT certContext = NULL;
// Add to temporary store so we can use the PFX functions to export a store + private keys in PFX format.
if (CertAddEncodedCertificateToStore(hCertStore, X509_ASN_ENCODING, certData, certSize, CERT_STORE_ADD_NEW, &certContext))
{
CRYPT_KEY_PROV_INFO keyProviderInfo;
// Link keypair to certificate (without this the keypair gets "lost" on export).
ZeroMemory(&keyProviderInfo, sizeof(keyProviderInfo));
keyProviderInfo.pwszContainerName = T2W((LPTSTR)keyContainerName);
keyProviderInfo.pwszProvName = MS_DEF_RSA_SCHANNEL_PROV_W; /* _W used intentionally. struct hardcodes LPWSTR. */
keyProviderInfo.dwProvType = PROV_RSA_SCHANNEL;
keyProviderInfo.dwFlags = CRYPT_MACHINE_KEYSET;
keyProviderInfo.dwKeySpec = AT_KEYEXCHANGE;
// Finally, export to PFX and provide to caller.
if (CertSetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, 0, (LPVOID)&keyProviderInfo))
{
CRYPT_DATA_BLOB pfxBlob;
DWORD pfxExportFlags = EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY;
// Calculate size required.
ZeroMemory(&pfxBlob, sizeof(pfxBlob));
if (PFXExportCertStore(hCertStore, &pfxBlob, T2CW(keyPassword), pfxExportFlags))
{
pfxBlob.pbData = (BYTE *)LocalAlloc(0, pfxBlob.cbData);
if (pfxBlob.pbData != NULL)
{
// Now export.
if (PFXExportCertStore(hCertStore, &pfxBlob, T2CW(keyPassword), pfxExportFlags))
{
if (pfxSize != NULL)
{
*pfxSize = pfxBlob.cbData;
}
if (pfxBuffer != NULL)
{
// Caller must free this.
*pfxBuffer = pfxBlob.pbData;
}
else
{
// Caller did not provide target pointer to receive buffer, free ourselves.
LocalFree(pfxBlob.pbData);
}
result = TRUE;
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to export certificate in PFX format (0x%08x)."), GetLastError());
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to export certificate in PFX format, buffer allocation failure (0x%08x)."), GetLastError());
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to export certificate in PFX format, failed to calculate buffer size (0x%08x)."), GetLastError());
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to set certificate key context property (0x%08x)."), GetLastError());
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to add certificate to temporary certificate store (0x%08x)."), GetLastError());
}
CertCloseStore(hCertStore, 0);
hCertStore = NULL;
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to create temporary certificate store (0x%08x)."), GetLastError());
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to sign/encode certificate or out of memory (0x%08x)."), GetLastError());
}
if (certData != NULL)
{
LocalFree(certData);
certData = NULL;
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to generate certificate serial number (0x%08x)."), GetLastError());
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to encode X.509 certificate name into ASN.1 or out of memory (0x%08x)."), GetLastError());
}
if (certNameData != NULL)
{
LocalFree(certNameData);
certNameData = NULL;
}
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to export public key blob or out of memory (0x%08x)."), GetLastError());
}
if (pkInfo != NULL)
{
LocalFree(pkInfo);
pkInfo = NULL;
}
CryptDestroyKey(hKey);
hKey = NULL;
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to generate public/private keypair for certificate (0x%08x)."), GetLastError());
}
CryptReleaseContext(hProv, 0);
hProv = NULL;
}
else
{
SetOutputErrorMessage(errorMessage, _T("Failed to acquire cryptographic context (0x%08x)."), GetLastError());
}
return result;
}
void
SetOutputErrorMessage(LPTSTR *errorMessage, LPCTSTR formatString, ...)
{
#define MAX_ERROR_MESSAGE 1024
va_list va;
if (errorMessage != NULL)
{
size_t sizeInBytes = (MAX_ERROR_MESSAGE * sizeof(TCHAR)) + 1;
LPTSTR message = (LPTSTR)LocalAlloc(0, sizeInBytes);
va_start(va, formatString);
ZeroMemory(message, sizeInBytes);
if (_vstprintf_s(message, MAX_ERROR_MESSAGE, formatString, va) == -1)
{
ZeroMemory(message, sizeInBytes);
_tcscpy_s(message, MAX_ERROR_MESSAGE, _T("Failed to build error message"));
}
*errorMessage = message;
va_end(va);
}
}
We've used this to generate SSL certificates on startup, which is fine when you only want to test encryption and not verify trust/identity, and it takes only about 2-3 seconds to generate.

Really you want your development environment to match production as much as possible. WCF will check revocation lists during transport negotiation or signature checking and self signed certificates, or faked certifications using makecert do not support CRLs.
If you have a spare machine you could use Windows Certificate Services (free with Server 2003 and 2008). This provides a CA and you can request certificates (SSL or client) from it. It needs to be a spare machine as it sets itself up under the default web site and completely messes up if you have already tweaked that. It also publishes CRLs. All you would need to do is to install the root certificate for the CA on your development boxes and away you go.

You have the option to either generate a certificate to use in development, or disabling the use of certificates through the configuration file. I would recommend actually using a certificate also in development.

Extending Leon Breedt's answer, to generate the in-memory x509 certifcate you can use the source code from Keith Elder's SelfCert.
using (CryptContext ctx = new CryptContext())
{
ctx.Open();
var cert = ctx.CreateSelfSignedCertificate(
new SelfSignedCertProperties
{
IsPrivateKeyExportable = true,
KeyBitLength = 4096,
Name = new X500DistinguishedName("cn=InMemoryTestCert"),
ValidFrom = DateTime.Today.AddDays(-1),
ValidTo = DateTime.Today.AddYears(5),
});
var creds = new ServiceCredentials();
creds.UserNameAuthentication.CustomUserNamePasswordValidator = new MyUserNamePasswordValidator();
creds.ServiceCertificate.Certificate = cert;
serviceHost.Description.Behaviors.Add(creds);
}

How about changing the configuration between development and production?

My suggestion would be to consider a couple of different approaches:
For development -> There are ways to generate an SSL certificate locally so that tests with https can be done in an environment that you have total control over.
For "beta" testing -> Consider getting a second certificate for this as there may be a continuous need to do some beta testing between releases so it likely can be used over and over again.

Related

Identity Two-Factor Authentication alternatives

One of my clients did not want to use any of the standard options (SMS or Email) for 2FA and I was wondering what others have implemented instead.
I felt that the site would be too vulnerable with just a username and password combination, even using max-attempts and timeouts.
A simple option that multiplies up the login uncertainty is by adding an additional security question as part of the login page.
My answer is posted below
Using the code-first approach in creating the user database, I added a set of security questions into my IdentityDbContext class.
public DbSet<SecurityQuestion> SecurityQuestions { get; set; }
This provides a simple list of questions such as "What is your favourite food". the questions should engender reasonably generic answers. The questions are added in the Configuration class Seed method
void AddSecurityQuestion(ApplicationDbContext db, string question)
{
db.SecurityQuestions.Add(new SecurityQuestion() { Question = question });
}
A simple table is sufficient
public class SecurityQuestion
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(128)]
[DisplayName("Question")]
public string Question { get; set; }
}
A field as added to the Identity User class. This will contain either null or a hash of a security question and answer. For completeness, a property is added to check if the hash is present. The first time a user logs on, the hash is saved. On subsequent logons, the hash is checked
public string SecurityQuestion { get; protected set; }
[NotMapped]
public bool HasSecurityQuestion
{
get
{
return this.SecurityQuestion != null;
}
}
Hashing uses the same code as the internal Identity methods and stores the seed and hash in the same string
public static string HashSecurityQuestion(string question, string answer)
{
if (question == null)
{
throw new ArgumentNullException("Question is null");
}
if (answer == null)
{
throw new ArgumentNullException("Answer is null");
}
string questionAndAnswer = question + "_" + answer;
// random salt and hash in save result
byte[] salt;
byte[] buffer2;
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(questionAndAnswer, 0x10, 0x3e8))
{
salt = bytes.Salt;
buffer2 = bytes.GetBytes(0x20);
}
byte[] dst = new byte[0x31];
Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
return Convert.ToBase64String(dst);
}
A verification method is required
public static bool VerifyHashedPassword(string hashedSecurityQuestion, string question, string answer)
{
if (hashedSecurityQuestion == null)
{
return false;
}
if (question == null)
{
throw new ArgumentNullException("Question is null");
}
if (answer == null)
{
throw new ArgumentNullException("Answer is null");
}
string questionAndAnswer = question + "_" + answer;
// has to retrieve salt
byte[] buffer4;
byte[] src = Convert.FromBase64String(hashedSecurityQuestion);
if ((src.Length != 0x31) || (src[0] != 0))
{
return false;
}
byte[] dst = new byte[0x10];
Buffer.BlockCopy(src, 1, dst, 0, 0x10);
byte[] buffer3 = new byte[0x20];
Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(questionAndAnswer, dst, 0x3e8))
{
buffer4 = bytes.GetBytes(0x20);
}
return buffer3.SequenceEqual(buffer4);
}
In the login process, there is one extra step to verify the security question and answer are checked. The MVC View displays a drop down of questions and a textbox for an answer, both values for which are in the view model
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: true);
// if the user login is a success, check if a security question exists
if (result == SignInStatus.Success && user.HasSecurityQuestion)
{
// security question exists, so check it
if (!user.VerifySecrityQuestion(model.SecurityQuestion, model.SecurityQuestionAnswer))
{
result = SignInStatus.Failure;
}
}
Authy/Twilio Developer Evangelist here. There are a couple other options for additional security that you have:
Enforce & encourage strong passwords
This includes things like minimum length, showing a password "strength" indicator, and including easy ways for people to use a password manager.
I compiled more details about recommendations for strong passwords:
https://github.com/robinske/betterpasswords
One Time Passwords in the form of TOTP
This is what you'd see with apps like Authy or Google Authenticator. TOTP (time-based one time passwords) is a standard, you can read about that here.
Authy has APIs to implement OTPs here.
Push Authentication
This is another form of 2FA that allows your user to "approve" or "deny" a login request in the form of a push notification. This is the most secure form of 2FA with a seamless user experience, you can read more about how Authy does that here.
Authy has APIs to implement push authentication here.
========
Note: security questions are a lot like additional passwords that can be more easily Googled, so I'd encourage your client to think about using a true second factor.

How to migrate SimpleMembership user data to ASPNET Core Identity

I have an application that I originally built when I was young and foolish and its authentication is set up using the SimpleMembership framework, with all user data contained in a webpages_Membership table. I am very interested in rebuilding my backend as an AspNetCore Web API with AspNetCore Identity via SQL Server, without losing user information.
I've had good luck with coming up with SQL scripts to move everything into an AspNetUsers table in preparation for working with Identity instead of SimpleMembership, but where I'm running into an issue is password hashing. I gather from articles like this and this that my best bet is to override PasswordHasher<IdentityUser> to duplicate the SimpleMembership crypto flow, and then rehash passwords as they come in to gradually migrate the database.
The trouble is I can't find out how to achieve this flow duplication in .NET Core. The latter article linked above states that the SimpleMembership flow is achieved via the System.Web.Helpers.Crypto package, which does not appear to exist in the .NET Core library, and I can't figure out if its implementation is documented anywhere. (Its MSDN documentation says that it is using RFC2898 hashing but I don't know enough about crypto to know if that's enough to go on by itself. This isn't my area of expertise. :( )
Any insight on how to approach this would be much appreciated. Thank you!
For anyone else who may be running into the same trouble -- I was able to find a copy of the System.Web.Helpers.Crypto code somewhere on GitHub, and more or less copied it into a custom password hasher class thus:
public class CustomPasswordHasher : PasswordHasher<IdentityUser>
{
public override PasswordVerificationResult VerifyHashedPassword(IdentityUser user, string hashedPassword,
string providedPassword)
{
var isValidPasswordWithLegacyHash = VerifyHashedPassword(hashedPassword, providedPassword);
return isValidPasswordWithLegacyHash
? PasswordVerificationResult.SuccessRehashNeeded
: base.VerifyHashedPassword(user, hashedPassword, providedPassword);
}
private const int _pbkdf2IterCount = 1000;
private const int _pbkdf2SubkeyLength = 256 / 8;
private const int _saltSize = 128 / 8;
public static bool VerifyHashedPassword(string hashedPassword, string password)
{
//Checks password using legacy hashing from System.Web.Helpers.Crypto
var hashedPasswordBytes = Convert.FromBase64String(hashedPassword);
if (hashedPasswordBytes.Length != (1 + _saltSize + _pbkdf2SubkeyLength) || hashedPasswordBytes[0] != 0x00)
{
return false;
}
var salt = new byte[_saltSize];
Buffer.BlockCopy(hashedPasswordBytes, 1, salt, 0, _saltSize);
var storedSubkey = new byte[_pbkdf2SubkeyLength];
Buffer.BlockCopy(hashedPasswordBytes, 1 + _saltSize, storedSubkey, 0, _pbkdf2SubkeyLength);
byte[] generatedSubkey;
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, _pbkdf2IterCount))
{
generatedSubkey = deriveBytes.GetBytes(_pbkdf2SubkeyLength);
}
return ByteArraysEqual(storedSubkey, generatedSubkey);
}
internal static string BinaryToHex(byte[] data)
{
var hex = new char[data.Length * 2];
for (var iter = 0; iter < data.Length; iter++)
{
var hexChar = (byte) (data[iter] >> 4);
hex[iter * 2] = (char) (hexChar > 9 ? hexChar + 0x37 : hexChar + 0x30);
hexChar = (byte) (data[iter] & 0xF);
hex[iter * 2 + 1] = (char) (hexChar > 9 ? hexChar + 0x37 : hexChar + 0x30);
}
return new string(hex);
}
[MethodImpl(MethodImplOptions.NoOptimization)]
private static bool ByteArraysEqual(byte[] a, byte[] b)
{
if (ReferenceEquals(a, b))
{
return true;
}
if (a == null || b == null || a.Length != b.Length)
{
return false;
}
var areSame = true;
for (var i = 0; i < a.Length; i++)
{
areSame &= (a[i] == b[i]);
}
return areSame;
}
}
This class overrides VerifyHashedPassword and checks whether the user's provided password works with the old Crypto hashing; if so, the method returns PasswordVerificationResult.SuccessRehashNeeded. Otherwise, it passes the password off to the base class's method and verifies it as normal with the .NET Core hashing behavior.
You can then instruct your UserManager to use this password hasher instead of the default by including it in your dependency injection configuration in Startup.cs:
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IPasswordHasher<IdentityUser>, CustomPasswordHasher>();
}
...
}
My eventual intention is to have my controller trigger a rehash of the user's password when that SuccessRehashNeeded result is returned, allowing a gradual migration of all users to the correct hashing schema.

How to simulate large number of clients using UCMA, for load testing?

I have created application using Lync client side SDK 2013 and UCMA 4.0 . Now I test my application with large number of users. How can I simulate large number of client using UCMA or Lync client side SDK?
It depends on what exactly what you want to "simulate".
If you just want call traffic there is sipp, but that is just simple sip calls and doesn't really reflect an actual Microsoft Lync Client.
As far as I know, Microsoft doesn't provide any load testing tools in Lync. You will have to generate them yourself base on what exactly you want to "simulate".
With a UCMA trusted application, you should be able to startup and use a large number of user endpoints to "simulate" common lync services (like randomly changing presence, making calls, send IM's, etc). You would have to create such an app yourself.
I created a tool in UCMA to do my stress test for all my applications than I have made.
It is simple to make, and it is composed of two parts.
This example is a stress tester for calls. Of course, you can easily make a different one by using this example.
We create our platform, follow our Set-CsTrustedApplication.
var platformSettings = new ProvisionedApplicationPlatformSettings("InnixiTester", "urn:application:innixitester");
var collabPlatform = new CollaborationPlatform(platformSettings);
collabPlatform.EndStartup(collabPlatform.BeginStartup(null, null));
Ok, I know what I am doing here is a wrong chaining together, the Begin and the End into one line of code. However, this is just a code exemple. I invite you to read the article of Tom Morgan, he explains why it is not good to do it like me.
We use here a Parallel loop to create all our users-endpoint. In that way, it goes faster.
/*
* Proprieties of the class
*/
private AutoResetEvent _waitForStressTestToFinish = new AutoResetEvent(false);
private List<UserEndpoint> _listUserEndpoints = new List<UserEndpoint>();
private int _maxUsers = 200;
private int _tickTotal;
private int _tickCount;
private int _nbrCallsByIntervall;
/*
* End
*/
_maxUsers = 200; // Nbr max of users
const var callsTotal = 200; // Nbr of total call
const var timeToTest = 30; // Total time to test
const var intervalOfCalls = 5; // We want to make our calls between specific intervals
Parallel.For(0, _maxUsers, i =>
{
CreateUserEndpoint(collabPlatform, i.ToString());
});
You simply create your UserEndpoint here. The scenario is that my users in the active directory are stressuser0 to stressuser200. With extension starting from +14250 to +1425200
private void CreateUserEndpoint(CollaborationPlatform cp, string iteration)
{
try
{
UserEndpointSettings settingsUser = new UserEndpointSettings($"sip:stressuser{iteration}#pferde.net", "pool2010.pferde.net", 5061);
settingsUser = InitializePublishAlwaysOnlineSettings(settingsUser);
var userEndpoint = new UserEndpoint(cp, settingsUser);
userEndpoint.EndEstablish(userEndpoint.BeginEstablish(null, null));
PublishOnline(userEndpoint);
_listUserEndpoints.Add(userEndpoint);
Console.WriteLine($"The User Endpoint owned by URI: {userEndpoint.OwnerUri} was created\n");
}
catch (Exception)
{
Console.WriteLine($"failed to create for --> sip:stressuser{iteration}#pferde.net");
throw;
}
}
private UserEndpointSettings InitializePublishAlwaysOnlineSettings(UserEndpointSettings settings)
{
settings.AutomaticPresencePublicationEnabled = true;
settings.Presence.PreferredServiceCapabilities.AudioSupport = CapabilitySupport.Supported;
return (settings);
}
Now time to place the calls! We are going to code a simple algorithm with a timer. Is going to calculate how many calls it needs to make for X time and for Y Calls and for Z intervals.
Console.WriteLine("Tape a key to place calls...");
Console.ReadKey();
PlaceCalls(callsTotal, timeToTest, intervalOfCalls);
_waitForStressTestToFinish.WaitOne();
}
catch (Exception ex)
{
Console.WriteLine($"Shutting down platform due to error {ex}");
ShutdownPlatform(collabPlatform);
}
ShutdownPlatform(collabPlatform);
}
private void PlaceCalls(int callsMax, int timeMax, int timeIntervall)
{
_tickTotal = timeMax / timeIntervall;
_nbrCallsByIntervall= callsMax / _tickTotal;
Console.WriteLine($"_nbrCallsByIntervall --> {_nbrCallsByIntervall}");
var timeIntervalTimespan = new TimeSpan(0, 0, 0, timeIntervall);
_timer = new Timer(timeIntervalTimespan.TotalMilliseconds);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true;
}
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (_tickCount < _tickTotal)
{
Console.WriteLine($"\n Pause Timer | On {_tickCount} to {_tickTotal}\n");
_timer.Enabled = false;
for (var i = 0; i <= _nbrCallsByIntervall - 1; ++i)
{
ConversationSettings convSettings = new ConversationSettings();
Conversation conversation = new Conversation(_listUserEndpoints[generateNumber(0, _listUserEndpoints.Count)], convSettings);
var audioVideoCall = new AudioVideoCall(conversation);
CallEstablishOptions options = new CallEstablishOptions();
var gNbr = generateNumber(0, _listUserEndpoints.Count);
try
{
// Here I'm calling a single phone number. You can use GenerateNumber to call stressusers each others. But you have to extend your code to accept the calls coming.
audioVideoCall.BeginEstablish($"3322", options, null, audioVideoCall);
}
catch (Exception)
{
Console.WriteLine("Fail to Call the remote user...");
throw;
}
Console.WriteLine($"Call--> +1425{gNbr}.Counter--> {_tickCount} Ticket--> {_tickTotal} and thread id {Thread.CurrentThread.ManagedThreadId}");
}
_tickCount++;
_timer.Enabled = true;
Console.WriteLine("\n reStart Timer \n");
}
else
{
Console.WriteLine("\n!!! END Stress test !!!\n");
_timer.Enabled = false;
_waitForCallToEstablish.Set();
}
}
private int generateNumber(int min, int max)
{
var r = new Random();
Thread.Sleep(200);
return (r.Next(min, max));
}

Get Unity WWW response status code

I'm working with Unity WWW for some Rest API request. But it don't support to get response status (only return text and error). Any solution for it? Thanks!
edit: Since the time I asked this question, Unity released a new framework for HTTP communications called UnityWebRequest. It's much more modern than WWW, and provides definitive access to the Response Code, as well as more flexibility around headers, HTTP verbs, etc. You should probably use that instead of WWW.
apparently you need to parse it from the response headers yourself.
this seems to do the trick:
public static int getResponseCode(WWW request) {
int ret = 0;
if (request.responseHeaders == null) {
Debug.LogError("no response headers.");
}
else {
if (!request.responseHeaders.ContainsKey("STATUS")) {
Debug.LogError("response headers has no STATUS.");
}
else {
ret = parseResponseCode(request.responseHeaders["STATUS"]);
}
}
return ret;
}
public static int parseResponseCode(string statusLine) {
int ret = 0;
string[] components = statusLine.Split(' ');
if (components.Length < 3) {
Debug.LogError("invalid response status: " + statusLine);
}
else {
if (!int.TryParse(components[1], out ret)) {
Debug.LogError("invalid response code: " + components[1]);
}
}
return ret;
}

Getting client certificate in restlet

I managed to implement an https restlet with bot client and server certificated. I can prove it works since if I call the server with an untrusted certification communication fails. Unfortunately I can't find the certificate of the client on the server. I'm using this code:
List<Certificate> certs = request.getClientInfo().getCertificates();
but list is empty. What I'm doing wrong?
EDIT:
version is Restlet-Framework/2.3m2
The problem is related to the use of the default server implementation via com.sun.httpserver.
The class org.restlet.engine.connector.HttpExchangeCall should return the certificates in the getCertificates() method,
but it always returns null. This class is used in org.restlet.engine.connector.HttpsServerHelper
which in turn is the helper for the Restlet framework when using the server implementation com.sun.httpserver.
To fix this, a couple of things are needed.
First, a new class HttpsExchangeCall:
package org.restlet.engine.connector;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import org.restlet.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpsExchange;
/**
* The default {#link HttpExchangeCall} fails to extract certificates from the SSL connection.
* This class implements {#link #getCertificates()} to extract certificates.
*/
#SuppressWarnings("restriction")
public class HttpsExchangeCall extends HttpExchangeCall {
private static final Logger log = LoggerFactory.getLogger(HttpsExchangeCall.class);
private final HttpsExchange sexchange;
public HttpsExchangeCall(Server server, HttpExchange exchange) {
this(server, exchange, true);
}
public HttpsExchangeCall(Server server, HttpExchange exchange, boolean confidential) {
super(server, exchange, confidential);
if (exchange instanceof HttpsExchange) {
sexchange = (HttpsExchange) exchange;
} else {
sexchange = null;
}
}
#Override
public List<Certificate> getCertificates() {
if (sexchange == null) {
log.debug("Cannot extract peer certificates from unsecure connection.");
return null;
}
Certificate[] certs = null;
try {
certs = sexchange.getSSLSession().getPeerCertificates();
if (log.isDebugEnabled()) {
log.debug("Found " + (certs == null ? "no" : Integer.toString(certs.length)) + " peer certificate(s).");
}
} catch (Exception e) {
log.debug("Unable to find peer certificates - " + e);
}
List<Certificate> lcerts = null;
if (certs != null) {
lcerts = new ArrayList<Certificate>();
for (int i = 0; i < certs.length; i++) {
lcerts.add(certs[i]);
}
}
return lcerts;
}
}
Then a copy of HttpsServerHelper
renamed to HttpsServerHelper2 with one line modified. Replace the line
HttpsServerHelper.this.handle(new HttpExchangeCall(getHelped(),
with the line:
HttpsServerHelper2.this.handle(new HttpsExchangeCall(getHelped(),
This helper needs to be registered:
Engine.getInstance().getRegisteredServers().add(new HttpsServerHelper2(null));
and creating a Server now becomes very explicit:
Component component = new Component();
Server server = new Server(
(Context) null, Arrays.asList(Protocol.HTTPS),
(String) null, Constants.PORT_TEST, component.getServers().getNext(),
HttpsServerHelper2.class.getName()
);
component.getServers().add(server);
I'm hoping Restlet's own HttpExchangeCall will be updated to extract the certificates:
it is a minor fix and saves a lot of unneeded code required to work around the issue.
In the mean time, you can find all the source code (using Restlet 2.3.4) and a working example in the
restlet-clientcert Github project.
This method should provide what you are looking for: request.getClientInfo().getCertificates()
http://restlet.com/learn/javadocs/2.3/jse/api/org/restlet/data/ClientInfo.html
You can also retrieve the cipher suites