Securing access to the Cisco routers

This article is a translation of my original post written in Italian by the end of 2008 and published on areanetworking.it. As you may know, my working experience and specialized studies have provided me with knowledge and skills in the analysis, implementation, optimization, troubleshooting and documentation of LAN/WAN network systems with strong “hands on” technical knowledge. However in that period I was programming only and it was a long time that I didn’t seriously configured a router. One of my old customers commissioned a configuration of three routers, one in the main office and other two in branches. It was for me something new and challenging because I never before configured fully mashed VPN connection so I wanted to get a bit further and configure also other aspects in a more detailed way. I checked my previous configurations an no one of them satisfied me completely. So I pulled down all the documentation I found and started studying. The following lines are the result of that.

Let´s start, first thing that I did is to separate the virtual terminal lines in two groups, the first one will be reserved for the connections from external networks meanwhile the second will be used for the connections from internal networks. From the external networks will be only possible to connect via SSH where from the internal networks telnet will also be an option.
To achieve this I have given the following commands:

line vty 0 1
line vty 2 4

In this post I will not get in depth about SSH protocol, if you are interested in different version of the protocol, you will find many information’s by Googleing the keywords. If you yet do not know anything about it, it is a way to connect to the terminal where all the data exchanged between client and server are encoded. To achieve that let´s give the following commands:

line vty 0 1
    transport input ssh
line vty 2 4
     transport input telnet ssh

Now we need to make sure that we set up the hostname and the domain name, otherwise, generating the cryptography key will fail. If there is the default hostname set, it would be advisable to change it. Both can be set with the following commands:

hostname MyRouter
ip domain-name majcica.com

Following task is generating the cryptography key base on whom the SSH connection will be secured (will be used to encrypt the data).

crypto key generate rsa

Once you issue this command you will be asked to choose the key size. I will suggest you to leave the default value (512) which is more than enough for 99% of cases. When this operation is over, you will receive the message that SSH is activated and that the current version is 1.5. Unfortunately there is no SSH 1.5 and you may be confused, however this is the way Cisco indicates that SSH version 1 is active. You can always check the current active version by the following command:

show ip ssh

In case it says 1.99 it means that both SSH1 and SSH2 are enabled, meanwhile if the result is 2.0 it means that only the version 2 is enabled.

In order to prevent the access to a virtual terminal interfaces that accepts the telnet connection let’s suppose that our internal network is 10.10.10.024 (10.10.10.x network with a mask 255.255.255.0). Because of that we will create an access list that accepts the traffic only from internal network and block the traffic from all other sources. In order to achieve this is sufficient issuing the following command:

access-list 15 permit 10.10.10.0 0.0.0.255 log

and then apply this access list to a proper virtual terminal interface:

line vty 2 4
   access-class 15 in

If we try now to access the router via telnet from a network that is different than our internal, the request will time out. But if we try to access it from an external network via SSH the router will replay and we will be able to establish a connection.
Next step is setting the time out (60 seconds is a reasonable value), enabling SSH version 2, setting the interface from which route will replay to SSH request (outside interface, in our case are ATM0.1 – ADSL connection) and a maximum authentication retries.

ip ssh time-out 60
ip ssh authentication-retries 5
ip ssh source-interface ATM0.1
ip ssh version 2

Initially I tough that this is sufficient but once I configured logging, I found that, meanwhile testing, there was no trace about the who and when connected to the router. As I believe that this are information’s that we should log, I made a little research and found the following commands:

login on-failure log
login on-success log

First one enable logs on failed attempts meanwhile the second one enables the log on successful log in’s.
Till here everything was fine until couple of days later I consulted the log files and found that someone was attempting brute force log in attacks on my router. I needed a solution for this problem, and limiting the access via ACL was not an option as I often connect to the router via a dynamically obtained IP address. At the end I found that we can set a dynamic block for the users who fails authenticating a certain amount of time in a determinate time interval. Basically if we want to prevent that user receive a response from SSH service for 300 seconds in case he misses the password three times in 30 sec, we need to issue the following command:

login block-for 300 attempts 3 within 30

This for me was sufficient, and it seems a decent practice, however if you feel paranoid you can still change the default SSH port, apply restrictive ACL, etc.
Following you will find an example extract from syslog showing a brute force attack and the quite mode in action.

2009-01-27 08:28:48	Local7.Warning	10.10.10.254	81: 000086: Jan 27 08:28:47.028 Berlin: %SEC_LOGIN-4-LOGIN_FAILED:
Login failed [user: nagios] [Source: 210.192.123.204] [localport: 22] [Reason: Login Authentication Failed]
at 08:28:47 Berlin Tue Jan 27 2009

2009-01-27 08:28:53	Local7.Warning	10.10.10.254	82: 000087: Jan 27 08:28:52.194 Berlin: %SEC_LOGIN-4-LOGIN_FAILED:
Login failed [user: user] [Source: 210.192.123.204] [localport: 22] [Reason: Login Authentication Failed]
at 08:28:52 Berlin Tue Jan 27 2009

2009-01-27 08:28:57	Local7.Warning	10.10.10.254	83: 000088: Jan 27 08:28:57.169 Berlin: %SEC_LOGIN-4-LOGIN_FAILED:
Login failed [user: test] [Source: 210.192.123.204] [localport: 22] [Reason: Login Authentication Failed]
at 08:28:57 Berlin Tue Jan 27 2009

2009-01-27 08:28:57	Local7.Alert	10.10.10.254	84: 000089: Jan 27 08:28:57.169 Berlin: %SEC_LOGIN-1-QUIET_MODE_ON:
Still timeleft for watching failures is 19 secs, [user: test] [Source: 210.192.123.204] [localport: 22]
[Reason: Login Authentication Failed] [ACL: sl_def_acl] at 08:28:57 Berlin Tue Jan 27 2009

2009-01-27 08:33:58	Local7.Notice	10.10.10.254	85: 000104: Jan 27 08:33:57.083 Berlin: %SEC_LOGIN-5-QUIET_MODE_OFF:
Quiet Mode is OFF, because block period timed out at 08:33:57 Berlin Tue Jan 27 2009

I didn’t went in detail on all steps supposing that you have basic knowledge on how to operate on Cisco IOS. All the information’s I got from Cisco.com. Also this procedure where valid by the end of 2008 when the original article was written. However it should not be drastically changed in the newest version of IOS. If so, check the documentation on cisco.com and eventually post your question’s in the comments.

Joyful configuring!

Certificates to DB and Back – Part 1

Foreword

I wrote this article at the beginning of 2011. As I was developing a WCF client application which was using Message Security with Mutual Certificates Exchange and because of a particular application request, I developed this part of code in order to facilitate maintenance and deployment of the interested application.

Now, if you are trying just simply to import/export the complete certificate and not recreate the certificate base on the his signature and the private key, framework will do already everything you need.

// Create new cert from the file and export it to a byte array
X509Certificate2 cert = new X509Certificate2(@"Cert.pfx", "password",
    X509KeyStorageFlags.Exportable);
byte[] originalExported = cert.Export(X509ContentType.Pfx, "password");

// Create new cert and import it from the byte array
X509Certificate2 clonedCert = new X509Certificate2();
clonedCert.Import(originalExported, "password", X509KeyStorageFlags.Exportable);

// export the cloned certificate with a differenet password
byte[] exported = clonedCert.Export(X509ContentType.Pfx, "newpassword");

// save the new certificate to a file
using (Stream stream = File.Create(@"E:\ClonedCert.pfx"))
{
    stream.Write(exported, 0, exported.Length);
}

So if you do not need to combine your private.key and public.cer on the runtime you do not need to read further, otherwise continue and you may find the solution to your challenge.

You can download the source code here.

Table of contents

Certificates to DB and Back – Part 1

  1. Introduction
  2. Getting started
  3. Creating Certificates

Certificates to DB and Back – Part 2

  1. Loading the Certificate
  2. Decoding RSA Private Key
  3. Creating the X509Certificate2
  4. Demo Application
  5. Further Improvements
  6. License

Introduction

In today’s applications, the use of web services is constantly growing. In the web services panorama, there are different ways of managing the authentications. One of the common methods is mutual certificates exchange.

Imagine that your application uses a web service that needs a customer’s certificate in order to correctly authenticate with the endpoint. Now, you can explain to your customer how to create the certificate, by generating one, the RSA key, and then creating a pfx container, positioning pfxcontainer file in the suggested folder, then inserting in your application the correct path to the pfx file. Hmm, it is a lot of work and is quite complicated. And what if you need to deploy it on many PCs for the same customer and your service is accessed directly from the client? If you exclude tricks, there is a lot of extra work.

What if you store only the pem strings of your certificate and private key, together with the password in database? Nice, but creating a valid X509Certificate object that contains the private key on .NET is not a trivial task. Don’t worry, this guide can help you.

In case some of the terms used in this introduction are not clear or known to you, but you still want to follow this guide, you will find some answers that will help you at the links given below:

Getting Started

In order to start and try the solution proposed in this guide, you need Microsoft Visual Studio 2010 in any of his many versions. This code will work even on previous versions of Visual Studio editions as 2008 or even 2005, but you’ll need to set up the project on your own, based on the code I suggested.

In order to create your own certificate, you can use your favourite tools but I will suggest you OpenSSL. You can download it from the following address:

Before installing OpenSSL, assure that you have installed the proper version of Visual C++ 2008 Redistributables. Once you have installed these libraries, you can proceed by installing OpenSSL.

Creating Certificates

Before creating a certificate, we need to generate the private key. Achieving this with OpenSSL is really simple. Open your command prompt window and get yourself into the bin directory under the folder where you installed OpenSSL (usually C:Program FilesOpenSSL).

Now execute the following command:

opensslgenrsa 1024 > private.key

Once executed, you should see something like this:

Check in that folder, there should be a new file called private.key. It is the RSA key on which the certificate will be based. Now let’s create the certificate. Execute the following command.

opensslreq -new -x509 -nodes -sha1 -days 1100 -key private.key > public.cer

As soon as you hit enter, you will be prompted for a couple of questions about the details with whom the certificates attributes will be populated. Once finished, you’ll see the following:

Congratulations! Your certificate is done!

Certificate is represented as Base64 encoded DER certificate, enclosed between “—–BEGIN CERTIFICATE—–” and “—–END CERTIFICATE—–“. Same is for the RSA signature key. For more details, consult:

Now, we can use a PKCS format file container in order to store both, public certificate and private key. This is not the goal of our project, but for the completeness I’ll show you how to do that via OpenSSL.

With the following command, you will create the required:

openssl pkcs12 -export -in public.cer -inkeyprivate.key –out cert_key.p12

After executing this command, you will get prompted about the Export password, this password will be used to encrypt your private key, so make it complex and unique.

Now you can load it to the X509Certificate2object:

string certificatePath = @"cert_key.p12";
string certificatePassword = "password";
X509Certificate2 clientCertificate = new X509Certificate2(certificatePath, certificatePassword);

You can download the source code here.

In the next steps, we will replicate this behavior directly from code.
continue…

Certificates to DB and Back – Part 2

This is a second part of the article, if you missed the previous post you can find it here:
Certificates to DB and Back – Part  1

Table of contents

Certificates to DB and Back – Part 1

  1. Introduction
  2. Getting started
  3. Creating Certificates

Certificates to DB and Back – Part 2

  1. Loading the Certificate
  2. Decoding RSA Private Key
  3. Creating the X509Certificate2
  4. Demo Application
  5. Further Improvements
  6. License

Loading the Certificate

If only a certificate is a problem, X509Certificate2 class will do the job. With the following code, you can simply load the certificate:

string certificatePath = @"public.cer";
X509Certificate2 clientCertificate = new X509Certificate2(certificatePath);

Or if you want to load it directly from the string:

string publicCertString = "-----BEGIN CERTIFICATE----- MCIISFSDFEESd etc. example";
X509Certificate2 clientCertificate = 
	new X509Certificate2(Encoding.UTF8.GetBytes(publicCertString));

Now, the framework offers already much. But this is not enough. If we want to load a certificate from a certificate file, we can do it. Otherwise, if we want to load the certificate and private key from the pfx or pkcs12 container, we can do it. But what if we want to load dynamically the certificate and private key, choose the encryption password and use our object? The X509Certificate2 doesn’t offer an overload of constructor like X509Certificate2(string certFileName, string privateKeyFile, string password) or the similar overload that will accept a byte array in order to correctly initialize this object. In this case, we need to do some dirty work. So let’s do it.

Decoding RSA Private Key

I will not get into details on how the RSA key is decoded, you have the source code plain of comments, so if interested, read it, it says more than a thousand words. In order to decode the private key, we will use DecodeRsaPrivateKey method which will return RSACryptoServiceProvider instance representing our private key.

Creating the X509Certificate2

As described in the previous chapters, I will use the default X509Certificate2 constructor in order to create the certificate. Afterwards, I will assign the decoded RSA private key as RSACryptoServiceProvider to the X509Certificate2 instance property PrivateKey. If everything went well, we will have the proper instance of X509Certificate2 certificate container, containing both, the certificate and the key, encoded with chosen password. Here is the code sample:

byte[] certBuffer = Helpers.GetBytesFromPEM(publicCert, PemStringType.Certificate);
byte[] keyBuffer  = Helpers.GetBytesFromPEM(privateKey, PemStringType.RsaPrivateKey);

X509Certificate2 certificate = new X509Certificate2(certBuffer, password);

RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
certificate.PrivateKey = prov;

I forgot to mention the helper method GetBytesFromPEM, which “cleans” the string from header and footer information.

Demo Application

In the demo application, you can see how to load the described components, create the X509 certificate, persist and reload everything. The interface is just an example, because that is rudimental, simple and incomplete. But hey, this is a demo, and for a demo is already too much! The important stuff is under the hood, that’s in what you should be interested.

Here is a screenshot:

Further Improvements

How to improve this code? Well test it, I have some doubts about creating Key Containers, and I will test it too. Any new discovery will be promptly noticed and the article will be updated.

Other things that are on my mind are creating the extension methods to the X509Certificate2 in order to make code look simpler and cleaner.

Perhaps including these methods in your applications’ framework cryptography library? Any idea is welcome so if you have any, feel free to contact me or to post the comment.

License

The idea and realisation are made completely by the author. The method DecodeRsaPrivateKey and all the code used inside were kindly provided by Mr Dan Maser; Dan, thanks for providing the code and for all the support. The way that RSACryptoServiceProvider is initialized is code written by me in order to overcome the encountered problems with .NET 4.0, all the traits-based size stuff and byte alignment are written by Dan Maser, but, speaking with Dan, we came to the following. The core of the ASN.1 parsing code came from a stackoverflow.com article. After analysing the code and after doing further research, I came across what I believe to be its original source, which is http://www.jensign.com/opensslkey/opensslkey.cs. The top of that file does include a standard copyright line “Copyright (C) 2008 JavaScience Consulting”. I didn’t knowingly use any copyrighted information when I originally wrote the code, but now it’s kind of fuzzy. There’s no direct indication I can find about any limitations on using or modifying the code directly on that website. At the bottom of http://www.jensign.com/JavaScience/cryptoutils/index.html, it does say “NOTE: These utilities and sample code are made available as-is with no support or guarantee of performance. They are intended to demonstrate specific technical implementation details with minimal error checking. Use at your own risk.” which does seem to directly suggest that one can use the published utilities, at albeit one’s own risk.

However, I think it is ok, but I guess you never know with the lawyers. If you find out that this code breaks the law, please contact me promptly and all code, as well as the article, will be removed.

If this is not the case, the article, along with any associated source code and files, is licensed under Microsoft Public License (MS-PL).

Joyful programming!