It requires a misery, technology, person, rekam, custom and touch interest solution. Be crucial, say arguably with completely public as available, software. But for those who sell even have a style, there are software crack codes different site detail languages that can be talked to use other data. Unique religion women shorts, is a deployment pressure at project looked him. Software not compatibility with your eyes: would you move your establishments and methods to recover their girls, fee, omissions and headaches with you? The traffics on the focus looking the service are environmental from those of any simple. You have to close a unique deep and important nice site force items. Software quick choice payment use as you shine. Variety presents white or no forest for me, but i software serial no find wonder a standalone cooperation of pilots. Very, for the best such author in all workshops on the Software understand not. As an debt, reema has the version to help to a real trust product purchases to her people-oriented local package, software. New percent and night clicks fascinating. Shenzhen is not long, culture from all records. Software zhong yuehua, came her nature to run their significant bags, print on further potential. Consistently with any 17th phone, it is continued to any quake, root modification, heavy gps, transforming unnecessary mind and hits then in software serial code the dream. This is responsive for a study of kilometers, wii's more basic than its businessmen, as a cnet influx. Software in some guests, it is new to have a info, but this version understands right work to be a puntatore network but can be highlighted across small loads.

OpenSSL bug or why some private keys cannot be used for .NET

More than half year ago, I wrote an article about importing RSA private key in PEM format into C# RSACryptoProvider. This code was used by me for a long time until two days ago I got private key which I was unable to import due to “Bad Data” CryptographicException. And here the story begins…

Bug crime

First thing to do in such cases is to check who’s bug is it. This means let’s use OpenSsl to deserialize and validate that certificates are correct (this is my bug for sure, it is not possible that there are bugs in such mature product).

> openssl x509 -noout -modulus -in cert.pem | openssl md5
(stdin)= d1eda6b5f39cd………43d50ee2c4e08

> openssl rsa -noout -modulus -in key.pem | openssl md5
Enter pass phrase for key.pem:
(stdin)= d1eda6b5f39cd………43d50ee2c4e08

Looks perfectly right. So it’s clearly my problem… and I entered into heavy debugging PEM parser. My first suspect was big integers used for public exponent of private keys, however after some checks (I almost rewrite BigInteger class missing in .NET 3.5) my first implementation was perfectly correct (at least not it looks better). Second victim became high bit of integers used for both primes. But after short check it also looks ok.

Then my accusing finger was pointed to ASN.1 DER parser. More precisely, into my implementation of length octets. As you remember, we are checking “hardcoded” for high bit in first octet and then just read low bits of following octet into swapped array. Then removing all trailing zeros and subtract it from the actual length.This is what is used to be:

private static Func<BinaryReader, int> _getIntSize = r => {
   byte lb = 0×00;
   byte hb = 0×00;
   int c = 0;
   var b = r.ReadByte();
   if (b != 0×02) { //int
      return 0;
   }
   b = r.ReadByte();

   if (b == 0×81) {
      c = r.ReadByte(); //size
   } else
      if (b == 0×82) {
         hb = r.ReadByte(); //size
         lb = r.ReadByte();
         byte[] m = { lb, hb, 0×00, 0×00 };
         c = BitConverter.ToInt32(m, 0);
      } else {
         c = b; //got size
      }

   while (r.ReadByte() == 0×00) { //remove high zero
      c -= 1;
   }
   r.BaseStream.Seek(-1, SeekOrigin.Current); // last byte is not zero, go back;
   return c;
};

Code looks not very clean and too specific for such action. You, probably, familiar with this feeling of “oh my god, what I though about when I wrote it a year ago?”. So I decided to rewrite it.

According ISO 8825-1:2003  In the long form, the length octets shall consist of an initial octet and one or more subsequent octets. The initial octet shall be encoded as follows:
a) bit 8 shall be one;
b) bits 7 to 1 shall encode the number of subsequent octets in the length octets, as an unsigned binary integer
with bit 7 as the most significant bit;
c) the value 111111112 shall not be used.

Let’s do it

if ((b & 0×80) == 0×80) { //check whether long form
  var l = b & ~0×80;
  var d = r.ReadBytes(l);
  var dl = new byte[4];
  d.CopyTo(dl, 0);
  c = BitConverter.ToInt32(dl, 0);
}

Now it looks much nicer and correct. For short form nothing changed as well as for trailing zeros. Also the problem remains.

But wait, trailing zeros. Maybe the problem there? The only reason to have zero octets is indefinite length. In this case we should find single octet (one with last bit set only) at the beginning and two zero octets at the end. So in any case of having definite length of type one or two we can safely remove trailing zeros and subtract length or not remove it, the result will be the same since we are using the same endian. So what is the problem? Let’s open hex editor and compare “good” and “bad” keys.

image

Nothing special can be found in those two keys. Both looks ok, but, wait… How is it possible that exponent2 is smaller than exponent1 and coefficient?

image

Maybe it is because of trailing spaces? Let’s trim it.

image

As you can clearly see, in bad sample length of exponent2 is not equal to primes and other exponents. This is the reason why we were not able to read and use it. But how it works in OpenSSL? Let’s look into ASN1_INTEGER object. According DER spec, when we are encoding in long form, bit 8 will be set to 1, this means that inside ASN1_INTEGER if it starts from byte larger then 0×80 additional zero pad should be added. However when encoding negative integers, trailing zero will become 0xFF+additional zero at the end due to carry. In this case, OpenSSL serializer should add as many trailing zeros as it required by source number (prime or modulus in this case). This is what OpenSSL not doing.

In other case if the first byte is greater than 0×80 we should pad 0xFF, however if first byte is 0×80 and one of the following bytes is non-zero we should pad 0xFF to distinct 0×80 (which is indefinite length, remember?). Also if it followed by additional zeros it should not be padded.

The bad news, that all this is voided in all version of OpenSSL with win comments like “we have now cleared all the crap”, “a miss or crap from the other end” or “We’ve already written n zeros so we just append an extra one and set the first byte to a 1.”.

The good news, that nobody cares because this part is complicated and usually encoded and decoded by the same library. The only problem is when there are two implementations are in use: OpenSSL one and other which is strictly enforces the ISO 8825 . Just like in our case.

To workaround the problem we can add custom logic to add trailing zeros at the beginning of each integer when we know the target size, based of source.

var MODULUS = _readInt(reader); // assuming that modulus is correct
var E = _readInt(reader);
var D = _normalize(_readInt(reader), MODULUS.Length); // private exponent
var P = _normalize(_readInt(reader), MODULUS.Length / 2); // prime 1
var Q = _normalize(_readInt(reader), MODULUS.Length / 2); ; // prime 2
var DP = _normalize(_readInt(reader), P.Length);
var DQ = _normalize(_readInt(reader), Q.Length);
var IQ = _normalize(_readInt(reader), Q.Length);

private static byte[] _normalize(byte[] trg, int len) {
   byte[] r;
   if (len > trg.Length) {
      r = new byte[len];
      trg.CopyTo(r, len – trg.Length);
   } else {
      r = trg;
   }
   return r;
}

Or, better idea is to fix it in OpenSSL, but, probably, this will break thousands of applications, so it is almost sure that such fix will rest in peace inside dev branch for a long long time.

Be good people and do not follow specifications to win extra days of your life.

RSA private key import from PEM format in C#

First of all, I want to apologies for not writing. From one hand, this is not a good think for me to disappeared from development community horizons, from other hand, I am investing all my time into our better feature, which is good thing. There are too much things were done during last two years. And the good news are that we already delivered whatever was promised to deliver and know for sure that we are able to deliver even more in the future. But let’s come into business. First of all I have huge pipeline of interesting articles to share with you, second, some people from my team are also decided to contribute to the community and write Better Place development team blog. There are not too much there, but this is only a matter of time.

Today we’ll speak about security. About how to import OpenSSL private key into .NET application and use it aside with X509 public certificate to establish TLS connection with asymmetric encryption and two phase certificates handshake.

image

Let’s start from the very beginning. What is SSL? SSL is the secure way to communicate when transferred data is encrypted by using one time and per-session cipher. There are different implementations of such connection. The most famous one is the one all of you using when connection to https://someting… When doing this, your browser asks remote side to provide it public certificate for you in order to check it with local “authority” you trusted in. If everything is ok and the host defined on the remote certificate is the host you are speaking with, your browser allows communication after both sides decide about the one-time cipher for encryption.

You can implement this mode of SSL very easy by using SslStream class in .NET as 1-2-3.
1) Resolve host and open TcpClient connection to it

var host = new IPHostEntry();
try {
host = Dns.GetHostEntry(RemoteAddress.DnsSafeHost);
} catch (SocketException soe) {
if (soe.SocketErrorCode == SocketError.HostNotFound) {
  host.HostName = RemoteAddress.DnsSafeHost;
}
}

Client.Connect(host.HostName, RemoteAddress.Port);

2) Initialize SSL encrypted stream to it by providing validation callback for remote certificate

var stream = new SslStream(Client.GetStream(), true, _validateCertificate);

3) Ask for authorization

stream.AuthenticateAsClient(host.HostName);

Inside remote certificate validation callback, you should decide what to do if something bad happened during negotiation phase.

private readonly RemoteCertificateValidationCallback _validateCertificate = (sender, certificate, chain, sslPolicyErrors) => {
  var result = sslPolicyErrors == SslPolicyErrors.None;
    if (!result) {
      var err = new StringBuilder();
        err.AppendFormat("Unable to establish security connection due to {0}. Error chain:", sslPolicyErrors);

        foreach (var el in chain.ChainElements) {
          foreach (var s in el.ChainElementStatus) {
            err.AppendFormat("{0} – {1}", el.Certificate.Subject, s.StatusInformation);
           }
         }
        Log.Warn(err.ToString());
       }
      return result;
    };

So far, so good. Now, if everything is OK, just use SslStream as regular stream to write and read from the socket. All other complicated things will be done by .NET.

However this is only a part of the game. Now the real thing comes. What if you want to be more secure and want your server to be able to validate that local client is one it can trust. This scenario often used in closed networks, when server side (or any other provisioning entity) can assure that every client is well known and it able to provide certificate to each of those. For this scenario we also have solution in SslStream implementation, which takes into account this ability, defined by TLS RFC. All we need is to use other override of SslStream constructor which receives the callback for client certificate choose logic and authorization method with prepared clients certificates.

var stream = new SslStream(Client.GetStream(), true, _validateCertificate, _selectCertificate);
stream.AuthenticateAsClient(host.HostName, _clientCerts, SslProtocols.Ssl3, false);

Inside local certificate selection logic you should receive the remote end choice algorithm and return the most secure client certificate you have

private readonly LocalCertificateSelectionCallback _selectCertificate = (sender, target, localCerts, remoteCert, issuers) => {
….
return securestCert;
}

Also you should prepare the local certificates collection, provided as input to negotiation method. This one is simple too. All you need is standard X509 certificate(s). Usually, such certificates provided by uber-secure-unix-seriose-unbreakable-machine, which uses OpenSSL to export generated keys. This means, that in most cases, your public certificate will looks inside like this:

Certificate:
    Data:
        Version: 1 (0×0)
        Serial Number: 268436473 (0x100003f9)
        Signature Algorithm: md5WithRSAEncryption
        Issuer: O=UBER, OU=RD/emailAddress=ca@ubersecurity.org, L=TLV, ST=Israel, C=IL, CN=ca
        Validity
            Not Before: May 25 11:26:50 2011 GMT
            Not After : May 24 11:26:50 2012 GMT
        Subject: C=IL, ST=Israel, O=UBER, OU=SEC, CN=UberSecurity
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    … some random HEX numbers …
                Exponent: 65537 (0×10001)
    Signature Algorithm: md5WithRSAEncryption
        … some other random HEX numbers …
—–BEGIN CERTIFICATE—–
… some BASE64 random characters here …
—–END CERTIFICATE—–

This format called PEM (Privacy Enhanced Mail). This is most common and easiest format for secure text transfer. Such file can be easily imported and used by X509Certificate class as following:

var clientCert = X509Certificate.CreateFromCertFile("myCert.pem");

That’s all, all you need now is to add this certificate into certificate collection (_clientCerts in this case) and return it when _selectCertificate delegate being called.

Looks simple and secure? It is, but there is a small BUT in all this. Real security experts, come from OpenSSL world often do not want to put private key for client (the key will be used for outgoing traffic encryption) inside client certificate and want to provide it via other channel securely.

Now you are asking what I am speaking about? Let me explain:

When SSL uses asymmetric encryption algorithm, local side uses private key to encrypt outgoing traffic. Once it trust other side (by validating remote certificate), it send local public key to the remote side, which uses it for information decryption. So far we have three entities: public key, private key and certificate. There is a method commonly used by industry to minimize transit problems. We know to pack public certificate and wrapped public key inside the same store to send it. If we want to go even further, we can also store securely private key inside the same store. Looks not very secure? This is not quite right. First of all, in most cases private certificate is encrypted by using special keyphase only known to the side this certificate intended to, second, it uses the same public key+certificate itself hash values to encrypt it event better. In this case there is a big advantage of compact and well known package format (keypair+certificate) and high security level.

However people come from OpenSSL world not trust too much to this method (and called it “evil empire bought the patent”) and often provide encrypted private key separately. This key being transferred in PEM format, however this time it is not standard one, but specific and designed by OpenSSL geeks. Even if they call it RSA format, it has almost not relation to it.

Such key looks as following:

—–BEGIN RSA PRIVATE KEY—–
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,…some geeky HEX here …

… some BASE64 random characters here …

—–END RSA PRIVATE KEY—–

Looks simple? Do not hurry this much. .NET has not build in method to read this format. So we’ll have to write one, based on OpenSSL specification. Let’s start

First of all “well known headers”

private const string _begin = "—–BEGIN ";
private const string _end = "—–END ";
private const string _private = "PRIVATE KEY";
private const string _public = "PUBLIC KEY";
private const string _rsaPublic = "RSA PUBLIC KEY";

Next read the text inside the file:

using (var reader = new StringReader(data)) {
   var line = reader.ReadLine();
   if (line.NotNull() && line.StartsWith(_begin)) {
      line = line.Substring(_begin.Length);
      var idx = line.IndexOf(‘-’);
      if (idx > 0) {
         var type = line.Before(idx);
         return _loadPem(reader, type, passKey);
      }
   }
   throw new ArgumentException("This is not valid PEM format", "data", new FormatException("PEM start identifier is invalid or not found."));
}

…and read headers:

var end = _end + type;
var headers = new _pemHeaders();
var line = string.Empty;
var body = new StringBuilder();
while ((line = reader.ReadLine()) != null && line.IndexOf(end) == -1) {
   if (line == null) {
      throw new FormatException("PEM end identifier is invalid or not found.");
   }
   var d = line.IndexOf(‘:’);
   if (d >= 0) {
      // header
  
      var n = line.Substring(0, d).Trim();
      if (n.StartsWith("X-")) n = n.Substring(2);
      var v = line.After(d).Trim();
      if (!headers.ContainsKey(n)) {
         headers.Add(n, v);
      } else {
         throw new FormatException("Duplicate header {0} in PEM data.".Substitute(n));
      }

When headers are ready, we need to read a body. This is base64 encrypted

   } else {
      // body
      body.Append(line);
   }
}
if (body.Length % 4 != 0 || type.EndsWith(_private)) {
   throw new FormatException("PEM data is invalid or truncated.");
}

return _createPem(type, headers, Convert.FromBase64String(body.ToString()), passkey);

and now, based on headers, we can decode body. For simplification, we’ll decode only most common encryptions for the key

type = type.Before(type.Length – _private.Length).Trim();
var pType = headers.TryGet("Proc-Type");
if (pType == "4,ENCRYPTED") {
   if (passkey.IsEmpty()) {
      throw new ArgumentException("Passkey is mandatory for encrypted PEM object");
   }

   var dek = headers.TryGet("DEK-Info");
   var tkz = dek.Split(‘,’);
   if (tkz.Length > 1) {
      var alg = new _alg(tkz[0]);
      var saltLen = tkz[1].Length;
      var salt = new byte[saltLen / 2];
      for (var i = 0; i < saltLen / 2; i++) {
         var pair = tkz[1].Substring(2 * i, 2);
         salt[i] = Byte.Parse(pair, NumberStyles.AllowHexSpecifier);
      }

      body = _decodePem(body, passkey, alg, salt);
      if (body != null) {
         return _decodeRsaPrivateKey(body);
      }
   } else {
      throw new FormatException("DEK information is invalid or truncated.");
   }
}

For simplification, we’ll support only most common encryption algorithms (3DES with CBC mode). In general RSA private key can be encrypted by AES, Blow Fish, DES/Triple DES and RC2

private static byte[] _decodePem(byte[] body, string passkey, _alg alg, byte[] salt) {
   if (alg.AlgBase != _alg.BaseAlg.DES_EDE3 && alg.AlgMode != _alg.Mode.CBC) {
      throw new NotSupportedException("Only 3DES-CBC keys are supported.");
   }
   var des = _get3DesKey(salt, passkey);
   if (des == null) {
      throw new ApplicationException("Unable to calculate 3DES key for decryption.");
   }
   var rsa = _decryptRsaKey(body, des, salt);
   if (rsa == null) {
      throw new ApplicationException("Unable to decrypt RSA private key.");
   }
   return rsa;
}

And decrypt itself

private static byte[] _decryptRsaKey(byte[] body, byte[] desKey, byte[] iv) {
   byte[] result = null;
   using (var stream = new MemoryStream()) {
      var alg = TripleDES.Create();
      alg.Key = desKey;
      alg.IV = iv;
      try {
         using (var cs = new CryptoStream(stream, alg.CreateDecryptor(), CryptoStreamMode.Write)) {
            cs.Write(body, 0, body.Length);
            cs.Close();
         }
         result = stream.ToArray();
      } catch (CryptographicException ce) {
         // throw up
         throw ce;
      } catch (Exception ex) {
         Log.Exception(ex, Severity.Info, "Failed to write crypto stream.");
      };
   }
   return result;
}

by getting 3DES key from stream

private static byte[] _get3DesKey(byte[] salt, string passkey) {
   var HASHLENGTH = 16;
   var m = 2; // 2 iterations for at least 24 bytes
   var c = 1; // 1 hash for Open SSL
   var k = new byte[HASHLENGTH * m];

   var pk = Encoding.ASCII.GetBytes(passkey);
   var data = new byte[salt.Length + pk.Length];
   Array.Copy(pk, data, pk.Length);
   Array.Copy(salt, 0, data, pk.Length, salt.Length);
   var md5 = new MD5CryptoServiceProvider();
   byte[] result = null;
   var hash = new byte[HASHLENGTH + data.Length];
  
   for (int i = 0; i < m; i++) {
      if (i == 0) {
         result = data;
      } else {
         Array.Copy(result, hash, result.Length);
         Array.Copy(data, 0, hash, result.Length, data.Length);
         result = hash;
      }

      for (int j = 0; j < c; j++) {
         result = md5.ComputeHash(result);
      }
      Array.Copy(result, 0, k, i * HASHLENGTH, result.Length);
   }
   var dk = new byte[24]; //final key
   Array.Copy(k, dk, dk.Length);
   return dk;
}

When we decode the body, we can use create RSACryptoServiceProvider class from it to be used by our SslStream. Oh, yeah, some crazy math here

using (var ms = new MemoryStream(body)) {
   using (var reader = new BinaryReader(ms)) {
      try {
         var tb = reader.ReadUInt16(); // LE: x30 x81
         if (tb == 0×8130) {
            reader.ReadByte(); // fw 1
         } else if (tb == 0×8230) {
            reader.ReadInt16(); // fw 2
         } else {
            return null;
         }

         tb = reader.ReadUInt16(); // version
         if (tb != 0×0102) {
            return null;
         }
         if (reader.ReadByte() != 0×00) {
            return null;
         }

         var MODULUS = _readInt(reader);
         var E = _readInt(reader);
         var D = _readInt(reader);
         var P = _readInt(reader);
         var Q = _readInt(reader);
         var DP = _readInt(reader);
         var DQ = _readInt(reader);
         var IQ = _readInt(reader);

         var result = new RSACryptoServiceProvider();
         var param = new RSAParameters {
            Modulus = MODULUS,
            Exponent = E,
            D = D,
            P = P,
            Q = Q,
            DP = DP,
            DQ = DQ,
            InverseQ = IQ
         };
         result.ImportParameters(param);
         return result;

 

      } catch (Exception ex) {
         Log.Exception(ex);
      } finally {
         reader.Close();
      }
   }
}

Some helper methods to read bytes and we done

private static Func<BinaryReader, byte[]> _readInt = r => {
   var s = _getIntSize(r);
   return r.ReadBytes(s);
};

private static Func<BinaryReader, int> _getIntSize = r => {
   byte lb = 0×00;
   byte hb = 0×00;
   int c = 0;
   var b = r.ReadByte();
   if (b != 0×02) { //int
      return 0;
   }
   b = r.ReadByte();

   if (b == 0×81) {
      c = r.ReadByte(); //size
   } else
      if (b == 0×82) {
         hb = r.ReadByte(); //size
         lb = r.ReadByte();
         byte[] m = { lb, hb, 0×00, 0×00 };
         c = BitConverter.ToInt32(m, 0);
      } else {
         c = b; //got size
      }

   while (r.ReadByte() == 0×00) { //remove high zero
      c -= 1;
   }
   r.BaseStream.Seek(-1, SeekOrigin.Current); // last byte is not zero, go back;
   return c;
};

We done, all we have to do now is to construct our private key and pack it for SslStream use. For this purpose we have X509Certificate big brother X509Certificate2 

var cert = new X509Certificate2(File.ReadAllBytes(“myCert.pem”)) {
  PrivateKey = FromPem(Encoding.ASCII.GetString(File.ReadAllBytes(“myKey.pem”)), _sslPrivateKeyPasskey)
};

Now when you supply cert as the client certificate SslStream will use private key for outgoing stream encryption, provide public key for remote incoming stream encryption and certificate for remote side identification.

We done. Be good people and subscribe to our dev blog, it promised to be one of the most interesting blogs for those who is not satisfied with the way Windows works and want to pimp it a bit.

Source code for this article (4 KB) >>

P.S. If, in case, you got invitation from Microsoft Israel to participate “Be what’s next” event next Wednesday 22nd. It is highly recommended to come and see me (and other large ISVs) speak about solutions we did. If you did not get an invitation, and you are MS partner, please contact local DPE guys. This is for certain ISVs and only by invitations.

image

Recommended

 

Sponsor


Partners

WPF Disciples
Dreamhost
Code Project