I consider feasibility and performance under .NET (using C#) of the verification part of a standard RSA signature scheme that is not directly supported. Towards that goals, I need the raw RSA public-key encryption function x → (x65537) mod N (where x is a byte array as wide as the public modulus N is, like 256 bytes).
On other platforms, the standard technique is to implement that function using RSA encryption with no padding (Java's Cipher with "RSA/ECB/NoPadding"). But I can't find how to perform this under .NET. What are my options?
.NET doesn't provide this functionality inbox. If you're just doing public key operations then you can use the BigInteger class without having security liabilities. (Don't use it for private key operations, because a) it'll have your private key fairly obviously in memory and b) it doesn't have a Montgomery Ladder based ModPow, so it would leak the Hamming Weight of your private key)
RSA existingKey = HoweverYouWereGettingAKey();
RSAParameters rsaParams = existingKey.ExportParameters(false);
BigInteger n = PrepareBigInteger(rsaParams.Modulus);
BigInteger e = PrepareBigInteger(rsaParams.Exponent);
BigInteger sig = PrepareBigInteger(signature);
BigInteger paddedMsgVal = BigInteger.ModPow(sig, e, n);
byte[] paddedMsg = paddedMsgVal.ToArray();
if (paddedMsg[paddedMsg.Length - 1] == 0)
{
Array.Resize(ref paddedMsg, paddedMsg.Length - 1);
}
Array.Reverse(paddedMsg);
// paddedMsg is now ready.
private static BigInteger PrepareBigInteger(byte[] unsignedBigEndian)
{
// Leave an extra 0x00 byte so that the sign bit is clear
byte[] tmp = new byte[unsignedBigEndian.Length + 1];
Buffer.BlockCopy(unsignedBigEndian, 0, tmp, 1, unsignedBigInteger.Length);
Array.Reverse(tmp);
return new BigInteger(tmp);
}
Related
Consider the following code using RSA...
Example:
byte[] raw = Encoding.Default.GetBytes("Hello, World!");
RSA key = RSA.Create();
for (int index = 0; index < 5; index++)
{
byte[] signed = key.SignData(raw, HashAlgorithmType.SHA256, RSASignaturePadding.Pkcs1);
string hashed = signed.ToSha256String();
Console.WriteLine(hashed);
}
Output:
043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89
043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89
043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89
043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89
043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89
Now consider the same code, except using ECDSA...
Example:
byte[] raw = Encoding.Default.GetBytes("Hello, World!");
ECDsa key = ECDsa.Create();
for (int index = 0; index < 5; index++)
{
byte[] signed = key.SignData(raw, HashAlgorithmType.SHA256);
string hashed = signed.ToSha256String();
Console.WriteLine(hashed);
}
Output:
043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89
a31fe9656fc8d3a459e623dc8204e6d0268f8df56d734dac3ca3262edb5db883
a871c47a7f48a12b38a994e48a9659fab5d6376f3dbce37559bcb617efe8662d
d7ef0a04f3c8055644677299a9414a75adcb15916eb48417416c9317ace2ff4f
779f5dd63960abda52a7da70464b92eedd47f84a8dffda2d672e6a99de1bab95
RSA's signature output I expected; ECDSA's, I didnt. Why does ECDSA produce different signatures for the same data?
Elliptic Curve Digital Signature Algorithm (ECDSA), that is an adaptation of the classical DSA algorithm, relies on a cryptographically secure random number generator. For example: ECDSA signing algorithm calculates a message's hash, then generates a random integer k and calculates the signature (a pair of integers {R, S} ). R is calculated from k, and S is calculated using the message hash + the private key + the random number k. So, the signature is non-deterministic due to a randomness.
You can experiment with an Elliptic Curve here.
The signature of ECDSA consists of a combination of R and S, where R is a value that is dependent of random parameter k which is in turn input into the ECDSA algorithm. S in turn depends on that value. If R would be a static value then it would allow an attacker to calculate the private key (!).
The fact that signature generation using PKCS#1 v1.5 padding is deterministic makes it the odd one out. Actually, RSA with PSS padding is randomized using an explicit salt, so the deterministic behavior is not tied to RSA in any way; you could even say that it is an unwanted property as it does expose information about the message / data that has been signed, potentially to other parties than the verifier.
It is however possible to calculate k using a deterministic scheme based on the message itself and the private key if you must. This may have some advantages in particular protocols; for instance it means that no random number generator needs to be present. Generally it is recommended to the non-deterministic / randomized version of ECDSA.
In the end, the proof is in the pudding: if verification with the public key succeeds then your signature generation procedure was according to specification. For the scheme to be secure the public key must of course be validated and trusted, among other requirements.
I have a 128 bit 3DES key 1915372928A30803A25B0659A4DD6525, how could I split the key into 3 components and calculate the KCV for each component? I'd like to do similarly to the online tool below
https://www.emvlab.org/keyshares/?combined=1915372928A30803A25B0659A4DD6525&combined_kcv=2082A4&one=B9FFAF926385DBED0FBC087F5DC674C3&one_kcv=C69561&two=EA3CD5B063E0BF73F6C5ECB5F7D32080&two_kcv=33D908&three=4AD64D0B28C66C9D5B22E2930EC83166&three_kcv=03DCA8&numcomp=three&parity=ignore&action=Generate+128+bit
The code i used to generate 3DES key
public byte[] GenerateThreeDesKey()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] tripleDesKey = new byte[16];
rng.GetBytes(tripleDesKey);
for (var i = 0; i < tripleDesKey.Length; ++i)
{
int keyByte = tripleDesKey[i] & 0xFE;
var parity = 0;
for (int b = keyByte; b != 0; b >>= 1)
parity ^= b & 1;
tripleDesKey[i] = (byte)(keyByte | (parity == 0 ? 1 : 0));
}
return tripleDesKey;
}
After getting the key, how to split the key into 3 components and calculate the KCV?
Generate two separate DES 128 bit keys the same way as you are doing now, these are components 1 and 2. Then XOR these keys together with your current (master key). The result is the third component. You can adjust the parity of that key as well if you want.
To calculate the KCV's, simply use the generated components to encrypt a block of 8 bytes set to zero. You can use ECB mode or CBC mode (without padding) if a direct block encrypt is not available. For CBC you need to set the IV to all zeros as well. Then take the leftmost bytes of the result and encode as hexadecimals.
1- to calculate the KCV you have to encrypt 16 bytes of zero's with generated key
(data: 00000000000000000000000000000000, key: 404142434445464748494A4B4C4D4E4F) = 8BAF473F2F8FD0948BAF473F2F8FD094 (last three bytes is KCV (8BAF47))
2- to split key into 3 component
Start with the Key 404142434445464748494A4B4C4D4E4F
create 2 random number of the same length (16 bytes in this example):
Rand 1 : 988A59D7273186B8C9C9922B6D40BA75 and Rand 2: 8936E5269ADFABE7D4829B2EFB3BF5D9
(the random numbers will become Component1 and Component2)
now XOR the 3 numbers. i.e. XOR Key1, Component1 and Component2 together:
XOR(0123456789ABCDEFFEDCBA9876543210, 988A59D7273186B8C9C9922B6D40BA75, 8936E5269ADFABE7D4829B2EFB3BF5D9) = 109FF9963445E0B0E397B39DE02F7DBC (the result will be key Component3)
I am using this function to change public key and encrypt data:
public byte[] EncryptData(byte[] data2Encrypt)
{
string key = "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413";
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024);
BigInteger intk;
BigInteger.TryParse(key, out intk);
RSAParameters privateKey = new RSAParameters();
byte[] expont = { 1, 0, 1 };
byte[] modulus = intk.ToByteArray();
Logger.log(Log_Type.ERROR, "Pierwszy bit: " + modulus[0]);
privateKey.Exponent = expont;
privateKey.Modulus = intk.ToByteArray();
rsa.ImportParameters(privateKey);
return rsa.Encrypt(data2Encrypt, false);
}
But it return me array with 129 length instead od 128 (What should be max lenght using 1024 bits i think). What can be a reason?
If you use BigInteger an additional bit is always placed before the
number. If your key has 1024 bits you get 1025 bits, so skip the
first byte if it is 0x00 (meaning a positive value)
BigInteger produces signed little-endian numbers, while RSAParameters requires unsigned big-endian. You can still use BigInteger though, just convert its output to what RSAParameters is expecting.
byte[] modulus = intk.ToByteArray().Reverse().Skip(1).ToArray();
Reverse to make the number big-endian, and Skip(1) to skip the sign.
I am not sure, that it should be even converted into BitInteger. RSA key what I am trying to get is similar to this function in C++
void Crypt::rsaSetPublicKey(const std::string& n, const std::string& e)
{
BN_dec2bn(&m_rsa->n, n.c_str());
BN_dec2bn(&m_rsa->e, e.c_str());
// clear rsa cache
if(m_rsa->_method_mod_n) { BN_MONT_CTX_free(m_rsa->_method_mod_n); m_rsa->_method_mod_n = NULL; }
}
Where 'n' is this key srting and 'e' is : "65537"
If it should not be a BigInteger, then what?
I am writing an asp.net MVC app that drives an IPhone application.
I want the Iphone to send me its UUID looks like this:
2b6f0cc904d137be2e1730235f5664094b831186
On the server I want to generate a Guid:
466853EB-157D-4795-B4D4-32658D85A0E0
On both the Iphone and the Server I need a simple aglorithm to combine these 2 values into an Auth token that can be passed around. Both the IPhone and the ASP.NET MVC app need to be able to compute the value over and over based on the UUID and the GUID.
So this needs to be a simple algorithm with no libraries from the .net framework.
Full Solution Here
public void Test()
{
var DeviceId = Guid.NewGuid();
var newId = "2b6f0cc904d137be2e1730235f5664094b831186";
var guidBytes = DeviceId.ToByteArray();
var iphoneBytes = StringToByteArray(newId);
byte[] xor = new byte[guidBytes.Length];
for (int i=0;i<guidBytes.Length;i++)
{
xor[i] = (byte) (guidBytes[i] ^ iphoneBytes[i]);
}
var result = ByteArrayToString(xor);
}
public static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
Well, the iPhone ID looks like a hex string, so converting both to binary and XORing the bytes ought to do it. You could store the result as an array, hex string, or base-64 encoded string as appropriate.
The way you refer to this as an "auth token" is a little concerning, however. Session ids must be unpredictable. You might consider generating an array of cryptographically random data on the server instead of a GUID.
Edit
// Convert the server GUID to a byte array.
byte[] guidBytes = severGuid.ToByteArray();
// Convert the iPhone device ID to an array
byte[] idBytes = StringToByteArray(iPhoneId);
Sadly, it seems .NET doesn't have a built-in method to convert to/from hex strings, but this subject has been covered before: Convert byte array to hex string and vice versa
// Once you've XORed the bytes, conver the result to a string.
string outputString = ByteArrayToString(outputBytes);
Just as a side note, all the "auth token" mechanisms I've worked with (at least) concatenated a constant value (a "secret") with the current time, then hashed them together then sent the hash and the date. The server then reconstructed the hash from the received date and known "secret" and then compared to the received hash (signature).
My point here was "concatenated with date" - this allows the resulting signature to be different every time which in theory should be more secure.
Rather than XORing, which loses information, you could just concatenate these hex digits.
Why do you even need the GUID? The phone ID is unique, the GUID seems to add no value.
I thought you can use two way algorithm. It mean the algorithm can be encode and decode like Base64, SHA256, AES
I using:
c#: RSACryptoServiceProvider
JAVA: KeyFactory.getInstance("RSA")+Cipher
I sending public key (exponent + modulus) as byte array from java to c#. It's ok, there is the same bytes. But when i try to encrypt some data with one key in Java and c# - there is different results.
Java Key Generation:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize( Config.CRYPTO_KEY_NUM_BITS );
m_KeyPair = keyGen.genKeyPair();
m_PublicKey = KeyFactory.getInstance("RSA").generatePublic(
newX509EncodedKeySpec(m_KeyPair.getPublic().getEncoded()));
byte[] exponent = m_PublicKey.getPublicExponent().toByteArray();
byte[] modulus = m_PublicKey.getModulus().toByteArray(); // then sending...
C# Key Recieve:
// Recieved...
m_ExternKey = new RSAParameters();
m_ExternKey.Exponent = exponent;
m_ExternKey.Modulus = modulus;
m_RsaExtern = new RSACryptoServiceProvider();
m_RsaExtern.ImportParameters(m_ExternKey);
byte[] test = m_RsaExtern.Encrypt(bytesToEncrypt, true);
and problem is that encrypted bytes is different.
Thank you.
RSA encryption is randomized. For a given public key and a given message, each attempt at encryption yields a distinct sequence of bytes. This is normal and expected; random bytes are injected as part of the padding phase, and not injecting random bytes would result in a weak encryption system. During decryption, the padding bytes are located and removed, and the original message is recovered unscathed.
Hence it is expected that you will get distinct encrypted messages with Java and C#, but also if you run your Java or C# code twice.
RSA Encription mustn't return diffferent values with simular keys - its standardized algorithm. Check your keys.
RSA Parameters contains more parameters than modulus and exponent if i remember correctly. You need fully initialized rsa parameters to get the encryption correct (in .net).
Moreover, your private and private key is not even set in .net
i hope this is helpful , in C# lough code
byte[] rsaExp = rsaParameters.Exponent.ToByteArray();
byte[] Modulus = rsaParameters.Modulus.ToByteArray();
// Microsoft RSAParameters modulo wants leading zero's removed so create new array with leading zero's removed
int Pos = 0;
for (int i = 0; i < Modulus.Length; i++)
{
if (Modulus[i] == 0)
{
Pos++;
}
else
{
break;
}
}
byte[] rsaMod = new byte[Modulus.Length - Pos];
Array.Copy(Modulus, Pos, rsaMod, 0, Modulus.Length - Pos);