I have made some code that allows me to do cryptography in c# - using primarily AesManaged() and SHA256Managed() in System.Security.Cryptography.
The use case is that the tool needs to be able to pull off an encrypted piece of data, decrypt it, display it to the user, allow for editing and encrypt again before sending it back again.
I would like to be able to do similar on Windows Phone, but it seems that the namespace is not available on the phone.
So what are my options now? Will it be available on Windows Phone 10? It seems that doing crypto-stuff would be a relatively common task in a phone app?
Edit: added information about what the app should do
What are you trying to do with the cryptography?
Because if you just need to store some user credentials, best way is using the PasswordVault
MSDN reference here https://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.security.credentials.passwordvault.aspx
I've made an example of this here
http://depblog.weblogs.us/2014/11/20/migrating-from-sl8-0-protectdata-to-rt8-1-passwordvault/
Added some example code on how to add and remove entries from the Vault ( more details on the blog post )
public async Task AddAccount(Account accountToAdd)
{
//Reinitialize the vault to see if the given account is already available
await this.InitializeSettingsService();
Account accountFromVault = this.Accounts.FirstOrDefault(item => item.UserName.Equals(accountToAdd.UserName, StringComparison.OrdinalIgnoreCase));
if(accountFromVault == null)
_vault.Add(new PasswordCredential(Constants.VAULTRESOURCENAME, accountToAdd.UserName, accountToAdd.Password));
if (accountFromVault != null && !accountFromVault.Password.Equals(accountToAdd.Password, StringComparison.Ordinal))
{
_vault.Remove(new PasswordCredential(Constants.VAULTRESOURCENAME, accountFromVault.UserName, accountFromVault.Password));
_vault.Add(new PasswordCredential(Constants.VAULTRESOURCENAME, accountToAdd.UserName, accountToAdd.Password));
}
Account accountFromMemory = this.Accounts.FirstOrDefault(item => item.UserName.Equals(accountToAdd.UserName, StringComparison.OrdinalIgnoreCase));
if (accountFromMemory != null)
{
if (!accountFromMemory.Password.Equals(accountToAdd.Password, StringComparison.OrdinalIgnoreCase))
{
this.Accounts.Remove(accountFromMemory);
this.Accounts.Add(accountToAdd);
}
}
else
this.Accounts.Add(accountToAdd);
}
public async Task RemoveAccount(Account accountToRemove)
{
//Reinitialize the vault to see if the given account is already available
await this.InitializeSettingsService();
Account accountFromVault = this.Accounts.FirstOrDefault(item => item.UserName.Equals(accountToRemove.UserName, StringComparison.OrdinalIgnoreCase));
if (accountFromVault != null)
_vault.Remove(new PasswordCredential(Constants.VAULTRESOURCENAME, accountToRemove.UserName, accountToRemove.Password));
Account accountFromMemory = this.Accounts.FirstOrDefault(item => item.UserName.Equals(accountToRemove.UserName, StringComparison.OrdinalIgnoreCase));
if (accountFromMemory != null)
this.Accounts.Remove(accountFromMemory);
}
As #WDS noted, the tools for doing cryptography is located in Windows.Security.Cryptography namespace - available on #WP8 .
So I rewrote my hash-implementation like this:
public IBuffer ComputeHash(string value)
{
IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(value, BinaryStringEncoding.Utf8);
var objAlgProv = HashAlgorithmProvider.OpenAlgorithm("SHA256");
var strAlgNameUsed = objAlgProv.AlgorithmName;
var buffHash = objAlgProv.HashData(buffUtf8Msg);
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
return buffHash;
}
Examples of how to do encryption and decryption can be found here:
https://msdn.microsoft.com/en-us/library/windows/apps/windows.security.cryptography.core.cryptographicengine.aspx
Related
We have an iOS app that is built in different platform(swift,xamarin). We have a requirements that required those app to share variables using keychain. The keychain sharing is working for swift to swift App but for swift to Xamarin App it is not working. Possible something wrong with my code. I don't see much documentation in Xamarin on how to access keychain.
SecStatusCode res;
var rec = new SecRecord(SecKind.GenericPassword)
{
Generic = NSData.FromString("sharedKey"),
AccessGroup = ""
};
var match = SecKeyChain.QueryAsRecord(rec, out res);
if (match != null)
{
var dt = match.ValueData.ToString();
return dt;
}
This might be caused by your Xamarin app not being access the keychain.
I don't know if you have this implemented, but if this is missing, try to Enable Keychain Access in your Xamarion iOS application.
I figure out my issue.
SecStatusCode res;
var rec = new SecRecord(SecKind.GenericPassword)
{
AccessGroup = "Shared access group example MC123.com.test.shared",
Account = "SharedKey that you are looking for"
};
var match = SecKeyChain.QueryAsRecord(rec, out res);
if (match != null)
{
var dt = match.ValueData.ToString();
return dt;
}
Nice framework... but I am trying to establish a connection to an available network. I have connected manually, and made a snapshot of the XML. Then removed the connection.
I am able to find the network,
I pass in the SSID I want to connect to (yes, it is found..). I also pass the profileSecurity to use... but not really sure what to put there. From my XML I tried bot AES as well as WPA2PSK. But, SetProfile always returns false. And not sure how to extract an error message about what went wrong. That would be nice.
As the profile fails, the ConnectNetwork also fails, but that would be expected I think.
Any pointer would be nice. And a bit more documentation on these points would make the tool a lot better.
// get the network to connect to
var availableNetwork = NativeWifi.EnumerateAvailableNetworks()
.FirstOrDefault(x => x.Ssid.ToString() == ssid);
if (availableNetwork is null)
return;
var profile = NativeWifi.EnumerateProfiles().FirstOrDefault(x => x.Name == ssid);
if (profile is null)
{
// build XML
string profileName = ssid;
string mac = StringToHex(profileName);
string profileXml = string.Format("<?xml version=\"1.0\"?><WLANProfile xmlns = \"http://www.microsoft.com/networking/WLAN/profile/v1\"><name>{0}</name><SSIDConfig><SSID><hex>{1}</hex><name>{0}</name></SSID></SSIDConfig><connectionType>ESS</connectionType><connectionMode>auto</connectionMode><MSM><security><authEncryption><authentication>WPA2PSK</authentication><encryption>AES</encryption><useOneX>false</useOneX></authEncryption><sharedKey><keyType>passPhrase</keyType><protected>true</protected><keyMaterial>... key removed for security...</keyMaterial></sharedKey></security></MSM><MacRandomization xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v3\"><enableRandomization>false</enableRandomization><randomizationSeed>153878511</randomizationSeed></MacRandomization></WLANProfile>", ssid, mac);
// create a profile
var profileResult = NativeWifi.SetProfile(availableNetwork.Interface.Id, ProfileType.AllUser, profileXml, encryption, true);
}
else
{
//todo: log here
}
var wasConnected = NativeWifi.ConnectNetwork(availableNetwork.Interface.Id, ssid, availableNetwork.BssType);
I have spent several hours trying to have a valid Encrypt/Decrypt function that uses Asymmetric encryption, and the best option seems to be RSA.
But The thing is that I want to be able to provide my own public/private key to the function as a string myself in the form : "769de1f1a9dd6e114f81b9490ea42a2967840353edd358a35c84e2c831dd40a2"
something very very similar to the 'eth-crypto' npm library for javascript.
but I haven't found any article or documentation that explains that.
so has anyone implemented it before or have an article that explains it.
keep in mind that when using asp.net core the FromXmlString doesn't work even when using 3.0
Here is my Encrypt function so far :
public static string EncryptAsymmetric(string textToEncrypt, string publicKeyString)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(textToEncrypt);
using (var rsa = new RSACryptoServiceProvider(2048))
{
try
{
rsa.FromXmlString(publicKeyString);
var encryptedData = rsa.Encrypt(bytesToEncrypt, true);
var base64Encrypted = Convert.ToBase64String(encryptedData);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
but it throws an error on the FromXmlString ( operation not supported on this platform )
so if there is any other way maybe......
any help is very appreciated because I have been looking into it for many hours with no result.
When I access X509Certificate2.PublicKey or X509Certificate2.PrivateKey initialized from an object that was generated with BouncyCastle, I'm getting my domain account locked out (if I do it multiple times). It happens if I run the program on behalf of a local account with the same name but different password. Here is the code:
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System.IO;
using System.Security.Cryptography.X509Certificates;
namespace TestCertificateConversion
{
class Program
{
static void Main(string[] args)
{
var certString = GetCertificateString();
var textReader = new StringReader(certString);
var pemReader = new PemReader(textReader);
var bcCert = pemReader.ReadObject() as Org.BouncyCastle.X509.X509Certificate;
var netCert = DotNetUtilities.ToX509Certificate(bcCert);
var netCert2 = new X509Certificate2(netCert);
var publicKey = netCert2.PublicKey; // this locks out domain account
}
private static string GetCertificateString()
{
return #"-----BEGIN CERTIFICATE-----
MIICvDCCAaSgAwIBAgIQANDHl0sFjYUG3j76dYTadzANBgkqhkiG9w0BAQsFADAQ
MQ4wDAYDVQQDDAVwYWNlbTAgFw0xNjAxMDExMjQ4MzdaGA8yMjAwMDEwMTIyNTg0
N1owEDEOMAwGA1UEAwwFcGFjZW0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC5AKAkYnerRUmeAX0Z+aZsX39LXTVZiUd8U6waw7Hzd9E0YA50tHWizfEI
w7IBZwXS0aiXwHqJvrslc3NNs0grwu/iYQl+FGdudKmgXVE7Riu0uFAHo6eFr0dO
o0IP3LS+dPSWarXEBLbymaXRiPJDyHLefvslcSM9UQ2BHOe7dnHh9K1h+XMKTw3Z
/3szAyABBX9hsJU/mc9XjrMNXHJXALSxTfLIPzDrfh+aJtlopWpnb6vQcXwKksyk
4hyVUfw1klhglJgN0LgBGU7Ps3oxCbOqns7fB+tzkBV1E5Q97otgvMR14qLZgc8k
NQrdMl57GaWQJl6mAP1NR1gZt2f1AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ
KoZIhvcNAQELBQADggEBAAEz3vJOfqao9QXPWSz8YCjbWG1FeVG0NdYpd422dC2V
Vrzlo5zrkRv5XPhBOY3o81OhUe7iByiiM9suYfXLNxxd29TBGB5amO8Yv1ZX0hS/
zvVF6QS0+zZvOiujVhfHGiJxKypqgaigI6NM80ZDKPzsRPwFLIJiZYwQ7eQUlrpt
WGgFkZC23/mSOkY6VMmO5zugeMoiXRyFq33uWLlaAr+zJtRh1IPRmkA1lJv0bkC1
SslO0oSDoT2lcvZkQ5odFKX5i1z7T/wioQqG62i8nsDSz+iZOqUyDx7bL8fIEHog
qgwizgr2aAPLO/VQKU9pRTyRNFl/GL5bi7w8NN+rLxE=
-----END CERTIFICATE-----";
}
}
}
I'm not sure what I'm doing wrong, are there any security settings I might need to change to prevent it from locking out domain accounts?
Can you check and confirm if the service account is coming in this format
I checked the .net source code and found what causes an authentication problem in X509Certificate2.PublicKey. It is a creation of a new OID object:
public PublicKey PublicKey {
[SecuritySafeCritical]
get {
if (m_safeCertContext.IsInvalid)
throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidHandle), "m_safeCertContext");
if (m_publicKey == null) {
string friendlyName = this.GetKeyAlgorithm();
byte[] parameters = this.GetKeyAlgorithmParameters();
byte[] keyValue = this.GetPublicKey();
Oid oid = new Oid(friendlyName, OidGroup.PublicKeyAlgorithm, true); // this line
m_publicKey = new PublicKey(oid, new AsnEncodedData(oid, parameters), new AsnEncodedData(oid, keyValue));
}
return m_publicKey;
}
}
The OID constructor is called with lookupFriendlyName set to 'true', which leads to FindOidInfoWithFallback function:
// Try to find OID info within a specific group, and if that doesn't work fall back to all
// groups for compatibility with previous frameworks
internal static string FindOidInfoWithFallback(uint key, string value, OidGroup group)
{
string info = FindOidInfo(key, value, group);
// If we couldn't find it in the requested group, then try again in all groups
if (info == null && group != OidGroup.All)
{
info = FindOidInfo(key, value, OidGroup.All);
}
return info;
}
The first FindOidInfo returns null and then it is called second time with OidGroup.All. Eventually it results in cryptAPI call:
CAPIMethods.CryptFindOIDInfo(dwKeyType, pvKey, dwGroupId);
From documentation:
The CryptFindOIDInfo function performs a lookup in the active
directory to retrieve the friendly names of OIDs under the following
conditions:
The key type in the dwKeyType parameter is set to CRYPT_OID_INFO_OID_KEY or CRYPT_OID_INFO_NAME_KEY.
No group identifier is specified in the dwGroupId parameter or the GroupID refers to EKU OIDs, policy OIDs or template OIDs.
It then attempts to authentication with local user account and as a result I'm getting my domain account locked. From the comments to the code I see that the second FindOidInfo call was added for compatibility with older frameworks and potentially I can remove it. Unfortunately there is no easy was to change the code since it is in the framework itself. I may try to inherit the X509Certificate2 object and rewrite PublicKey and PrivateKey, but I don't really like that idea.
I'm trying to make a console application which accesses my SkyDrive account, however I cannot figure out how to get the Live SDK working.
I'm running on Live SDK version 5.4 and this is the code I'm trying to run - the loginResult.Status is always "Unknown":
private static async Task<LiveConnectClient> ConnectToLive()
{
LiveAuthClient authClient = new LiveAuthClient("my live ID");
var loginResult = await authClient.IntializeAsync(new[] { "wl.basic" });
if (loginResult.Status == LiveConnectSessionStatus.Connected)
return new LiveConnectClient(loginResult.Session);
return null;
}
A few things I'm not certain about (since the SDK documentation is somewhat lackluster at best):
"My live ID" - is this just my e-mail address used for my personal Live account, or is it some sort of application specific ID that you have to create somewhere ?
Is InitializeAsync the proper method to call for authenticating ? All examples I've found mention a "LoginAsync", but that method is not available in the DLL.
Is it even possible to use the SDK outside of Windows Phone / Metro apps ?
I got the following code to work using a SkyDriveClient downloaded from http://skydriveapiclient.codeplex.com/releases/view/103081
static void Main(string[] args)
{
var client = new SkyDriveServiceClient();
client.LogOn("YourEmail#hotmail.com", "password");
WebFolderInfo wfInfo = new WebFolderInfo();
WebFolderInfo[] wfInfoArray = client.ListRootWebFolders();
wfInfo = wfInfoArray[0];
client.Timeout = 1000000000;
string fn = #"test.txt";
if (File.Exists(fn))
{
client.UploadWebFile(fn, wfInfo);
}
}