C# and PHP ECDH not matching - c#

I'm trying to generate a shared secret between a web server running PHP and a C# desktop application. I'm aware of the BouncyCastle library, but I'd prefer not having to use it since it's pretty huge.
I'm using phpecc and ECDiffieHellmanCng and trying to generate a shared secret between the two parties but I'm having issues with exporting/importing in C#.
It seems phpecc requires der/pem format in order to import a key, and ECDiffieHellmanCng doesn't seem to have any easy way to export in a compatible format.
Would I need to write my own pem/der encoder and decoder in order to do this or is there some alternative easier way?
Currently I'm doing the following in C#:
using (var ecdh = new ECDiffieHellmanCng())
{
ecdh.HashAlgorithm = CngAlgorithm.ECDiffieHellmanP384;
ecdh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
var encoded = EncodePem(ecdh.PublicKey.ToByteArray());
//... do something with encoded
}
private static string EncodePem(byte[] data)
{
var pemDat = new StringBuilder();
var chunk = new char[64];
pemDat.AppendLine("-----BEGIN PUBLIC KEY-----");
var encodedData = Convert.ToBase64String(data);
for (var i = 0; i < encodedData.Length; i += chunk.Length)
{
var index = 0;
while (index != chunk.Length && i + index < encodedData.Length)
{
chunk[index] = encodedData[i + index];
index++;
}
pemDat.AppendLine(new string(chunk));
}
pemDat.AppendLine("-----END PUBLIC KEY-----");
return pemDat.ToString();
}
Obviously the above is only doing the pem encoding, so on the php side it returns an error when it's trying to parse it:
Type: Runtime
Exception Message: Invalid data.
File: /.../vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php
Line: 49

.NET Core 1.0 and .NET Framework 4.7 have the ECParameters struct to import/export keys. The ToByteArray() method you called is producing a CNG EccPublicBlob which has very little to do with the SEC-1 ECParameters format.
I'm going to assume that you wanted to use secp384r1/NIST P-384, even though you specified that as a hash algorithm. If you want some other curve, you'll need to do some translations.
The (.NET) ECParameters struct will only help you get started. Turning that into a file requires translating it into a PEM-encoded DER-encoded ASN.1-based structure. (But if you're sticking with NIST P-256/384/521, you can do it with the byte[] you currently have)
In SEC 1 v2.0 we get the following structures:
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier {{ECPKAlgorithms}} (WITH COMPONENTS {algorithm, parameters}),
subjectPublicKey BIT STRING
}
ECPKAlgorithms ALGORITHM ::= {
ecPublicKeyType |
ecPublicKeyTypeRestricted |
ecPublicKeyTypeSupplemented |
{OID ecdh PARMS ECDomainParameters {{SECGCurveNames}}} |
{OID ecmqv PARMS ECDomainParameters {{SECGCurveNames}}},
...
}
ecPublicKeyType ALGORITHM ::= {
OID id-ecPublicKey PARMS ECDomainParameters {{SECGCurveNames}}
}
ECDomainParameters{ECDOMAIN:IOSet} ::= CHOICE {
specified SpecifiedECDomain,
named ECDOMAIN.&id({IOSet}),
implicitCA NULL
}
An elliptic curve point itself is represented by the following type
ECPoint ::= OCTET STRING
whose value is the octet string obtained from the conversion routines given in Section 2.3.3.
Distilling this down to the relevant parts, you need to write
SEQUENCE (SubjectPublicKeyInfo)
SEQUENCE (AlgorithmIdentifier)
OBJECT IDENTIFIER id-ecPublicKey
OBJECT IDENTIFIER secp384r1 (or whatever named curve you're using)
BIT STRING
public key encoded as ECPoint
The AlgorithmIdentifier contains data that's fixed given you don't change the curve:
SEQUENCE (AlgorithmIdentifier)
30 xx [yy [zz]]
OBJECT IDENTIFIER id-ecPublicKey (1.2.840.10045.2.1)
06 07 2A 86 48 CE 3D 02 01
OBJECT IDENTIFIER secp384r1 (1.3.132.0.34)
06 05 2B 81 04 00 22
and we can now count how many bytes were in the payload: 16 (0x10), so we fill in the length:
30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04
00 22
The public key encoding that everyone understands is "uncompressed point", which is
04 th eb yt es of x. th eb yt es of y.
Turns out, that has a fixed size for a given curve, too, so unlike most things that are DER encoded, you can do this in one pass :). For secp384r1 the x and y coordinate are each 384 bit values, or (384 + 7)/8 == 48 bytes, so the ECPoint is 48 + 48 + 1 == 97 (0x61) bytes. Then it needs to be wrapped in a BIT STRING, which adds one payload byte and the length and tag. So, we get:
private static byte[] s_secp384r1PublicPrefix = {
// SEQUENCE (SubjectPublicKeyInfo, 0x76 bytes)
0x30, 0x76,
// SEQUENCE (AlgorithmIdentifier, 0x10 bytes)
0x30, 0x10,
// OBJECT IDENTIFIER (id-ecPublicKey)
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
// OBJECT IDENTIFIER (secp384r1)
0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22,
// BIT STRING, 0x61 content bytes, 0 unused bits.
0x03, 0x62, 0x00,
// Uncompressed EC point
0x04,
}
...
using (ECDiffieHellman ecdh = ECDiffieHellman.Create())
{
ecdh.KeySize = 384;
byte[] prefix = s_secp384r1PublicPrefix;
byte[] derPublicKey = new byte[120];
Buffer.BlockCopy(prefix, 0, derPublicKey, 0, prefix.Length);
byte[] cngBlob = ecdh.PublicKey.ToByteArray();
Debug.Assert(cngBlob.Length == 104);
Buffer.BlockCopy(cngBlob, 8, derPublicKey, prefix.Length, cngBlob.Length - 8);
// Now move it to PEM
StringBuilder builder = new StringBuilder();
builder.AppendLine("-----BEGIN PUBLIC KEY-----");
builder.AppendLine(
Convert.ToBase64String(derPublicKey, Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine("-----END PUBLIC KEY-----");
Console.WriteLine(builder.ToString());
}
Running the output I got from that into OpenSSL:
$ openssl ec -pubin -text -noout
read EC key
(paste)
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEwpbxYmcsNvr14D8k+0VQCkSY4WCV/3V10AiIq7sFdmUX
9+0DMuuLDmcKjL1ZFEFk0yHCPpY+pdkYtzPwE+dsApCPT3Ljk0AxHQBTSo4yjwsElMoA4Mtp8Qdo
LZD1Nx6v
-----END PUBLIC KEY-----
Private-Key: (384 bit)
pub:
04:c2:96:f1:62:67:2c:36:fa:f5:e0:3f:24:fb:45:
50:0a:44:98:e1:60:95:ff:75:75:d0:08:88:ab:bb:
05:76:65:17:f7:ed:03:32:eb:8b:0e:67:0a:8c:bd:
59:14:41:64:d3:21:c2:3e:96:3e:a5:d9:18:b7:33:
f0:13:e7:6c:02:90:8f:4f:72:e3:93:40:31:1d:00:
53:4a:8e:32:8f:0b:04:94:ca:00:e0:cb:69:f1:07:
68:2d:90:f5:37:1e:af
ASN1 OID: secp384r1
NIST CURVE: P-384

Related

How to remove N number of bytes from end of file, only if they match a specified array of bytes

I am running into an issue building a program to process files provided. These files are XML files formatted with UTF-8. Oddly, some files end with 0x0A 0x00, and cause our XML parser to throw an error. I am looking to build a function to remove these bytes on the end of a file if they exist, without "hard coding" 0x0A 0x00. Ideally this function could be used in the future for any similar behavior with an array of any size.
Here is the exception:
System.Xml.XmlException:
hexadecimal value 0x00, is an invalid character. Line 250, position 1.
This occurs in some files, but not all. The root cause of this behavior is yet to be discovered.
I'm sorry, I do not have a code sample, as I have not been able to get anything remotely close to work :) I will edit this post if I get something somewhat working.
Any insight is appreciated!
Something like this should do the trick, keep in mind though this does not have any error handling built in, this is just the barebones functionality:
static void TrimFile(string filePath, byte[] badBytes)
{
using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
{
byte[] bytes = new byte[badBytes.Length];
file.Seek(-badBytes.Length, SeekOrigin.End);
file.Read(bytes, 0, badBytes.Length);
if (Enumerable.SequenceEqual(bytes, badBytes))
{
file.SetLength(Math.Max(0, file.Length - badBytes.Length));
}
}
}
You can call it like this:
TrimFile(filePath, new byte[] { 0x0A, 0x00 });
Here's a test file I created with 0xCA 0xFE 0xFF 0xFF at the end (some bunk data)
62 75 6E 6B 20 66 69 6C 65 CA FE FF FF
bunk fileÊþÿÿ
After running TrimFile(filePath, new byte[] { 0xCA, 0xFE, 0xFF, 0xFF });
62 75 6E 6B 20 66 69 6C 65
bunk file
Hope this comes in handy!

C# convert byte to hex after mode 100 and send it using serial

I need to run mode 0x100 and send hex message
this is the mode100 function I have created (which is working )
public static byte Mode100(byte[] p)
{
byte lcs = 0;
foreach (byte b in p)
{
lcs += b;
}
return lcs;
}
this is what I'm trying to send
byte[] msg = { 0X06, 0XA2, 0XD2, 0X06, 0XD3, 0X11, 0XD4, 0X65, 0X6F };
var Mode100Dec = Mode100(msg);//value in int
string Mode100hex = Mode100Dec.ToString("X"); //value in hex 0-F
byte [] Mode100Byte = Encoding.Default.GetBytes(Mode100hex);//value in dec ascci of hex
var hexString = BitConverter.ToString(Mode100Byte); //value in hex of ascii
for this example the the Mode100 function return me
12(Dec)
which is C(Hex)
but how do I convert it to byte[] so I can send 0x0C ?
because now it change me the "C" to 67 Dec \ 42 Hex
which is wrong .....
I have look this post
How do you convert a byte array to a hexadecimal string, and vice versa?
but it didn't help me to get the answer I need
I agree with note from #500-InternalServerError that it seems you want the BINARY value, not the hex. Your example actually shows this -- your checksum would be 0x09, which is what you send. The ASCII for for 0x09 would be two characters, '0' & '9', which in ASCII would be 0x30 0x39. So I think you are confusing "Decimal" and "Hex" (which are conversion of binary values to strings) with the binary value (which is neither decimal nor hex).
To get the result you seem to be looking for, remove the conversion to a HEX string:
byte[] msg = { 0X06, 0XA2, 0XD2, 0X06, 0XD3, 0X11, 0XD4, 0X65, 0X6F };
var Mode100val = Mode100(msg); //value as a BYTE
byte [] Mode100Byte = new byte[] { Mode100val};
The array of a single byte will contain a single binary value 12 decimal / 0C hex.

RSA Encrypt Handle invalid issue

I am trying to encrypt a text and the decrypt it using public private key pair. I need the keys to handled as string. So I convert these key from string to RSAParameters. But when i try to encrypt the text, it throws me an error as "The handle is not valid".
static RSAParameters _publicKey = new RSAParameters();
static RSAParameters _privateKey = new RSAParameters();
static string _strPublicKey = "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsG5sBbnH7gXkrExzNOeK\r\nEjoRDHUH5uU+crH52Z1uXCEx8LiFow8RwrvZGqjYXgBwxzqOQwHJt3utoNVY0niP\r\nHjfPXwKTk79PkeET/mtRar1gEcCOr0/hgHxT3YGlQLw2ugVIulMzlUBRY4rceNv3\r\nEiSZ+4cnO04hJ6UiftrCfwTe6q9Hadp6B6SX+N9hgcHhRX4iR/VYUf/6cvN+NAgb\r\ntuF0Dk61C6ulh2Gvdj2TBCLaq1LPF5H+ghrRxjK/Zn6MG9BW2ju9g8zYKuufaaaM\r\ndbTmN+Z4f27nEOxFq5wWqeaWl53yrMia6xnOi7vtU8zcwBL7jSLgwrkyO8LabCdz\r\neQIDAQAB\r\n-----END PUBLIC KEY-----";
static string _strPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEogIBAAKCAQEAsG5sBbnH7gXkrExzNOeKEjoRDHUH5uU+crH52Z1uXCEx8LiF\r\now8RwrvZGqjYXgBwxzqOQwHJt3utoNVY0niPHjfPXwKTk79PkeET/mtRar1gEcCO\r\nr0/hgHxT3YGlQLw2ugVIulMzlUBRY4rceNv3EiSZ+4cnO04hJ6UiftrCfwTe6q9H\r\nadp6B6SX+N9hgcHhRX4iR/VYUf/6cvN+NAgbtuF0Dk61C6ulh2Gvdj2TBCLaq1LP\r\nF5H+ghrRxjK/Zn6MG9BW2ju9g8zYKuufaaaMdbTmN+Z4f27nEOxFq5wWqeaWl53y\r\nrMia6xnOi7vtU8zcwBL7jSLgwrkyO8LabCdzeQIDAQABAoIBAA2z5cvkC/UenA4N\r\nufzn5r9Xpy9Sf5SdRWZfEEqogYPCSECr9CUf7H81W71IU9WpLxkqIRZvMx1/C5Ms\r\nPsPJ/UOZjg+RAak9+I4Z7xWZfC9QGgAG9o4DJD54aYMQqKcIdy+nbWibQaxb3HZg\r\nuJLicqQEF7mDW7atcMHFf5JepzB6LO7u9mfgR03uHQh6r6ym27BTGwssSmEeOeiA\r\n+tOPEhCsbZMSs5+8aGMoV08OqjscytQCWDY8rwA8ZE/qis+cNxKo0OluRTde68mH\r\nbr42CZpNJNulhg4mZyxtrtC+D13VcRpFeKW7WbMBwEUJ8/liUBDvAPLB3Np46FsG\r\njcZfFmkCgYEAtSk5HGEk+y8dyl+l62u7oir5IEZf8vNsMJ+CrpF1C8e6qShRe9uy\r\nJ5pN/4dBb2thknOLsaw6K2qYGNkH3TYSpHusW7v4Iuy6ONmzXHCxcgconzCWJ0HI\r\nLWnYRZAHv8PdOxdFqukjLqFOz6fEyIJ5Ayp+7qxg3QRmE7bwnEwWYK8CgYEA+VEC\r\nBUVUd8tHLqMyaYuv7HXQlQ3J01fobts98k+xqQtKsMUu4SZQ/5uQF8Wk3P6AAKiU\r\ngeD81Fpu4PINrRH97R0twnlWru1fHNSHNenuLASW+3I4l975PhZJc5AeV7VpRcnY\r\nyZCMAKO/XRpfgTEsv5HNfidUsYuJ/9epQzVy6FcCgYAs1V3f6x621ys9OTybrZbL\r\nBG2REjmOq7V7tw4lW7QmzTAhyuuXhoBpkqN4+KU2CNIl51iMCP6AXin0BEoQ8d/d\r\nOwolzbgUFJfll+LunqkbejAQbXrLjlkW/Bnc5U81oyhuBk1khbwCP0N82p01rix6\r\nnxq4wIpcSElm2aBkXeQv2wKBgCvewUhEJtTdhC0Esn44AkDNimJwBq+VrGS1V3Un\r\n6M8iGYZ5bAJaR65ypSxJrvTkI4n6IAeqm1KShyg174ogvFnY5JBv4Xzub+oWy6QF\r\nAc/lDtw4ARVYOutd6JbZKT2twlRxbCAruzbxmV68oUmOaZ1b/pjQOury7tmCDVqy\r\nMQIJAoGAIqUiIAnKLBrXhfN0nqGt2iOnRl31Ef3p/pTNwhUBdJphi/zlE9JHTI2Q\r\nCiLCGyJTpr3FoPjIJZ2P+fRrB3FmVuGNVZw5s8g4ouuDWNyCL/upVwU84eAWMu7P\r\nDsUL5Ia5W7/Cm7d0/nqchUCkRslIsH+bfGj0NZ7qcE5H1D8Ee0A=\r\n-----END RSA PRIVATE KEY-----";
static void Main(string[] args)
{
var message = "Hello World!";
var data = Encrypt(message);
var res = Decrypt(data);
}
private static RSAParameters GetRSAParameters(string pPublicKey)
{
byte[] lDer;
//Set RSAKeyInfo to the public key values.
int lBeginStart = "-----BEGIN PUBLIC KEY-----".Length;
int lEndLenght = "-----END PUBLIC KEY-----".Length;
string KeyString = pPublicKey.Substring(lBeginStart, (pPublicKey.Length - lBeginStart - lEndLenght));
lDer = Convert.FromBase64String(KeyString);
//Create a new instance of the RSAParameters structure.
RSAParameters lRSAKeyInfo = new RSAParameters();
lRSAKeyInfo.Modulus = GetModulus(lDer);
lRSAKeyInfo.Exponent = GetExponent(lDer);
return lRSAKeyInfo;
}
private static byte[] GetModulus(byte[] pDer)
{
//Size header is 29 bits
//The key size modulus is 128 bits, but in hexa string the size is 2 digits => 256
string lModulus = BitConverter.ToString(pDer).Replace("-", "").Substring(58, 256);
return StringHexToByteArray(lModulus);
}
private static byte[] GetExponent(byte[] pDer)
{
int lExponentLenght = pDer[pDer.Length - 3];
string lExponent = BitConverter.ToString(pDer).Replace("-", "").Substring((pDer.Length * 2) - lExponentLenght * 2, lExponentLenght * 2);
return StringHexToByteArray(lExponent);
}
public static byte[] StringHexToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static string Encrypt(string mess)
{
string response = "";
var input = Encoding.UTF8.GetBytes(mess);
using (var rsa = new RSACryptoServiceProvider(1024))
{
rsa.PersistKeyInCsp = false;
rsa.ImportParameters(GetRSAParameters(_strPublicKey));
var decrypt = rsa.Encrypt(input, true);
response = Convert.ToBase64String(decrypt);
}
return response;
}
public static string Decrypt(string mess)
{
string response = "";
var input = Convert.FromBase64String(mess);
using (var rsa = new RSACryptoServiceProvider(2048))
{
rsa.PersistKeyInCsp = false;
rsa.ImportParameters(GetRSAParameters(_strPublicKey));
var decrypt = rsa.Decrypt(input, false);
response = Encoding.UTF8.GetString(decrypt);
}
return response;
}
Above is the code that I am using to encrypt and decrypt. After the conversion, I convert them to string and pass it to the decrypt function. Please advice
Your attempt to read the DER is definitely lacking. Possibly because you have a 2048-bit key and your "parser" at best only works for a 1024-bit key (though I'm having trouble deciding what key it could legitimately read).
Your public key hex is
30820122300D06092A864886F70D01010105000382010F003082010A02820101
00B06E6C05B9C7EE05E4AC4C7334E78A123A110C7507E6E53E72B1F9D99D6E5C
2131F0B885A30F11C2BBD91AA8D85E0070C73A8E4301C9B77BADA0D558D2788F
1E37CF5F029393BF4F91E113FE6B516ABD6011C08EAF4FE1807C53DD81A540BC
36BA0548BA5333954051638ADC78DBF7122499FB87273B4E2127A5227EDAC27F
04DEEAAF4769DA7A07A497F8DF6181C1E1457E2247F55851FFFA72F37E34081B
B6E1740E4EB50BABA58761AF763D930422DAAB52CF1791FE821AD1C632BF667E
8C1BD056DA3BBD83CCD82AEB9F69A68C75B4E637E6787F6EE710EC45AB9C16A9
E696979DF2ACC89AEB19CE8BBBED53CCDCC012FB8D22E0C2B9323BC2DA6C2773
790203010001
The DER breakdown (because I can't help myself) is
30 82 01 22
30 0D
06 09 2A 86 48 86 F7 0D 01 01 01
05 00
03
82 01 0F
00
30 82 01 0A
02 82 01 01
00B06E6C05B9C7EE05E4AC4C7334E78A123A110C7507E6E53E72B1F9D99D6E5C
2131F0B885A30F11C2BBD91AA8D85E0070C73A8E4301C9B77BADA0D558D2788F
1E37CF5F029393BF4F91E113FE6B516ABD6011C08EAF4FE1807C53DD81A540BC
36BA0548BA5333954051638ADC78DBF7122499FB87273B4E2127A5227EDAC27F
04DEEAAF4769DA7A07A497F8DF6181C1E1457E2247F55851FFFA72F37E34081B
B6E1740E4EB50BABA58761AF763D930422DAAB52CF1791FE821AD1C632BF667E
8C1BD056DA3BBD83CCD82AEB9F69A68C75B4E637E6787F6EE710EC45AB9C16A9
E696979DF2ACC89AEB19CE8BBBED53CCDCC012FB8D22E0C2B9323BC2DA6C2773
79
02 03
010001
Which is the ASN.1 equivalent of
SEQUENCE
SEQUENCE
OBJECT IDENTIFIER id-rsaEncryption
NULL
BIT STRING (0 bits unused) (wrapping)
SEQUENCE
INTEGER (positive) 0xB06E...7379
INTEGER 0x010001
Your modulus extractor skips 29 bytes and reads the next 128. (Why it does byte[] => hex => substring => byte[] instead of doing new byte[size] and Buffer.BlockCopy is beyond me). The problem is, your key is a 2048-bit key (the modulus length is 0x0101, and once we remove the sign-padding byte it's 0x0100 bytes long, with the high bit set, so 256*8 = 2048 bit modulus). So you need to have read 256 bytes. And, worse, because the payload is bigger than expected, it doesn't start at the offset you think it does. So where you should end up with B0 6E 6C ... 27 73 79 (after skipping the padding byte) you ended up with 82 01 01 00 *B0 6E 6C ... A5 22 7E (the asterisk marking the desired start of your payload). So, that's bad, and you are definitely not encrypting what you think you are. (The A5 22 7E sequence occurs on the line starting with 36BA, ending with 3 bytes left to go on that line)
What's worse is the GetExponent method. It seems to try to combine actual DER reading with presupposing what the value is. It should conclude 01 00 01, but it reads the first 01 of the payload as a length, then uses that length to count from the end. So the exponent value ends up being just 01. The RSA algorithm doesn't work with e=1, and what's happening is probably that a post-encryption self-test decided that the results were garbage, and the Win32 crypto library concluded that the problem is that the caller gave it something that wasn't a valid key handle, as opposed to a valid handle to an invalid key. In addition to not understanding the byte[] => hex => substring => byte[] pattern here I don't understand why this code decided that pDer[-3] (to use syntax from another language) was the length value. DER can only be read "left to right", and if it knew "the exponent is always 0x010001" then a) that'd be length-encoded at pDer[-4] b) just reading the last three bytes would be forgivable and c) given how many assumptions this code makes it would've made more sense to just blindly assume 0x010001.
So, you need to a) replace your parser with either a correct parser or at least a specialized one which functions at your desired keysize and b) never use that private key for anything important, since you just posted it to the Internet (and the private key variable wasn't even used in this problem).

Mifare DESFIRE EV1 GetCardUid

The following code works and allows me to get the UID of a Mifare 1k card. Unfortunately, it does not work with Desfire cards.
public byte[] GetUid()
{
byte[] uid = new byte[6];
int rc = Communicate(new byte[]{0xff, 0xca, 0x00, 0x00, 0x04}, ref uid);
if (rc != 0)
throw new Exception("failure: " + rc);
int rc1, rc2;
if (uid.Length == 2)
{
rc1 = uid[0];
rc2 = uid[1];
}
else
{
rc1 = uid[4];
rc2 = uid[5];
}
if (rc1 != 0x90 || rc2 != 0x00)
throw new Exception("failure: " + rc1 + "/" + rc2);
byte[] result = new byte[4];
Array.Copy(uid, result, 4);
return result;
}
I had a look at following resources
http://ridrix.wordpress.com/2009/09/19/mifare-desfire-communication-example/
http://code.google.com/p/nfc-tools/source/browse/trunk/libfreefare/libfreefare/mifare_desfire.c?r=532
... and tried to do it like this:
byte[] outb = new byte[15];
int rc9 = Communicate(new byte[] { 0x60 }, ref outb);
outb always contains { 0x67, 0x00 } and not, as expected, { af 04 01 01 00 02 18 05 }.
Connect is sucessful, and SCardGetAttrib allows me to fetch the ATR. The Communicate method works with SCardTransmit. I can post the code if it helps.
Thanks for any pointer!
EDIT:
Thanks for the first answer! I changed the program as suggested:
byte[] outb = new byte[9];
int rc5 = Communicate(new byte[]{0x90, 0x60, 0x00, 0x00, 0x00, 0x00}, ref outb);
Now outb is { 0x91, 0x7E }. This seems to be better, 0x91 looking like an ISO 7816 response code, but unfortunately not 0x90, as expected. (I also had a look at the DESFIRE_TRANSCEIVE macro in the second link that continues reading if it receives 0xf2.) I tried a Google search for ISO 7816 APDU response codes, but had no success in decoding the error code.
EDIT 2:
I also found the following comment:
with an omnikey 5321 I get DESFire ATR 3B8180018080 UID 04 52 2E AA 47
23 80 90 00 [from apdu FFCA000000] All other apdu give 917E unknown
error
This explains my error code and gives me another hint, FFCA000000 looking quite similar to my other Mifare 1k string. So with FFCA000000 I get a 9 byte response that seem to contain the UID. Interestingly, the FFCA000000 code also works with the 1k cards, so maybe my solution is just to change the last 04 to 00 and deal with responses of different length. Right?
EDIT 3:
It seems the penny has dropped... 0x04 = 4 bytes response = too small for a 7 byte UID = response 917E = buffer too small :-)
This code seems to work:
int rc = Communicate(new byte[] { 0xff, 0xca, 0x00, 0x00, 0x00 }, ref uid);
if (rc != 0)
throw new Exception("failure: " + rc);
int rc1 = uid[uid.Length-2], rc2 = uid[uid.Length-1];
if (rc1 != 0x90 || rc2 != 0x00)
throw new Exception("failure: " + rc1 + "/" + rc2);
byte[] result = new byte[uid.Length - 2];
Array.Copy(uid, result, uid.Length - 2);
return result;
Any comments?
Cla=ff commands are pcsc part 3 commands. Ins=ca should work with any cl reader that is pcsc 2.0x compliant
Try the "Native wrapped" version of the first link you supplied instead. Your interface expects ISO 7816-4 style APDU's (as it returns an ISO 7816-4 status word meaning wrong length).

Java SecretKey and replicating it's behaviour in C#

I am trying to replicate the encryption logic found in a Java library in a C# application.
The Java contains two methods which I have managed to replicate in C#. I get the same results in each program for any set of data.
createKey(byte data1[], MessageDigest md);
createIV(byte data2[], MessageDigest md);
The logic to generate the key and IV in Java is as follows:
public Cipher getCipher(byte[] password) {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte keyData[] = createKey(byte[] password, md);
SecretKey secretKey =
SecretKeyFactory.getInstance("DESede").
generateSecret(new DESedeKeySpec(keyData[]));
IVSpec ivspec = createIV(secretKey.getEncoded(), md);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(1, secretKey, ivSpec, md);
return cipher;
}
Let's say I have the follow:
Java Key HEX: 9c 3a 79 df ba 49 86 0 ed 58 1 d8 9b a7 94 0 bb 3e 8f 80 4d 67 0 0
When I build the secretKey and then call secretKey.getEncoded() I get:
Java Encoded Key: : 9d 3b 79 df ba 49 86 1 ec 58 1 d9 9b a7 94 1 ba 3e 8f 80 4c 67 1 1
Because I don't know what the SecretKey is doing internally I don't know how to replicate this in C#.
My current C# code looks like this:
public static ICryptoTransform createCryptoTransform(String password)
{
ICryptoTransform ct = null;
byte[] keyData = createKey(password);
byte[] ivData = createInitialisationVector(keyData);
printByteArray("keyData", keyData);
printByteArray("ivData", ivData);
TripleDESCryptoServiceProvider tdcsp = new TripleDESCryptoServiceProvider();
tdcsp.Key = keyData; / This seems to be ignored by CreateEncryptor method below
tdcsp.KeySize = 192;
tdcsp.IV = ivData; // This seems to be ignored by CreateEncryptor method below
tdcsp.Mode = CipherMode.CBC;
tdcsp.Padding = PaddingMode.PKCS7; // PKCS5 and PKCS7 provide the same padding scheme
ct = tdcsp.CreateEncryptor(keyData, ivData);
return ct;
}
As you can see, I'm using the ivData[] created from the unencoded key.
Everything works, that is I get the same encrypted result, if I pass the same IV data in when creating the encryptor but unfortunately I cannot modify how it generates it's IVSpec.
What is SecretKey doing internally and how do I replicate this in C#?
DES (and DESede) both derive 56 bits of key material from 64 bit input. The remaining 8 bits are used as parity check bits here. The source code shows you how Java handles this, you can apply the same in C#.
See also the section at the beginning of FIPS 46-3.

Categories

Resources