I have the following function I use to create a symmetric key. This code is used both on a Windows 10 UWP app and inside a portable library with profile7.
public bool Initialize( string securityKey ) {
byte[] saltBytes = new byte[256];
byte[] codeBytes = Encoding.UTF8.GetBytes( securityKey );
for ( int i = 0; i < 256; i++ ) {
saltBytes[i] = codeBytes[i % codeBytes.Length];
}
IBuffer bufferSalt = CryptographicBuffer.CreateFromByteArray( saltBytes );
_symmetricProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm( SymmetricAlgorithmNames.AesCbcPkcs7 );
_serverSessionKey = _symmetricProvider.CreateSymmetricKey( bufferSalt );
int key = _serverSessionKey.GetHashCode();
return true;
}
The issue I'm facing is that the key hash is different if I run the code directly from the app or from an app that uses the portable library. And of course encrypted data from the portable library does not decrypt in the app.
I do not require a really strong mechanism for generating the key, only a way to create the same key both on Windows and Android (that's why I use portable version) from a shared string.
I've already used the available key derivation functions in Windows.Security.Cryptography namespace but still got same issues: generated keys from same seed with same generation parameteres are different on different platforms. The above code was intended to just verify if the CreateSymmetricKey produces same key if supplied with same data and this seems not the case, so I'm trying to find out what's wrong or if there's some other mechanisms I can use to have same result
Any suggestions ?
Related
Deriving shared key using:
C# ECDiffieHellmanCng.DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey)
Kotlin KeyAgreement.generateSecret() followed by KeyAgreement.doPhase(key: Key!, lastPhase: Boolean)
Yields different results using curve "secp384r1".
Kotlin related links point to Kotlin for Android docs due to readability.
Simplified driver code to demonstrate the problem, assuming that C# .NET 7.0.1 console application is "Server" and Kotlin OpenJDK 19.0.1 application is "Client":
C#:
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
var listener = new TcpListener(IPAddress.Any, 13000);
listener.Start();
using var client = await listener.AcceptTcpClientAsync();
var sharedKey = await GetSharedKey(client, CancellationToken.None);
async Task<byte[]> GetSharedKey(TcpClient client, CancellationToken token)
{
//Generate ECDH key pair using secp384r1 curve
var ecdh = new ECDiffieHellmanCng(ECCurve.CreateFromFriendlyName("secp384r1"));
var publicKeyBytes = ecdh.ExportSubjectPublicKeyInfo();
Console.WriteLine($"Server Public Key: {Convert.ToBase64String(publicKeyBytes)}, " +
$"Length: {publicKeyBytes.Length}");
//Send the generated public key encoded in X.509 to client.
var stream = client.GetStream();
await stream.WriteAsync(publicKeyBytes, token);
//Receive client's public key bytes (X.509 encoding).
var otherPublicKeyBytes = new byte[publicKeyBytes.Length];
await stream.ReadExactlyAsync(otherPublicKeyBytes, 0, otherPublicKeyBytes.Length, token);
//Decode client's public key bytes.
var otherEcdh = new ECDiffieHellmanCng(ECCurve.CreateFromFriendlyName("secp384r1"));
otherEcdh.ImportSubjectPublicKeyInfo(otherPublicKeyBytes, out _);
Console.WriteLine($"Client Public Key: {Convert.ToBase64String(otherEcdh.ExportSubjectPublicKeyInfo())}, " +
$"Length: {otherEcdh.ExportSubjectPublicKeyInfo().Length}");
//Derive shared key.
var sharedKey = ecdh.DeriveKeyMaterial(otherEcdh.PublicKey);
Console.WriteLine($"Shared key: {Convert.ToBase64String(sharedKey)}, " +
$"Length: {sharedKey.Length}");
return sharedKey;
}
Kotlin:
import java.net.Socket
import java.security.KeyFactory
import java.security.KeyPairGenerator
import java.security.spec.ECGenParameterSpec
import java.security.spec.X509EncodedKeySpec
import java.util.*
import javax.crypto.KeyAgreement
fun main(args: Array<String>) {
val socket = Socket("127.0.0.1", 13000)
val sharedKey = getSharedKey(socket)
}
private fun getSharedKey(socket: Socket): ByteArray {
//Generate ECDH key pair using secp384r1 curve
val keyGen = KeyPairGenerator.getInstance("EC")
keyGen.initialize(ECGenParameterSpec("secp384r1"))
val keyPair = keyGen.generateKeyPair()
println("Client Public Key: ${Base64.getEncoder().encodeToString(keyPair.public.encoded)}, Length: ${keyPair.public.encoded.size}")
//Receive server's public key bytes (encoded in X.509)
val input = socket.getInputStream()
val publicKeyBytes = input.readNBytes(keyPair.public.encoded.size)
//Send the generated public key encoded in X.509 to server
val output = socket.getOutputStream()
output.write(keyPair.public.encoded)
// Decode the server's public key
val keySpec = X509EncodedKeySpec(publicKeyBytes)
val keyFactory = KeyFactory.getInstance("EC")
val otherPublicKey = keyFactory.generatePublic(keySpec)
println("Server Public Key: ${Base64.getEncoder().encodeToString(otherPublicKey.encoded)}, Length: ${otherPublicKey.encoded.size}")
// Use KeyAgreement to generate the shared key
val keyAgreement = KeyAgreement.getInstance("ECDH")
keyAgreement.init(keyPair.private)
keyAgreement.doPhase(otherPublicKey, true)
val sharedKey = keyAgreement.generateSecret()
println("Shared key: ${Base64.getEncoder().encodeToString(sharedKey)}, Length: ${sharedKey.size}")
return sharedKey
}
C# output:
Server Public Key: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEqza/eiK23hQIEW5mVdqOc0hAP3tPqittlcvPa6bGdyJK9n64sg0qYyDoPsxJ4pf7ROLz0ACrDS7n/e5Z0J1SMsWpBDViS8NRBvKwa1rQjWdFR0wzRaeVg09LIjnGs4Mj, Length: 120
Client Public Key: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE30zvqkljT4STiE6XfLtoN147WRGA92rz9BLZfbRkOjz7uNbQ3az46DdoyQi6+eON7QVjIf2H5LKBANSk+C5zRX6u8jjrbhURDHYBKgijOddy6mOaEwiADijD/NX72O2L, Length: 120
Shared key: /u+tZYHar4MxXfrn2oqPZAqhiB2pkSTRBZ12rUxdnII=, Length: 32
Kotlin output:
Client Public Key: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE30zvqkljT4STiE6XfLtoN147WRGA92rz9BLZfbRkOjz7uNbQ3az46DdoyQi6+eON7QVjIf2H5LKBANSk+C5zRX6u8jjrbhURDHYBKgijOddy6mOaEwiADijD/NX72O2L, Length: 120
Server Public Key: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEqza/eiK23hQIEW5mVdqOc0hAP3tPqittlcvPa6bGdyJK9n64sg0qYyDoPsxJ4pf7ROLz0ACrDS7n/e5Z0J1SMsWpBDViS8NRBvKwa1rQjWdFR0wzRaeVg09LIjnGs4Mj, Length: 120
Shared key: lErK9DJAutaJ4af7EYWvtEXicAwfSuadtQhlZxug26wGkgB/ce7hF6ihLL87Sqc3, Length: 48
It seems there are no problems with public key import/export, but C# side fails to even produce key of correct length (384 / 8 = 48).
Edit:
Somebody noticed that curiously enough C# "shared key" is Kotlin's shared key's SHA256 hash instead of the actual key.
I strongly suspect it's because of default key derivation function mismatch, but am not completely sure.
I would like to know what am I doing wrong and how to fix the issue.
Edit#2 - Solution:
As the accepted answer suggests - my suspicion is not entirely wrong. ECDiffieHellmanCng.DeriveKeyMaterial does a bit extra unnecessary work - namely returning derived key's SHA256 hash (by default) instead of the actual key and does not provide any means of returning the actual key.
For anyone that is interested in getting 48 byte shared key you will have to be content with it's SHA384 (or some other hashing algorithm) hash instead (or use BouncyCastle):
C# changes:
//Generate ECDH key pair using secp384r1 curve and change default key's hashing algorithm SHA256 to SHA384
var ecdh = new ECDiffieHellmanCng(ECCurve.CreateFromFriendlyName("secp384r1"))
{
HashAlgorithm = CngAlgorithm.Sha384
};
Kotlin changes:
val sharedKey = keyAgreement.generateSecret()
val sharedKeyHash = MessageDigest.getInstance("SHA384").digest(sharedKey)
println("Shared key SHA384 hash: ${Base64.getEncoder().encodeToString(sharedKeyHash)}, Length: ${sharedKeyHash.size}")
return sharedKeyHash
I also suggest to rename the GetSharedKey method to what it actually is - GetSharedKeysSHA384Hash.
The problem is that C# does do more than what is expected from the class. I.e. as usual, the .NET library doesn't adhere to the principle of least surprise:
The ECDiffieHellmanCng class enables two parties to exchange private key material even if they are communicating through a public channel. Both parties can calculate the same secret value, which is referred to as the secret agreement in the managed Diffie-Hellman classes. The secret agreement can then be used for a variety of purposes, including as a symmetric key. However, instead of exposing the secret agreement directly, the ECDiffieHellmanCng class does some post-processing on the agreement before providing the value. This post processing is referred to as the key derivation function (KDF); you can select which KDF you want to use and set its parameters through a set of properties on the instance of the Diffie-Hellman object.
Of course, the tw... developers that created the code don't exactly specify on what they perform the KDF, nor do they specify the default method used from the options that are shown. However, you can expect that they perform it on the X coordinate that is calculated by the Diffie-Hellman key agreement.
That said, it is not very clear from the Java class description either. The standard names document references RFC 3278, which points to the old Sec1 standard, section 6.1 using a broken link. Now Sec1 can still be downloaded, and if we look at section 6.1 we find a construction to encode integers to the a number of bytes that is the field size (and then take the required bytes). What however is returned is undoubtedly the same encoded X-coordinate that is the Input Keying Material to the KDF that Microsoft uses.
Phew, that was a lot of words to say that you have to take the result of the Kotlin code in bytes and then perform the SHA-256 algorithm on it. Oh, yeah, the SHA-256 default was guessed, it's also not specified as far as I can see by Microsoft, although they do expose the KeyDerivationFunction and HashAlgorithm properties and define the defaults for them.
There are some options to choose the parameters for the various KDF functions for ECDiffieHellmanCng, but you seem to be out of luck if you want to have the "raw" X-coordinate. If you want that you may need to use Bouncy Castle for C# but beware that it returns a raw integer for the X-coordinate instead of an encoding of a statically sized, unsigned, big endian integer.
I am fetching my certificate from Azure Key Vault using GetSecretAsync() method and then I am expecting to get the byte[] of the private key and the certificate eventually.
I have my application in .netcore3.1
This is how my code looks like :
var certWithPrivateKey = Client.GetSecretAsync(ConfigurationSettings.AppSettings["AKVEndpoint"], ConfigurationSettings.AppSettings["CertName"]).GetAwaiter().GetResult();
var privateKeyBytes = Convert.FromBase64String(certWithPrivateKey.Value);
X509Certificate2 x509Certificate = new X509Certificate2(privateKeyBytes);
var privateKey = x509Certificate.GetRSAPrivateKey() as RSA;
I get a valid privateKey of type RSACng, but any operation (tried ExportRSAPrivateKey()) on that throws an error of "'privateKey.ExportRSAPrivateKey()' threw an exception of type 'Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException'" and "The requested operation is not supported."
I am not sure how to proceed next here to get the byte[] of the private key and certificate.
Since you do actually seem to need to export: Your current code doesn't load the private key as exportable, so it can't be exported. The fix is to assert exportability:
X509Certificate2 x509Certificate =
new X509Certificate2(privateKeyBytes, "", X509KeyStorageFlags.Exportable);
If that's not enough, then you're encountering the difference between CAPI exportability and CNG exportability (Windows older, and newer, crypto libraries). If the private key from a PFX/PKCS#12 gets loaded into CNG it's only "encrypted exportable", but ExportParameters is plaintext-export.
There's a workaround, though... export it encrypted, then import that somewhere else with a more flexible export policy, then export again.
This snippet uses the .NET Core 3.0+ ExportPkcs8PrivateKey() method, since that's the format you want your data in, and new .NET 5 PemEncoding class to simplify turning the DER encoded output into PEM+DER output. If your exporter is on .NET Framework, this is a more complex problem. For .NET Standard 2.0 there's not really a clean solution (reflect call the methods for .NET Core/.NET 5, otherwise use the Windows-specific version for .NET Framework?).
byte[] pkcs8PrivateKey;
using (RSA privateKey = x509Certificate.GetRSAPrivateKey())
{
pkcs8PrivateKey = ExportPrivateKey(privateKey);
}
File.WriteAllText(
"tls.cer",
new string(PemEncoding.Write("CERTIFICATE", x509Certificate.RawData));
File.WriteAllText(
"tls.key",
new string(PemEncoding.Write("PRIVATE KEY", pkcs8PrivateKey));
...
private static byte[] ExportPrivateKey(RSA privateKey)
{
try
{
// If it's plaintext exportable, just do the easy thing.
return privateKey.ExportPkcs8PrivateKey();
}
catch (CryptographicException)
{
}
using (RSA exportRewriter = RSA.Create())
{
// Only one KDF iteration is being used here since it's immediately being
// imported again. Use more if you're actually exporting encrypted keys.
exportRewriter.ImportEncryptedPkcs8PrivateKey(
"password",
privateKey.ExportEncryptedPkcs8PrivateKey(
"password",
new PbeParameters(
PbeEncryptionAlgorithm.Aes128Cbc,
HashAlgorithmName.SHA256,
1)),
out _);
return exportRewriter.ExportPkcs8PrivateKey();
}
}
I am creating C# API to provide android and iOS mobile application with data, I need to receive and send the data to the mobile applications as encrypted
so I used asymmetric encryption algorithm [RSA], and I have one more question regarding this.
What is the best way to create public and private key and how can I
store them.
Is it write to send the public key as a plain text and is there any
way to send it as .pem file?
what is the public and private key formats used between mobile
application and .net API?
I used this method to create the public and private keys
public static Tuple<string, string> CreateKeyPair()
{
CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024, cspParams);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
return new Tuple<string, string>(privateKey, publicKey);
}
but this function returns Base64String keys and this is not readable in mobile applications.
Please Advice,
I'm trying to encrypt some data in Mono C#, send it to a NodeJS server and decrypt it there. I'm trying to figure out what algorithms to use to match the two.
I send the encrypted string encoded with base64. So I do something like this in Javascript, where I know the key which was used to encrypt the data in my C# application:
var decipher = crypto.createDecipher('aes192',binkey, biniv);
var dec = decipher.update(crypted,'base64','utf8');
dec += decipher.final('utf8');
console.log("dec", dec);
In Mono I create my Cypher with:
using System.Security.Cryptography;
using (Aes aesAlg = Aes.Create("aes192"))
I need to pass the correct string to Aes.Create() in order to have it use the same algorithm, but I can't find what it should be. "aes192" is not correct it seems.
I don't need aes192 this was just a tryout. Suggest a different encryption flavor if it makes sense. Security is not much of an issue.
Here are links to .NET and Nodejs docs:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.aes.aspx
http://nodejs.org/api/crypto.html
This code works for my Node.js side, but please replace the static iv, otherwhise aes encryption would be useless.
var crypto = require('crypto');
function encrypt(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
cipher = crypto.createCipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
cipher.update(data.toString(), 'utf8', 'base64');
return cipher.final('base64');
}
function decipher(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
decipher = crypto.createDecipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
decipher.update(data, 'base64', 'utf8');
return decipher.final('utf8');
}
function str_repeat(input, multiplier) {
var y = '';
while (true) {
if (multiplier & 1) {
y += input;
}
multiplier >>= 1;
if (multiplier) {
input += input;
} else {
break;
}
}
return y;
}
I hope this helps You.
NOTE: You need to deliver an 265bit aka 32 character key for this algorithm to work.
POSSIBLE .NET SOLUTION: This may help you Example
You should simply write new AesManaged().
You don't need to call Create().
You then need to set Key and IV, then call CreateDecryptor() and put it in a CryptoStream.
It turned out to be a stupid mistake. I thought the create function in Node.js could take a variable argument count. Turns out you need to call the createDecipheriv() instead.
Just for the record, you can easily check the padding and mode by looking at those properties in the Aes object. The defaults are CBC and PKCS7. That padding is also used in nodejs crypto. So a for a 128 key size my code to decrypt a base64 encoded string would be:
var crypto = require('crypto');
var binkey = new Buffer(key, 'base64');
var biniv = new Buffer(iv, 'base64');
var decipher = crypto.createDecipheriv('aes-128-cbc', binkey, biniv);
var decrypted = decipher.update(crypted,'base64','utf8');
decrypted += decipher.final('utf8');
console.log("decrypted", decrypted);
I have an issue where I need to be able to have a compiled exe ( .net 3.5 c# ) that I will make copies of to distribute that will need to change a key for example before the exe is sent out.
I cannot compile each time a new exe is needed. This is a thin client that will be used as part of a registration process.
Is it possible to add a entry to a resource file with a blank value then when a request comes in have another application grab the blank default thin client, copy it, populate the blank value with the data needed.
If yes how? If no do you have any ideas? I have been scratching my head for a few days now and the limitation as due to the boundaries I am required to work in.
The other idea I has was to inject the value into a method, which I have no idea how I would even attempt that.
Thanks.
Convert the assembly to IL, do a textual search and replace, recompile the IL to an assembly again. Use the standard tools from the .NET SDK.
Instead of embedding the key in the assembly, put it in the app.config file (or another file delivered with the application) and prevent your application from running if the key is not present and valid. To protect it against modification by users, also add an RSA signature the config file.
This code could be used to generate XML containing your key.
public static void Main()
{
Console.WriteLine(GenerateKey());
}
public static Byte[] Transform(Byte[] bytes, ICryptoTransform xform)
{
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{
using (CryptoStream cstream = new CryptoStream(stream, xform, CryptoStreamMode.Write))
{
cstream.Write(bytes, 0, bytes.Length);
cstream.Close();
stream.Close();
return stream.ToArray();
}
}
}
public static string GenerateKey()
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
// This is the private key and should never be shared.
// Generate your own with RSA.Create().ToXmlString(true).
String rsaPrivateKey = "<RSAKeyValue><Modulus>uPCow37yEzlKQXgbqO9E3enSOXY1MCQB4TMbOZyk9eXmc7kuiCMhJRbrwild0LGO8KE3zci9ETBWVVSJEqUqwtZyfUjvWOLHrf5EmzribtSU2e2hlsNoB2Mu11M0SaGd3qZfYcs2gnEnljfvkDAbCyJhUlxmHeI+35w/nqSCjCk=</Modulus><Exponent>AQAB</Exponent><P>4SMSdNcOP0qAIoT2qzODgyl5yu9RubpIU3sSqky+85ZqJHXLUDjlgqAZvT71ROexJ4tMfMOgSWezHQwKWpz3sw==</P><Q>0krr7cmorhWgwCDG8jmzLMo2jafAy6tQout+1hU0bBKAQaPTGGogPB3hTnFIr84kHcRalCksI6jk4Xx/hiw+sw==</Q><DP>DtR9mb60zIx+xkdV7E8XYaNwx2JeUsqniwA3aYpmpasJ0N8FhoJI9ALRzzp/c4uDiuRNJIbKXyt6i/ZIFFH0qw==</DP><DQ>mGCxlBwLnhkN4ind/qbQriPYY8yqZuo8A9Ggln/G/IhrZyTOUWKU+Pqtx6lOghVdFjSxbapn0W8QalNMFGz7AQ==</DQ><InverseQ>WDYfqefukDvMhPHqS8EBFJFpls/pB1gKsEmTwbJu9fBxN4fZfUFPuTnCIJsrEsnyRfeNTAUFYl3hhlRYZo5GiQ==</InverseQ><D>qB8WvAmWFMW67EM8mdlReI7L7jK4bVf+YXOtJzVwfJ2PXtoUI+wTgH0Su0IRp9sR/0v/x9HZlluj0BR2O33snQCxYI8LIo5NoWhfhkVSv0QFQiDcG5Wnbizz7w2U6pcxEC2xfcoKG4yxFkAmHCIkgs/B9T86PUPSW4ZTXcwDmqU=</D></RSAKeyValue>";
rsa.FromXmlString(rsaPrivateKey);
String signedData = "<SignedData><Key>Insert your key here</Key></SignedData>";
Byte[] licenseData = System.Text.Encoding.UTF8.GetBytes(signedData);
Byte[] sigBytes = rsa.SignData(licenseData, new SHA1CryptoServiceProvider());
String sigText = System.Text.Encoding.UTF8.GetString(Transform(sigBytes, new ToBase64Transform()));
System.Text.StringBuilder sb = new StringBuilder();
using (System.Xml.XmlWriter xw = System.Xml.XmlTextWriter.Create(sb))
{
xw.WriteStartElement("License");
xw.WriteRaw(signedData);
xw.WriteElementString("Signature", sigText);
xw.WriteEndElement();
}
return sb.ToString();
}
Example output from this code:
<?xml version="1.0" encoding="utf-16"?>
<License>
<SignedData>
<Key>Insert your key here</Key>
</SignedData>
<Signature>cgpmyqaDlHFetCZbm/zo14NEcBFZWaQpyHXViuDa3d99AQ5Dw5Ya8C9WCHbTiGfRvaP4nVGyI+ezAAKj287dhHi7l5fQAggUmh9xTfDZ0slRtvYD/wISCcHfYkEhofXUFQKFNItkM9PnOTExZvo75pYPORkvKBF2UpOIIFvEIU=</Signature>
</License>
Then you can use code like this to verify it. You never have to distribute the private key:
public static Boolean CheckLicenseSignature(String licXml)
{
try
{
System.Xml.XmlDocument xd = new System.Xml.XmlDocument();
xd.LoadXml(licXml);
String licSig = xd.SelectSingleNode("/License/Signature").InnerText;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
String rsaPublicKey = "<RSAKeyValue><Modulus>uPCow37yEzlKQXgbqO9E3enSOXY1MCQB4TMbOZyk9eXmc7kuiCMhJRbrwild0LGO8KE3zci9ETBWVVSJEqUqwtZyfUjvWOLHrf5EmzribtSU2e2hlsNoB2Mu11M0SaGd3qZfYcs2gnEnljfvkDAbCyJhUlxmHeI+35w/nqSCjCk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
rsa.FromXmlString(rsaPublicKey);
Byte[] licenseData = System.Text.Encoding.UTF8.GetBytes(xd.SelectSingleNode("/License/SignedData").OuterXml);
return rsa.VerifyData(licenseData, new SHA1CryptoServiceProvider(), Transform(System.Text.Encoding.UTF8.GetBytes(licSig), new FromBase64Transform()));
}
catch (System.Xml.XmlException ex)
{
return false;
}
catch (InvalidOperationException ex)
{
return false;
}
}
From within the capability of the .NET code itself, I'm not sure if this is doable. But it is possible to dynamically generate a .NET DLL which contains some key that can be referred from the main application. That is, if you wouldn't mind a second file in the distribution.
Or if you don't mind to use Ildasm to disassemble the .exe, change the key, then use Ilasm to reassemble, then you can do something to automate that.
The accepted answer is GARBAGE!
I HAVE DONE THIS SUCCESSFULLY. MUCH EASIER
Just put your base application (.net) that needs the key somewhere with a string resource FILLED WITH "XXXXXXXXXXXXXXX" (more than you'll need)
.Net resources are usually kept at the top of the code so you will find them fast skipping the first 100,000 bytes in my case.
Then you just read it in and look for those XXXXXX's. When you find them you replace them with the real API key and replace the rest of the X's with spaces you just trim off in code. This is the answer. It works and it works well.
ApiToken at = new ApiToken(UserId, SelectedCID);
at.MakeToken();
byte[] app = System.IO.File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.GetData("DataDirectory").ToString(), "notkeyedapp.exe"));
for (int i = 100000; i < app.Length; i++)
{
if (app[i] == 0x58 && app[i + 1] == 0x58 && app[i + 2] == 0x58)
{
for (int j = 0; j < 128; j++)
{
if (at.Token.Length >= j + 1)
app[i + j] = System.Text.Encoding.ASCII.GetBytes(at.Token[j].ToString())[0];
else
app[i + j] = 0x20;
}
break;
}
}
string filename = "SoftwareProduct for - " + BaseModel.CompanyName.Replace(".", "") + ".exe";
return File(app, System.Net.Mime.MediaTypeNames.Application.Octet, filename);
I don't think You can get away without recompiling Your .exe and having key embedded into said .exe. The compilation process can be automated though via use of ildasm.exe and ilasm.exe as Daniel Earwicker suggested in his response https://stackoverflow.com/a/2742902/2358659
I'd like to expand on that if anyone else stumbles across this topic in the future.
I recently was facing similar problem due to my poor source code version control habits. In a nutshell I had an executable that was supposed to write some data to a Google Spreadsheet by referencing it's ID. Long after executable was released came another request from a different team to use the tool, but it had to write same information into a different spreadsheet in order to keep data separate for two teams. At the time I did not have the original source code, hence I was not able to change the static variable holding the original spreadsheet ID. What I did was as follows:
Using CMD.exe → call "C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\ildasm.exe" "myApplication.exe" /out="myApplication.il"
Using Notepad++ → Find and replace original ID to new ID inside myApplication.il file. This action can also be automated by writing own C# application to do this, or using PowerShell, or using vb/j-script or using some other find and replace tool available off-the-shelf, like FART (using CMD.exe → call fart.exe myApplication.il "OldKey" "NewKey")
Using CMD.exe → call "C:\Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe" "myApplication.il" /res="myApplication.res" /key="myApplicationKeyFile.snk"
As You see, all of these steps can be put into one .bat file that takes "NewKey" as an input and produces new .exe with NewKey embedded.
I hope that helps.
What comes to my mind, but not tried yet: Create a default String in your program, for example as
static public string regGuid = "yourguidhere";
Then, search the compiled EXE with any decent hex editor. If you find the string, replace it with another test. If you still can execute the program, you could try to automate this process and voila! Here you are.