Pages

Wednesday, February 12, 2014

Fedora/Hydra/Blacklight on SmartOS (Part 2; Installing SmartOS, ZFS & SMF configuration)

Installation of SmartOS

SmartOS is a unique operating system provided by Joyent Inc.. Based on IllumOS its primary goals is to provide a bare metal VM manager coupled with speed, scaling & redundancy.

First things first, we need to obtain the latest USB image (I tried to use the DVD/CD disk option and a USB key was easier to work with due to space constraints within /).

Now we need to create a bootable USB key from the image we just downloaded. Joyent is kind enough to provide very useful documentation on how to accomplish this.

Installation will ask a couple of questions about networking & which disk to use a within your zpool configuration. My recommendation (if you have more than one disk) is to only use the first disk as we will be providing a more robust and optimal zpool configuration after our installation.

ZFS Configuration

At this point you should have a working SmartOS installation with one disk configured as a zpool. This example of configuring the zfs zpool is to allow for the following options:

  1. Primary raidz3-0 (zone)
  2. Hot swappable drives
  3. Dedicated mirrored caching drives (SSD disk types for performance)
  4. Dedicated mirror disks of ZIL block reads/writes

This example does involve a system that utilizes quite a few physical drives, both mechanical & SSD types. SSD is used for the caching drive only as it is to assist in performance of commonly accessed data.

Fig. 1 shows the command used to assemble the zpool as exampled in Fig. 2.

Fig. 1

$ zpool create zones raidz3 c0t0d0 c0t1d0 c0t2d0 c0t3d0 spare c1t0d0 c1t1d0 cache mirror c2t0d0 c2t1d0 log mirror c3t0d0 c3t1d0

Fig. 2

$ zpool status
  pool: zones
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        zones       ONLINE       0     0     0
          raidz3-0  ONLINE       0     0     0
            c0t0d0  ONLINE       0     0     0
            c0t1d0  ONLINE       0     0     0
            c0t2d0  ONLINE       0     0     0
            c0t3d0  ONLINE       0     0     0
        spares
          c1t0d0    AVAIL       0     0     0
          c1t1d0    AVAIL       0     0     0
        cache
          mirror-0
            c2t0d0    ONLINE       0     0     0
            c2t1d0    ONLINE       0     0     0
        logs
          mirror-1
            c3t0d0    ONLINE       0     0     0
            c3t1d0    ONLINE       0     0     0

errors: No known data errors

For more information on managing zpool's I recommend the documentation as it gives you the necessary information on various topics such as swapping out bad primary disks for disks that were designated as hot swappable spares, etc.

SMF Configuration

Next up is to tune the startup services and get the SmartOS ready for our Fedora/Hydra/Blacklight VM instances.

Because SmartOS runs entirely in memory any configuration options are to be placed in the default /opt/custom/smf which is mounted within the OS from the zpool configuration. SmartOS provides some documentation on this here.

I disabled quite a few services that are enabled by default as for the host OS they will not be necessary. See Fig 3. for contents of /opt/custom/smf and Fig 4. for a sample configuration file used to execute the svcadm command to shutdown services.

Fig. 3

$ ls -lah 
total 58
drwxr-xr-x   2 root     root          11 Feb 11 14:27 .
drwxr-xr-x   6 root     root           7 Feb 11 18:40 ..
-rw-r--r--   1 root     root        1.3K Feb  7 15:30 bash-history-link.xml
-rw-r--r--   1 root     root        1.1K Feb  7 15:46 disable-bind.xml
-rw-r--r--   1 root     root        1.1K Feb  7 15:46 disable-inetd.xml
-rw-r--r--   1 root     root        1.1K Feb  7 15:46 disable-multicast.xml
-rw-r--r--   1 root     root        1.1K Feb  7 15:46 disable-ntp.xml
-rw-r--r--   1 root     root        1.1K Feb 11 13:18 disable-sendmail-client.xml
-rw-r--r--   1 root     root        1.1K Feb 11 13:19 disable-sendmail.xml
-rw-r--r--   1 root     root        1.1K Feb 11 14:29 enable-ipf.xml
-rw-r--r--   1 root     root        1.3K Feb  7 15:29 inputrc-link.xml

Fig 4.

$ cat disable-sendmail.xml
<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='export'>
  <service name='global/disable-sendmail' type='service' version='0'>
    <create_default_instance enabled='true'/>
    <single_instance/>
    <dependency name='network' grouping='require_all' restart_on='error' type='service'>
      <service_fmri value='svc:/milestone/network:default'/>
    </dependency>
    <method_context/>
    <exec_method name='start' type='method' exec='svcadm disable sendmail' timeout_seconds='60'/>
    <exec_method name='stop' type='method' exec='svcadm enable sendmail' timeout_seconds='60'/>
    <property_group name='startd' type='framework'>
      <propval name='duration' type='astring' value='transient'/>
      <propval name='ignore_error' type='astring' value='core,signal'/>
    </property_group>
    <property_group name='application' type='application'/>
    <stability value='Evolving'/>
    <template>
      <common_name>
        <loctext xml:lang='C'>Disable sendmail services</loctext>
      </common_name>
    </template>
  </service>
</service_bundle>

Next we will create the necessary networking environment for our VM's and begin to setup our Fedora/Hydra/Blacklight stack

Monday, February 10, 2014

Fedora/Hydra/Blacklight on SmartOS (Part 1; Background, Research & Planning)

Software Background

ContentDM is a digital collection asset management software used by a majority of libraries. For a complete list of features etc. please take a look at their informational page.

An alternative to using ContentDM is to use a combination of FOSS projects:

  1. Fedora Commons: Digital content management
  2. Solr: Enterprise search platform (this will require a separate server)
  3. Project Blacklight: Discovery interface for Solr
  4. The Hydra Project: Front end component

Planning & Research

My research on the infrastructure used for these alternatives has pointed towards a simple operating system architecture of any number of linux distributions.

Currently our ContentDM infrastructure was given a five year life cycle. That was ten years ago. And due to the large digital collection puts the current system handling approximately ten terabytes. Currently our digital collection consists of a very large image and document library of various formats.

While ten terabytes today is not much, we also have close to sixty terabytes of video objects we wish to integrate.

That would bring the current project storage to a whopping seventy terabytes. The lifecycle for the project is again to be five years, but due to the amount of objects the solution will need to scale exponentially.

From a purely dev-ops perspective the traditional route is to implement a SAN, place a load balancer in front of a group machines, keep them synced, backed up and monitored and your up and running.

This project however will use a different strategy; implement a SAN, use a load balancer, fire up Smart-OS, use a KVM/Zone per application (Fedora/Hydra/Solr/Blacklight), use ACL's per VM instance, point all storage for Fedora (KVM/Zones) to the SAN, keep them synced, backed up and monitored.

The infrastructure

The planned environment:

  1. Scalability for physical hardware:
    • HAProxy can be used in front of any physical Smart-OS VM server and physical Solr servers, see for details
  2. Scalability for KVM/Zones:
    • Fedora VM instances can be added to each server instances quickly as the need arises and can use an NFS share on the SAN
    • Hydra VM instances can also be added to each server instance as new hardware is added to each server (memory, disks etc)
    • Blacklight VM instances can also be spun up when the need arises
  3. Security:
    • Fedora/Blacklight VM instances can be restricted to host based ACL's for internal VM networking only
    • Hydra VM instances can be publicly accessed per each Smart-OS host installation creating an internal DMZ per server

Thursday, January 9, 2014

Speeding up nmap with node.js

The tool; nmap

When it comes to network host discovery, enumerating details on remote hosts such operating system, device type and open ports nmap is the tool the majority of sys admins, hackers, pen testers & script kiddies turn to first.

I consider it a great piece of software, however I also find it painfully slow.

Performance

While nmap has many options for tuning the performance of a scan I thought it might be a worthy challenge to increase this. The current documentation has this to say about performance of nmap scans...

While Nmap utilizes parallelism and many advanced algorithms to accelerate these scans, the user has ultimate control over how Nmap runs.

Fair enough, lets see what we can do shall we?

node-libnmap

While far from a complete solution, the current version (v0.0.3) of node-libnmap dramatically speeds up scans.

It does this by creating equally distributed scan blocks based on the number of hosts per subnet range and the number of CPU cores on the machine. For example; the application generates a total set of blocks equal to 32 (8 core processor) for a machine residing on a network cidr of 10.0.2.0/24. Example:

nmap -sn -oG - 10.0.2.1-31
nmap -sn -oG - 10.0.2.33-63
nmap -sn -oG - 10.0.2.65-95
nmap -sn -oG - 10.0.2.97-127
nmap -sn -oG - 10.0.2.129-159
nmap -sn -oG - 10.0.2.161-191
nmap -sn -oG - 10.0.2.193-223
nmap -sn -oG - 10.0.2.225-255

The next step uses nodes thread model to create a new child process executing asynchronously (with the help of the async module) for every block generated.

benchmarks

Here are some preliminary benchmarks and samples

The results here are all coming from a virtual environment with limited system resources but should give an overall picture of performance of the scans. My VM environment is using 8 cores with 4 threads per core given a total returned from require('os').cpus.length = 32.

Nmap host discovery

$ time nmap -sn -oG - 10.0.2.0/24
# Nmap 5.51 scan initiated Wed Jan  8 18:54:07 2014 as: nmap -sn -oG - 10.0.2.0/24
Host: 10.0.2.2 ()       Status: Up
Host: 10.0.2.3 ()       Status: Up
Host: 10.0.2.15 ()      Status: Up
# Nmap done at Wed Jan  8 18:54:26 2014 -- 256 IP addresses (3 hosts up) scanned in 19.33 seconds

real    0m19.339s
user    0m0.052s
sys     0m0.080s

Nmap host discovery using node-libnmap

$ time node test/run.js 
{ adapter: 'eth0',
  properties: 
   { address: '10.0.2.15',
     netmask: '255.255.255.0',
     family: 'IPv4',
     mac: '52:54:00:12:34:56',
     internal: false,
     cidr: '10.0.2.0/24',
     hosts: 256,
     range: { start: '10.0.2.1', end: '10.0.2.254' } },
  neighbors: [ '10.0.2.2', '10.0.2.3', '10.0.2.15' ] }

real    0m3.323s
user    0m0.326s
sys     0m0.412s

And an example with multiple adapters on multiple 802.11q segments

$ time node test/run.js 
[ { adapter: 'eth0',
    properties: 
     { address: '10.0.2.15',
       netmask: '255.255.255.0',
       family: 'IPv4',
       mac: '52:54:00:12:34:56',
       internal: false,
       cidr: '10.0.2.0/24',
       hosts: 256,
       range: { start: '10.0.2.1', end: '10.0.2.254' } },
    neighbors: [ '10.0.2.2', '10.0.2.3', '10.0.2.15' ] },
  { adapter: 'eth1',
    properties: 
     { address: '192.168.2.15',
       netmask: '255.255.255.128',
       family: 'IPv4',
       mac: '52:54:00:12:34:57',
       internal: false,
       cidr: '192.168.2.0/25',
       hosts: 128,
       range: { start: '192.168.2.1', end: '192.168.2.254' } },
    neighbors: [ '192.168.2.2', '192.168.2.3', '192.168.2.15' ] } ]

real    0m3.447s
user    0m0.493s
sys     0m0.796s

Mileage may vary

Tuesday, December 31, 2013

node.js, express.js & the redis sessions store with encryption

node.js

For those of you that are already familiar with the wonderful language that is node.js please read ahead. For those that are not, lets close the gap.

Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

express.js

Much like any other web or interpreted language (Ruby, PHP, Python etc), node.js also has existing framworks to cut down development time. express.js is one of those frameworks.

One of the very nice things about express.js is performing operations such as the following:

$ npm install -g express
$ express --sessions --css stylus --ejs myapp
     
  create : myapp
  create : myapp/package.json
  create : myapp/app.js
  create : myapp/public
  create : myapp/public/javascripts
  create : myapp/public/images
  create : myapp/public/stylesheets
  create : myapp/public/stylesheets/style.styl
  create : myapp/routes
  create : myapp/routes/index.js
  create : myapp/views
  create : myapp/views/index.ejs

install dependencies:

$ cd myapp && npm install

run the app:

$ node app

What the above output performs is create a new project (in the exampled named 'myapp') that contains a very simple web application complete with stylesheets, HTML & the code necessary to run a web server application.

In addition their repo contains numerous project examples to help you get your own project up and running quickly.

redis

If your not familiar with redis perhaps an alternate tool such as memcached is more familiar. Both are software which can be used to serve content quickly and efficiently as they do not utilize disk writes etc and instead store the key/value data within memory making them extremely fast and useful for things such as 'session storage'.

Now that we have some background on redis, lets go back to talking about express.js. The express.js framework is simply a high level API providing access to the connect.js middleware for node.js.

sessions

This particular concent is in its simplest form the earliest method of allowing for persistant variable use within the HTTP 1.0 protocol. Because HTTP is considered a 'stateless' protocol developers needed a way to keep information from one page request to the next.

Use of sessions within the express.js framework is very easy. See below for a simple example of its use within the project we created earlier.

app.use(express.cookieParser());
app.use(express.session({secret: 'kung fu'}));

connect.js custom session store

There are many reasons one might want to use a custom session store. From the connect.js session manual:

Warning: connection.session() MemoryStore is not designed for a production environment, as it will leak memory, and will not scale past a single process.

As noted to ensure the scalability and stability of any web application requiring use of sessions it is best to utilize a custom session store.

Not to worry there are plenty of quality session store middleware modules we can choose from.

connect-redis-crypto

The connect-redis-crypto module is based on the connect-redis module which provides a transparent method of getting/setting/destroying server side sessions from connect.js middlware and implemented within the express.js framework.

The reason why connect-redis-crypto exists is because according to OWASP session management recommendations...

The stored information can include the client IP address, User-Agent, e-mail, username, user ID, role, privilege level, access rights, language preferences, account ID, current state, last login, session timeouts, and other internal session details. If the session objects and properties contain sensitive information, such as credit card numbers, it is required to duly encrypt and protect the session management repository.

Now lets see how to use it within our project:

var express = require('express'),
    RedisStore = require('connect-redis')(express);
     
app.use(express.session({
  secret: 'cookie signature secret',
  store: store: new RedisStore({
    secret: 'sessions crypto secret'
  })
}));

Summary

Not everyone is going to require the use of encryption for managing their session data but for those that do my fork of an already great module should be of assistance.

Monday, December 9, 2013

node.js, DH key exchanges & HMAC

What the !@#$ are you talking about?

Security is difficult to understand, cryptology doubly so. I recently read a wonderful blog post regarding 'cryptographic right answers'.

Seeing as this particular blog came from an expert in the field of cryptology implementation (et al; the developer of 'scrypt' hashing algorithm) and security (former BSD security officer), I think it is worth a read.

If your in a rush, the summary goes somewhere along the lines of 'use xyz algorithm due to the following abc strengths' and 'always use an HMAC to prevent tampering'.

Use SSL/TLS! blah blah!

Yes, I agree, however this protocol is not without its faults and weaknesses

The majority of my interest in this stems from researching methods to prevent or isolate and protect HTTP header & content in the event of a cipher downgrade, side channel, brute force, session renegotiation attack stemming from a traditional MITM or BGP routing bug presents itself.

Use oAuth! blah, blah!

This is also a very valid point and I also agree. However, leaving these fields visibly defined makes it easy in the event of a 'replay attack'.

The majority of today's HMAC use within the HTTP/HTTPS protocols get embedded within the browser response or the clients request headers as shown in figure 1 and defined in the IETF oAuth draft. The 'oauth_signature' field is clearly shown. (To test or see more examples of the oAuth header values, please visit hueniverse.com)

Figure 1
GET /?abc=123 HTTP/1.1
Host: abc.com:443
Authorization: OAuth realm="https://abc.com/",
    oauth_consumer_key="xyz",
    oauth_token="456",
    oauth_nonce="213",
    oauth_timestamp="0",
    oauth_signature_method="HMAC-SHA1",
    oauth_version="2.0",
    oauth_signature="Q%2BuWrnEFBv8CEwtNuxn3bIdkjsY%3D"

In the event of a communications channel being compromised the 'oauth_signature' field can be easily recalculated by an attacker based on the modified contents leaving the server/client none-the-wiser that their message has indeed been modified.

While the concept of utilizing a HMAC to verify a message is not a new concept, the question arises 'How can one use an HMAC effectively in an scenario where the TLS/SSL communications channel has been compromised?'

Ok, ok you made you point, now what?!

While I realize that my proposal is not a standard or even typical solution to the above mentioned attack vectors, it should prove beneficial if and when the need presents itself.

Figure 2 is an exportable module to assist in creating a 'hidden' payload which contains a valid HMAC digest & data payload. This of course could be expanded to accommodate for the use of a 'nonce' as well as a 'timestamp' (both can be very beneficial, and recommended, when it comes to providing the utmost security from attacks).

Figure 1
/*
 * node-dhcp-manager
 *
 * Copyright 2013 Jason Gerfen
 * All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

exports.createPayload = function(payload, digest, count) {
  var a = [],
      b = digest.split(''),
      iteration = count || 3,
      ret = []

  do {
    a.push(payload.substring(0, iteration))
  } while((payload = payload.substring(iteration, payload.length)) != "")

  for (var n = 0; n < a.length; n++) {
    if (b[n] && a[n]) {
        ret.push(a[n]+b[n]);
    } else if(b[n] && !a[n]) {
        ret.push(b[n]);
    } else if(!b[n] && a[n]) {
        ret.push(a[n])
    }
  }
  return ret.join('')
}

exports.extractPayload = function(payload, size, count) {
  var num = count + 1 || 4,
      regex1 = new RegExp('.{1,'+num+'}', 'g'),
      data1 = payload.match(regex1),
      regex2 = new RegExp('.{1,'+num+'}', 'g'),
      data2 = payload.match(regex2),
      digest = [],
      data = [],
      i = 0

  for (var n = 0; n < size; n++) {
    digest.push(data1[n].substr(count, data1[n].length))
  }

  data2.forEach(function(item) {
    if (i < size) {
      data.push(item.substr(0, item.length - 1))
    } else {
      data.push(item)
    }
    i++
  })

  return {
    digest: digest.join(''),
    payload: data.join('')
  }
}

What does this have to do with a DH exchange?!

Patience is a virtue! To utilize this particular module please refer to figure 3 which 'simulates' a Diffie-Hellman key exchange between two parties.

Figure 3
/*
 * node-dhcp-manager
 *
 * Copyright 2013 Jason Gerfen
 * All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

var chai = require('chai'),
    expect = chai.expect,
    should = chai.should(),
    crypto = require('../libs/crypto'),
    hs = require('../libs/handshake'),
    bob = crypto.init(),
    alice = crypto.init(),
    bobSecret = crypto.createSharedSecret(alice.pubKey),
    bobPubKey = crypto.encryptData(bobSecret, bob.pubKey),
    bobDigest = crypto.createDigest(bobSecret, bobPubKey),
    bobPayload = hs.createPayload(bobPubKey, bobDigest, 3)
    bobObj = hs.extractPayload(bobPayload, 128, 3),
    aliceSecret = crypto.createSharedSecret(bob.pubKey),
    alicePubKey = crypto.encryptData(aliceSecret, alice.pubKey),
    aliceDigest = crypto.createDigest(aliceSecret, alicePubKey)
    alicePayload = hs.createPayload(alicePubKey, aliceDigest, 3),
    aliceObj = hs.extractPayload(alicePayload, 128, 3)

describe('handshake.js', function(){

  describe('DH exchange', function(){
    it('bob & alice must share secret', function(done){

      expect(bobSecret).to.equal(aliceSecret)

      done()
    })
  })

  describe('create payload', function(){
    it('must return valid payload', function(done){

      expect(bobPayload).to.have.length(4224)
      expect(alicePayload).to.have.length(4224)

      done()
    })
  })

  describe('extract data from payload', function(){
    it('must contain valid payload & digest', function(done){

      bobObj.should.be.a('object')
      expect(bobObj).to.have.property('digest').with.length(128)
      expect(bobObj).to.have.property('payload').with.length(4096)

      aliceObj.should.be.a('object')
      expect(aliceObj).to.have.property('digest').with.length(128)
      expect(aliceObj).to.have.property('payload').with.length(4096)

      done()
    })
  })

  describe('verify digest', function(){
    it('must contain valid digest of payload', function(done){

      expect(bobObj.digest).to.equal(bobDigest)
      expect(aliceObj.digest).to.equal(aliceDigest)

      done()
    })
  })

  describe('verify keys', function(){
    it('must contain matching keys from originals', function(done){

      expect(crypto.decryptData(bobSecret, bobObj.payload)).to.equal(bob.pubKey)
      expect(crypto.decryptData(aliceSecret, aliceObj.payload)).to.equal(alice.pubKey)

      done()
    })
  })
})

Shameless plug

The above code snippets come from a much larger project which can be found @ node-dhcp-manager.

Monday, November 25, 2013

Database encrypted fields & key lifetime policies

Encryption Key Lifetimes

The majority of projects I come across that utilize or implement symmetric encryption don't necessarily address the issue of the key lifetime.

In simplest terms, the longer a key is in use is directly proportionate to the strength of the key.

As Tyle Durden puts it, "On a long enough time line, the survival rate for everyone drops to zero."

Problem With Administration

Maintaining a key's lifetime can be time consuming.

First you have to decrypt each and every field within a database using the current key, generate a new key of adequate length, then re-encrypt the data using the new key. This is, and should be an operation that occurs at a scheduled time and preferably off duty hours.

Consider the following scenario;

A key has been in use for six months, the current threat model dictates each database encryption key should be rotated every three months. Think of a public facing database server within a corporate DMZ.

Depending on the size of the database this quite literally could take hours and possibly days and its key is already past its life cycle by three months which has severely weakened the strength of its usefulness.

I Need A Robot!!

Indeed, I could use one to get me beers but modern science has yet to grant me this one simple wish.

For those with fairly large budgets enterprise solutions exist, however for those of us that exist on shoe string budgets perhaps my project; sqlSec can be of assistance.

I have been developing sqlSec to help with symmetric key rotation using a series of stored procedures in MySQL.

The robot is not really a robot, although for the daring it can be used and fired using an event scheduler to automatically rotate encryption keys.

Features?

Sure, it has quite a few:

  1. Easy installation
  2. Easy configuration
  3. Auto-backup options
  4. Scheduler ready
  5. Testing tools
  6. Migration tools

Download?

You can use Git (recommended) or download the package.

%> git clone https://github.com/jas-/sqlSec.git

Install?

I have made this process as easy as possible. Once you have extracted the zip file or cloned the repo simply run the installer.

%> ./install
sqlSec
Automate encryption key rotation for database
encrypted fields to meet password lifetime
security policies

Creating necessary database creation objects...

Database installation credentials

Enter MySQL username: root
Enter root MySQL password: 

Database settings

Database server name [localhost]: 
1) dhcp
Select database to use: 1

Backup directory [/tmp]: 

Create a backup?  [Y/n] 
Backup created... /tmp/2013-10-23-dhcp.sql

Creating database, users & permissions
Creating key rotaton procedures

Specify encrypted fields for database: dhcp

1) cors                  8) interfaces          15) sqlSec_settings
2) dns_servers           9) myTest              16) subnets
3) dns_zones            10) options             17) traffic
4) dnssec_keys          11) pools               18) viewServers
5) failover             12) routes              19) viewServersDetails
6) groups               13) servers             20) viewTraffic
7) hosts                14) sqlSec_map          21) Quit
Select table to view fields: 7
1) id                4) hardware-address  7) lease
2) hostname          5) subnet            8) notes
3) address           6) group             9) Main
Select field to enable encryption: 4
1) id                4) hardware-address  7) lease
2) hostname          5) subnet            8) notes
3) address           6) group             9) Main
Select field to enable encryption: 6
1) id                4) hardware-address  7) lease
2) hostname          5) subnet            8) notes
3) address           6) group             9) Main
Select field to enable encryption: 9
1) cors                  8) interfaces          15) sqlSec_settings
2) dns_servers           9) myTest              16) subnets
3) dns_zones            10) options             17) traffic
4) dnssec_keys          11) pools               18) viewServers
5) failover             12) routes              19) viewServersDetails
6) groups               13) servers             20) viewTraffic
7) hosts                14) sqlSec_map          21) Quit
Select table to view fields: 12
1) id
2) hostname
3) route
4) address
5) Main
Select field to enable encryption: 4
1) id
2) hostname
3) route
4) address
5) Main
Select field to enable encryption: 5
1) cors                  8) interfaces          15) sqlSec_settings
2) dns_servers           9) myTest              16) subnets
3) dns_zones            10) options             17) traffic
4) dnssec_keys          11) pools               18) viewServers
5) failover             12) routes              19) viewServersDetails
6) groups               13) servers             20) viewTraffic
7) hosts                14) sqlSec_map          21) Quit
Select table to view fields: 16
1) id        3) subnet    5) mask      7) route
2) hostname  4) address   6) dns       8) Main
Select field to enable encryption: 4
1) id        3) subnet    5) mask      7) route
2) hostname  4) address   6) dns       8) Main
Select field to enable encryption: 8
1) cors                  8) interfaces          15) sqlSec_settings
2) dns_servers           9) myTest              16) subnets
3) dns_zones            10) options             17) traffic
4) dnssec_keys          11) pools               18) viewServers
5) failover             12) routes              19) viewServersDetails
6) groups               13) servers             20) viewTraffic
7) hosts                14) sqlSec_map          21) Quit
Select table to view fields: 4
1) id         3) keyname    5) secret
2) hostname   4) algorithm  6) Main
Select field to enable encryption: 5
1) id         3) keyname    5) secret
2) hostname   4) algorithm  6) Main
Select field to enable encryption: 6
1) cors                  8) interfaces          15) sqlSec_settings
2) dns_servers           9) myTest              16) subnets
3) dns_zones            10) options             17) traffic
4) dnssec_keys          11) pools               18) viewServers
5) failover             12) routes              19) viewServersDetails
6) groups               13) servers             20) viewTraffic
7) hosts                14) sqlSec_map          21) Quit
Select table to view fields: 21
Cleaning up...

The process performs the following operations:

  1. Creates a randomly generated user account (privilege separation)
  2. Optionally creates backup of existing database prior to migration
  3. Adds two tables to existing database (version info & table/field mapping table)
  4. Imports stored procedures used to perform key rotation operations
  5. Provides a wizard interface to add table/field mappings which require use of encryption

Testing?

Yes, yes there are. In order to run them make sure you have run the installer, selected a database to modify & then selected table & field combinations which you wish to use as encrypted fields within the database.

If you simply wish to test with bogus data you can use the following example. This example creates 100 bogus records per table/field combination specified during the installation process so the total number of records for 5 fields on 5 tables would be 500.

%> mysql -u [username] -p[password] [db-name] -e 'CALL sqlSec_DBG_FP(100)'

Next we can perform a rotation process. During this process there is quite a few things taking place.

  1. A backup is created if specified to do so
  2. Any table/field combinations are used within primary loop
  3. The current encyption key is loaded
  4. A temporary table is created
  5. A view is created based on temporary table (used as cursor loop due to limitations in MySQL)
  6. The table/field combination values are decrypted with current key and placed in newly created temporary table
  7. A new encryption key is randomly generated
  8. The decrypted data is then encrypted with the new encryption key
  9. The newly encrypted data updates the original record id as to minimize disruption of record sets
  10. Temporary tables & views are removed

Here is a testing procedure to perform the above process X number of times, in this case X=10.

%> mysql -u [username] -p[password] [db-name] -e 'CALL sqlSec_DBG_Test(10)'

Usage?

The easiest method of saving & retrieving data once you implement the sqlSec project would be to create stored procedures to handle access to the decrypted (plain text) of the cipher text fields. Here are a few examples:

Searching records
Here is a simple example of creating a stored procedure which will search a table which contains encrypted fields.

DELIMITER //
CREATE PROCEDURE Search(IN search_param CHAR(128))
BEGIN
 CALL KR_GK(@Secret);
 SELECT `plain_text_field`, AES_DECRYPT(BINARY(UNHEX(cipher_text_field)), SHA1(@Secret)) AS cipher_text_field WHERE `plain_text_field` LIKE search_param OR AES_DECRYPT(BINARY(UNHEX(cipher_text_field)), SHA1(@Secret)) LIKE search_param;
END//
DELIMITER ;

Adding new records
Here is an example procedure that can be used to insert new encrypted data

DELIMITER //
CREATE PROCEDURE Add(IN plain_txt_field CHAR(128), IN cipher_txt_field CHAR(128))
BEGIN
 CALL KR_GK(@Secret);
 INSERT INTO `table_name` (`plain_text_field`, `cipher_text_field`) VALUES (plain_txt_field, HEX(AES_ENCRYPT(cipher_txt_field, SHA1(@Secret))));
END//
DELIMITER ;

I hope that helps some of you with small budgets, this took me a week or so of development but should serve you well.

Wednesday, November 20, 2013

node.js & the <keygen> element

History shmistory

Historically speaking this particular item should not have been introduced within the W3C HTML5 specification as it technically is NOT a new feature (first developed in 1996, subsequent SPKI working group formed in 1997, while RFC-2692 & RFC-2693 released in 1999).

PKI? WTF!? FTW!!

While by no means a turn key solution in regards to automation of user authentication certificate creation & signing for use within a PKI certificate based authentication service, it serves as a simple solution to the poor man.

Teh interwebz!

As trends in web application development have moved from the original CGI (perl's common gateway interface), to PHP, to Ruby & rails and on to node.js for server side development I decided having native support for handling of the <keygen> DOM element natively within my new favorite language was needed.

we <3 node.js

Although not officially within a stable branch at the moment the new 'Certificate' class which introduces functions to work with SPKAC can be used today to help with creating X.509 certificates which can be used for client authentication today.

Patches

To try this out you need only clone the latest node.js master branch; from a command line:

sh> git clone https://github.com/joyent/node.git
sh> cd node/
sh> ./configure
sh> make
sh> su -c "make install"

A demo?!

And now my demo project; simple is as simple does;

sh> git clone https://github.com/jas-/node-spkac.git
sh> cd node-spkac/
sh> node app.js
SPKAC demo server listening on port 3000

Now simply point your browser to http://localhost:3000 to test out the project's functionality.

Runner up!

Or if you don't want to load an unknown project you can always use the test case provided below...

sh> node test-spkac.js
test-spkac.js source
var spkac = {
  '1024': {
    'md4': 'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkYjViMzYxMTktNjY5YS00ZDljLWEyYzctMGZjNGFhMjVlMmE2MA0GCSqGSIb3DQEBAwUAA4GBAF7hu0ifzmjonhAak2FhhBRsKFDzXdKIkrWxVNe8e0bZzMrWOxFM/rqBgeH3/gtOUDRS5Fnzyq425UsTYbjfiKzxGeCYCQJb1KJ2V5Ij/mIJHZr53WYEXHQTNMGR8RPm7IxwVXVSHIgAfXsXZ9IXNbFbcaLRiSTr9/N4U+MXUWL7',
    'md5': 'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkYzBkZjFlYjctMTU0NC00MWVkLWFmN2EtZDRkYjBkNDc5ZjZmMA0GCSqGSIb3DQEBBAUAA4GBALEiapUjaIPs5uEdvCP0gFK2qofo+4GpeK1A43mu28lirYPAvCWsmYvKIZIT9TxvzmQIxAfxobf70aSNlSm6MJJKmvurAK+Bpn6ZUKQZ6A1m927LvctVSYJuUi+WVmr0fGE/OfdQ+BqSm/eQ3jnm3fBPVx1uwLPgjC5g4EvGMh8M',
    'sha1': 'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkZmI5YWI4MTQtNjY3Ny00MmE0LWE2MGMtZjkwNWQxYTY5MjRkMA0GCSqGSIb3DQEBBQUAA4GBADu1U9t3eY9O3WOofp1RHX2rkh0TPs1CeS+sNdWUSDmdV5ifaGdeXpDikEnh4QIUIeZehxwgy2EjiZqMjMJHF++KPTzfAnHuuEtpDmIGzBnodZa1qt322iGZwgREvacv78GxJBJvPP3KLe+EDDsERG1aWLJoSZRusadacdzNmdV4',
    'sha224': 'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkMTM4Y2MxY2ItMzNkMy00Zjk1LWFiMGEtM2I2ZWFkNzhkYjY3MA0GCSqGSIb3DQEBDgUAA4GBAKIjFSzlrhJpq1I3hKt0GL62ASgS86Lte2F0Ksp2tm2Nn8vvnZf78z46SuTW54uJT6c4NKoOgf4Fi8kk5pBR49m1ckZ8zlGvMk1d9VB7JX2oM88qA8YMlBfil8W/hI2SQ80WdffuVJ18nZFp7aqnAMB7DEYBi5Ncadnzew5+4SdO',
    'sha256': 'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkNjRhYmQzZGYtMzgyOS00NzVjLWJjNWEtMWY4YmMwYmM2YmUxMA0GCSqGSIb3DQEBCwUAA4GBAKlPC8NGR3GNZU/vkocxpnjdWoUCqN0zr4POqpuhfYdJyrnwEuhjMD7Ti2QyIXwAirjb6otm9DAMrQURKuges8yp7JeiN94efvHJi9ceUiyP3dT6EcRtgXLF7Ifx67NK9t69UuwlTs+TJJdzR1dRICLILqA0oX2GDyaV42/rF2tF',
    'sha384': 'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkMjIxN2FjMzItMjgxMS00MmVhLTlmMmItNTk4ZTk4ZTBlZGQwMA0GCSqGSIb3DQEBDAUAA4GBAGlUNMeVZphzRAfuH5QaMWUJFKneG+Mq3cEjxDu4uXT14geBXz76zjgfMTFaVFq2B96Pge0L5tAgRlDuBbM7SzsTQWDrR4alz+nsCKu7U3usT38ecOUnczUiYOaV1bw/OFKS47nLokyj6YWAxdvDAcEApQWUGY3+HPW9As693UwD',
    'sha512': 'MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkNGVmZmZiYjgtNmI2ZC00MTllLThhNTUtOTYzYmQzNWRkYWI5MA0GCSqGSIb3DQEBDQUAA4GBALl1q2/ce5L8Ai7CsHC7nrPbSeIfSrPFctLmPoHd1EAMOJL8yFC9fZ7l7mhPqEBh+0SKch8yrxpdzRHVGnPUfhqMdoMRK0zE9s8BoBgerH08T8ScLaFZfjHQYGM2Can8rHl4D2NkPBg3g2B6yIoszV887l3Lrxkx4whK+xTacrAc',
    'rmd160': 'MIIBWzCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkYTBiNDU1MzUtNTE1YS00OTM4LWI2N2UtNTM1MTI3MGJjM2FhMAoGBiskAwMBAgUAA4GBAEAoNEf9RPO4NV+Gmkd5oU5/qnw9AM81lVKDgGCHh5DZRDncX1Pqq5HS9IPy2rLFZUWpKGQiUzk0VYjeiXZkBndOvbocNpAWnnt4z4fpBn4LBBeW0hU32INDXtiSzJAN5yN+XOk5DlJ2R8YVGcp1cC/juVq95pnJZyH0UrsNgiyY'
  },
  '2048': {
    'md4': 'MIICZDCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJGMxYWMzMzFlLTAzMWItNDNhNS1hMWY2LTg1NjQ1ZWVlOThlZTANBgkqhkiG9w0BAQMFAAOCAQEAVFvmwKgxjcmf2ckdDZgP8pJtwi3LZnBy9dSUCVZqAuwuZ8aTX++Sz/2/EyJmM45LOlLVFv/qnmGZEFyd0s+g0kN/ZyZDaDpd4BjKhnUrRiTPhvVfhhyRSAZDKFDjc8ZZDkDNXBXexTBHof3y+A8CKzi1D+wcrSwwfW95g6NAMDhZ5xbsc+od9sfQMM+7sN1D/xxuZ4Chm2FnNNWjErENOz4kNwcs0xU0Q3Gjt+nJr9HFbo7bdFVqKl8Tq1VwlZu1caMuBSKyTaXscYaial5ueWcRasTtHRYuqswthTvUzC5Ap8mrOrHjKE4x385hOrpYY1uGHj2RiVouiy6oui9aMA==',
    'md5': 'MIICZDCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJGIyYTZlZDY5LWMwNjktNDgwNS1hMDYwLTIxMjAyZTBmMTlkNTANBgkqhkiG9w0BAQQFAAOCAQEADeHneffyM9PdSRm/RMSmNazAeSMxZzY+M8TxVrrtersZ2mg5MvBUaZcOV3fYGO/p3y7lI+ygn7qx8m8xTwOs82Ppl800NPfDmRkMqambN/ZxkT92sCG+KeCwuoACvUhEZn32GPRV1iu3AndUNbnsHwMAtXMvcFx0NJiyMLki4XtoD6U/D+xwtRFJoJrJNwPWoKbvrVng2T2c7rUoUsDSe85ZDmmEn9Hzw4dQuQGu0aWCe6rW5SjwhYvzYy+1AYXY+XQKv8uyQkPPJjHdG4ibMEYRcRqAC/TEEGEF0SbsuG3K0+sm9OcZnjs7utRwprjQqRaxmsRisD1oNsHL0TB2xg==',
    'sha1': 'MIICZDCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJGVjNTcyZTc5LWJjNGUtNGI3OS1iNGZjLWE2YjQ3OTc5MzBlZTANBgkqhkiG9w0BAQUFAAOCAQEAiuDJ7z8Qw/M4E++WZcuc0RfxKXgC7AQPadtKnI0YrPfOLHe3kFPjj8qDdWItS6XBNOS88NBeyn/MWUyzefetetmpGmRxnDtxmXKKEaPBJRJj7nc9+mxI6laf3F3J0p1EZxyKOgyWggx0BVIsyZNy6bHMJUE5cPXQtlp4wha9ismcFlMULJ1OBWe5xZToBPcHqW/Umw0oS1hSdU1B6r93QSgdgR6RnoeaKLKpz52riX2Qrdus8WBNHMPc9PU5kVo5gg0NduBoKFGDZVSlZLvoabD/eqirWH7q7CeJYJFPgoyXM+PRCkEvOsM4IuPRF1oNlgc6CjHfr9fUCCaPhQvFYA==',
    'sha224': 'MIICZDCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJDYyNzE1NDA1LTAzMTctNGQ5ZC04ZTY1LTdiYjhhYzk1NWJiYzANBgkqhkiG9w0BAQ4FAAOCAQEArPu8iRsO4yMhaEi8egenqbA8UrwbDujVvBsTlcP8ODLPb3WOyA4T4f0q6VJAGc3PZXYeIB3dg2L2ydsZLGYDIyX4ZszMSGzIU1iHAgaJiEXOZ0VPZ+HbdwLwJNCr9+D37XQ4JPzmzbEOp9BB9LtvNmuMWgsDaOUlwVzsufMP418lCTmEuO6MoiWHdttwavZaC6as3PZNdL9FzjGvGBHZmpSm/pmVhPhh+m1nzbZ7DxqTsjyRYC1PFAm4sybnLBT2gDrAR3Y+LQiQytO3hxHcq21EdLH8sjcouijmq7BWkH3MDzbAIKlbdjw4OsYODFlFJ5tcN+xY8qxd5OSXZuLwJQ==',
    'sha256': 'MIICZDCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJGIzNzUzMTA4LWMyMTgtNDBkNy1hMjY0LTE0NDc3NmM2NDVjZjANBgkqhkiG9w0BAQsFAAOCAQEAEjv//kS6PiYz9SAXvSYBHlswtSigNr1kS8tl6c9mOMPj1MOiXAwDC2u6qbrDCQ82OTZ5H9Q2Od2cRHbsFVmD3mkBvXVAmv1hzdc5NjRtFuzQ8kDhgSvoS9wI7UTfge+4n4zIjIWO/IDwAN4YlKYAJlp7Noj2k0f2vHAO0DS8arSp1q0912M9Jzar2x1wecBGMkgB2VzkXYSYvu1/DSAOWyb6D+U6NroAmTcoOzaipZ4ZuN3SjCkZvfXpO1yPhiElUOQu7VErull6ymcwRCIOc8SMTNXVHoMBiv2GTKfUyDhvRvFtEkMVVKJFvbZ8ROaa/cHWkgy2MgL/5tSgBbdhrQ==',
    'sha384': 'MIICZDCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJDhlNDliOWJiLTc1MTAtNDU5ZS1iOWUwLTBlYTU5MTU1OWExZDANBgkqhkiG9w0BAQwFAAOCAQEAefNKOYPURaemFAO8GUUi1966S2+HP7fEgAPzv/Fny/19hTQHpP5rGWYFGq0kvAAVhCHldEFD6+eduzrDaMVIYLD8OMOX6h688tzP3Hsy1rShdi2VzsK0nyRdZQJuLZMxmDcdXrJVTqrCbT6Ivfmah1LGbltpBbYw/Lb83kYyHEcmEr1Qkt41uHlmB7oHAM60HR2BqPMil+Pl25sx3Is6WWdesK0dxEmzR2ap/77rI4uKINWJLcP+fb0Eo5p4360WqVi1YC2lnlEYGhnyketl/ZH0QC3l31Y6ZgRO+Vn/OeIdBayEIvrJJdsaRLqhMeTzvtTvJEovRDn8AbipOsnoDg==',
    'sha512': 'MIICZDCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJGVhOGRkMjZmLTA0MTQtNGY2Yy05NGIzLTU5NjJhODBlYTc5ZDANBgkqhkiG9w0BAQ0FAAOCAQEAQCn+RvC/Ak78AOnO7YGEpqsmNO36p6cmiaxryyOcRRQbrkKRpjbjP/KkpWUELdlyVjkVnGTMIMMLxAfbvkVR+Pe3FWwfbKTs5oToVxo1NfH+h495ijS5u/PRBIDJtLnC5ggUo2hAQDppyNeyuh23oljT9+U3EDG9dDO1x01dSEUf003IqBxgLU59TOzs5+ouxISRqT7ww8DE57mWRKEQsnegKpgSka6dI7JcHFrDuV2cFN/Zha9Iyk9kE4Ox8saWa8rGZ7dL2GH4Q3PCZoTftruoyF9P7kF8qhZ8m2AfJL+6k8D2W7qRYL4LE/EmAQR9a4rN1Wd/xVlANevMrdqpnw==',
    'rmd160': 'MIICYTCCAUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGUKiE6xqFCsreRygPVnt1ZODwUKaXxgp1mWNrkWNlvdwNK088nxevVndZea3JuPIkxfNJah7muZ9ueoI2iZm6xn9kYH2eQcUeaOcnWb64t9TjMYI+LbW+zeGfyYV6Wgq8m0ExhzQWIbi8flAJAsV8VUbk6fb1a/gdfq8Sx6WYu5ttuN6p3YT+h7gijw5bcmZIzUKfESOpiYSVnARfOjtQ6IJFB0FqK5CKdYP01ZPz8p5Kn35wOkbwusk81CgRWmZhs1WoRJOCm3eE/kR5ou/6ACWB3P55DGopxyVdYsaVyyZ/TPGt6Hn/gzLQ08vyLWge6qBkVXNlWAt+yW42HR9PAgMBAAEWJDlmY2RiNDk3LWM4NzItNGY1Ny1iYmJhLTEzNTVlY2I0M2I0NjAKBgYrJAMDAQIFAAOCAQEAmAJdpd1Wo8D7tkU0EF6hMVAmHl5smeMTX11Gp+fL0hlKtG3VZ8t8Hm5is8GAGfkxWp/cPkLNU8TcET31zT3Bwr4eL/lUXu9EuqMbMJORyx7zCAhNQ5Nb19kn99D5jmJ6vHQs3bLj8AcMf/KEj8SzN/5BXUY9ksKlaMIwDwFW+odVKAOwpUPK88DFaxaZ+mhhej4jxrt3f7uZsV5K7nNvkyhYwjtjsn5MyPe9slFEe3am2mA1eI/hnkj4KLNoK2uDgh3I7c+jKZtI2fyKrKKva2p31sYxx5ybnOPG1iSg8/B7Cy4/0U2cks0lrOdXCF+YRxEUn50xxh+AlCY1iVYb0g=='
  },
  '4096': {
    'md4': 'MIIEZDCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiQyMzE5MWI4Mi1hYjAzLTRiYjYtODBiOC0xYmUxNmE4NWY3MmIwDQYJKoZIhvcNAQEDBQADggIBAIIC/e5CQkMYbXsqsidrCLQ3+PG3nURUquBZFTVC0G7Yr3v7j7iZIA3lqdn9uklTHg+hPr5o0CAF4F9NFBGH9GfXmRk3yVjvoRUw4Kn1opE9jWonjv6Lrsy7QzbWaOJ8NmIcP6QJAASpAqwmb3StndYPAU6nhZvK1ApkTFt3oKm/379/CfIA96tp9VYg7Z4X31u7C3+PT25flpWTvyVEdYIfIbNYss+SJ5gkiGB5laHWRYyiPenAdzHcQU4NCShsJb149uKe894uQsjBlme2VTULs42nMYxWH5d+ECtrKApuWF7v7yMdaypz7wskI019UytIjcFkcleG1zf2KyyUAgt4feT4zVrERcPcnu/rHaIVZ2FVwnVag8L8Dj049d+pW1xiA4zAAZ1n7Yh2p+nrd9pqsdn53A1etS3Bs0YkIHTdjc8NuD2RVFuqvzV8nyQbrsxm6jDsenMzG4BAK33AugnjzdEB1CY0hYK4miV4YFIgbkcKX26HKckHi6BIZYk61pyrDwrt62Cwbe89m68ape4VuOoTtuy///6d414DdyluPjtRHoQ75Pp3Ilpv54WXh4TesLRDnDmZcQ3at4DuHZvZMtFW5+2v2Tp6rmUM8TuT2Jb8AwF1t38Z7UU8HfDYXWhlT5rB9UtrMXyDVIBsDJW6/gai4yxWE+SPpLhFaPeZ',
    'md5': 'MIIEZDCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiQ5ZWI1MjFmMi0wYTFmLTRmNTgtOGE1ZS1jYTMxOGI4Zjk0ZTIwDQYJKoZIhvcNAQEEBQADggIBAJJJux48/uB2f9ef4lKtcz0Nn9gVfWfo5KZmThVu9MEpPGI/awHrpWA7Det1H4v1oducIUHqBKdufU9OHeYxQ8RZMan5+Da6uaBjn1dYcpQdCHstjtiJgSIxxGU7Gv4icdrJ0M4vBQ96x5dWZk24cCeL4CEqpK/UDd60cO2yk2EpFvlmws5jkJByM2EIqg19Zp5oOvixGybfxG06KMwwrZ+r3t1UgBgsiUX6P6ZLgTWqcv/TsAK4m8XO6AcxnuyU62a6f0xza+C/gvomiT/8/ZSVQh7vdDxl2QeGW75qVN7Gh9zjnnSLt8YcyeHz8YFNlrjMLGd6M2soafnUDE/PUszxqp3DrU43cbLxqOMRJBiotyR//4d0y8N3LpisI2VbN2HEX3IkdRzqVgGasOBeyBi9RUrUp7jP/84j/aBseGmaDpOZbcysLuS00s+N+JluuK4vBtMOMm6AdBZg5AWQoD+EvJsG/JHoEtx1+Tm5DsaBIm1E1IsMgRrgqvyBzIVr/Nwt+UoLNhMzKGR9PKb0gjBMld7fsHprkiurhPr5klM/+QVR1Mo/fhb1EeMwZz4se1xQ/o+hLp7I0/x7jWiYk06XlbvQj5kl/+MZA4ESBhgo1alQ2u+ecf6oEh7l2tCP08vRSIs2JcOMY5z0a16yWOrOaPION+UioRtgrMkfKyA8',
    'sha1': 'MIIEZDCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiRiYTBkNjhmMS04N2NlLTQ5OTUtODE3OS03MzczM2MyZjJlNjEwDQYJKoZIhvcNAQEFBQADggIBAL9pHr92a0OIzQRrsovVSX99gvOhkNTgDigIaxVCLR304HUyYLooRxiycYeu2yE+j66BbuC9B0W8SrkrWWAyh3/7t8Y8pYSJzItLXTzWzwWWClI15rmSl71iHJQ4hc1mY+418J39FQKfzCFAyrYwAZJ6eWSes8CYcxK9AlEbEJ4LJ+S7yJV2vIHgivXiE/pnkTkjbFg3yU2mD3cndXAHyCJwD3qKyF9ptaneC9urX3St9ZY67p8XpkJPTqGoE9LXXO75VxFIf6nAVPYwqTkEZUn8djPClMhiSjvDPirP0O/lcVWmhU+JmRzw7aQpDYew8pUyux36PCdmDCeSThgbKKd9+w1yQ/lmggXTUkxMaqCzs6K8st1V9cGykSHZEqq4yenMzqjAFlgHT01IzS+n6o0+buj9B7+3WjSgq+6EVy31fWDW3YQbw64vDkUnvxhcVDtI8SbiqcOn4SKIK0OZbE4Urx7r91gIca/cx9Bik3Wu2M6k6E796OPQvuMKqFwgXk9FJFsQ9ffMdoJpza3UwoDPL9znf2M26MhXzLKHtddb78eoTY6HP72f6tl+rGCe4dCj6Y46Q1HyF4GdCWx/ljuobJau1/rzzAcOF9ZaCJZR/6zkkUuYCh1I9OSJn58EGDMAb3duNlnZ+DNZtd0J3qPXexCAxE70Q5j+qWDAWMTY',
    'sha224': 'MIIEZDCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiRhZDg2ZjkzOS1iZDlhLTQzMGItOWQ2MS04MGQ2NTI0NzMwMDcwDQYJKoZIhvcNAQEOBQADggIBACEO5mq2bUiJV27Sw/b2lCbfqa9SHbU0nprk7cJed1JBFs1cS5cBGHtSU/bWF2xIiyU+jkVFf4jk+KGRiqu4+kdpLTbdy1eVThIsDSit7isscS4rtIRkoVrnBAmn1aJ9d1i1xTZVT4zjPydlKNNXVysqnPxgjDaxMRKk8WPafijK9PVJjX4lpXUIGS0gB3mL94Po7cvoLvlSVSM0rZxgHs4RCr/FRRQGBiCcPfKcyYWrcSL232ejP8SSaaGvOMkCy4acaBxKYOlwtxlp7bIXI5N2t5KnpgAewe3T35Piwy7BR+UkwgLMrAQRpupjXXB7q9gP+QSKZgcqIMBLov/Shg+NNlCqZnMPjhNO4ArWfHJbCQL7iQhpcBDXSmyJ3gkn2OfCLpaUqMGz5MfsZAoZQ1y0gZ1sFgR/WoycZeBW6kGdIYSur932dRwPYwOkt9WFu3GNiqqu5Rh9LamYMl5Ezc24ItzXfvKE+rvxSV2CfjeHGSvORhtgbJv39auL+3zVh7reKuhT74qRv797s2170XyZPZeJui6TCBxXafP7IxvygE9DprHu5TRnjtn+d/CnGJYZdviDNchZo/G0aLgbkhUQ56XajmgiIRD/yzVlb9246PsPhfpYkqAC3R1RiNgMaDsKCkOVlJ5DtEB+OLXbn1zZ1ksT0aHFIwpDDF9ADmbu',
    'sha256': 'MIIEZDCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiQ0ZGYwZGViNC03NWY5LTRiY2QtOTNiYi00Yjg3NWE0ZTI4ZGMwDQYJKoZIhvcNAQELBQADggIBAI89JU/5UPTvTIRc1DZJIOX/NJwQj3STqKfThN947vUgauj+0bB3rqBRo8AMyIRy4vdRm66qmA+3PLEDLtFFzTCxXP1mjYQ5KPM8IX+Chz68rkaE0ia58/tg55TyuMENJmYAjG+1IxeLTzesqRwI5P8ut/rZpu9TY795U5J4LHSUFuZWQn+VbN+hKyt3liIZSDEgEE4dTWSj2n8m4cqpRZaSbZw9uYKcylMfaIAr+Nl8i4l3WMyG1sIaCY/wz7yP0rPxg2Yazh5ksfRTuKo8K37cNfWG7Vsnfl5YI6w/9Dz49dGH0IeXl78mLbwdPVD/F0pJA37/iXW35hcj4QSp6wd6XdUJEN1GOk4qR8RTGm3W2Cw5o4g37efGa/CMeLWXBdYPgOeilh5BSL+9BtnQdqOxYvQFSuQiKoZf+A5udlaOb+5B6MnvE05oblvvYtsvqq+4/UCzztjsPhJKeTMzVX55wq4u/uqt0tJ8FYQp9MKpP72k5IdMrAlM8HdClZl6OC96PwLaehKLiRdBxhWZC7JZQ9jIaV55EHqAfxBqahrzw7fGEariWhAVkWXgEml/SlGqSCtQUqY6qge/SlvIIls5GR0ZxWiKNQtn2gSE4B+jspKyAHVFRu73bxpW7N/fI8PrPotlWfEjxdAg5Mo1bphyQH1/3jA8xzh2wvS8gEuE',
    'sha384': 'MIIEZDCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiQxMzEzZjI2Ny05NmE0LTQ5YzEtODQ4NS1iYjg4OGY5YTM2MmIwDQYJKoZIhvcNAQEMBQADggIBACWK6+9JShRHqJNRaMrQZxLMXXEiyXTAZAbTUvWWHupF6iGjC0St4BX253u2kuAr02v95Z4K2jerREjBsGbhR0pFjGpriExE5FnSooOJigvT+D6gv1Khaz56VWzcgYZDQQN2XwKCrHr/YdmDr/Sd9cdgRJ9fnJVNu9LRpE9W6BBEUJn8P3ThHf1tJHCxZqo5H7qoIB+gNZwWL5yoJ1sl4RXcdjyJ6Neza3pQ6tV6tqWn4yh65VqF+yVLMq7ycCX8vpoU16QalBkbqVUX0ap7Z4cx7U3bXCYr+lCji0VhrTSuyc4G1EL3jxf4TMjfsVJpsKR+hOhzrZNKPL1k+U59Whtc0jWFPPXY7dJEDng8+7veYu2pczBzaEvyYVHOuQl49SlaM9vFoplyADdET9Fgv0/nUm5uBNyrROlXNd7XOOzYp8Axepbq54Fs9t2EHJCcHENjHzzMgaAfjFI0ar0yBRtvhgyV7/Vw18xkTjvHZILSWA/FkKhtGD1qND6Nc5wJ/0f+B35cCTcfmO8E/vXMkfAgacgS/Q0ljkejJ/NnYm5rR5W+gAqXciE42ZWRbXH2mlAhFZu/+eGg7Rtozlgxx5qWRBCfSNTd0Srn/u2+yPAL89XKAGPjgyaXNsqR82eTiLGRNt9U0S6ob1NkfwqQ4ioO6RgDKIrz2XJ94rHdlFeQ',
    'sha512': 'MIIEZDCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiQ3ODdmYjc2ZC01NWViLTQzOWYtYjg0Ni1hZDY1ZDFjYmY5YjkwDQYJKoZIhvcNAQENBQADggIBAKTkLUiwz+VeX2OkD/YzleHVDrh3BzCXwUBtVUueDgP5/BPfAF7/p7uMZ2kM4Eg48cI9XrymIcp3I4mVa488p5M0iA+0Nx67YAhCvzed57G0vVRlb6ZM5hIduy8WCcGaxRUm7Wv8tYS2pcsPm2zTh//uWbofTcvx0otQQc6XSdhD4+4qM+Ad4neTzqFngDYsZavDPWFsYN6oODh17i3ByAnA8oVTfjSj6eYs32tAEHh5Gag+H1ebFh4QuxORUno0PpJ4sfY5l10bH6vu/j/RlXSYgHHNp2E2cjJl81Copy8J8zyYBr+kpwQRqbxiabrFtqZYFiVj87BEVSoFQMjZACPs8Ff8sGAHbuWDHzjKsZd3zsbpb2Tz5cGmvyfqc2b4xhnUHQ5T2w64kB4ZR9L03LmE5EPxSaFMayZZC/B7IJanbcaLEl5j5ce1Dn6ToeYCV65JP7ZJc7+/dNT5tU+znt2/m04vk9Vq14dhJ5d4nrXU5IkxG1dSWiyDd0Q+LW77pbf5+v4wdlBRWgoOkPGSAIztrtv0iLjBEA8mesefz7Wlkka0g865eetuIlDpjYwOPmlu+7xnstKlC5gB7V3e2gGPDX8UrGsNf0/2G+O/0K0+kPxx9WDUuWtUGRNYd+Ny1ljY4RPHBiKmPKiCJyfoUWw4j2TDbxcmCZhxEfkmXmKC',
    'rmd160': 'MIIEYTCCAkwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDIY5P5YLslXFx62YGZRHfqhVEGTT4xvvJbcjT+SY0tfVl6vWI9bB1jWxMfRkuKewCLl5AeXIYmOXOdIFHTYyHFpKaBxRrDC87GLWvPkvJtspWySM5S55FpDRugKIFGTTLc3IUIZfEC5mG/F2mxzZ7cThgI99vVBlwKngvPEO4ihhNwaEfcVf3+eiQ+pfh5Uqx1n27Xj/Da5yBIyvKAqR8MHtfuhflTjqgzk7wGMi0nD/FvGsiVLEu9b3/3pnjWYOTkOsTQibPXWgAZyFRVSblt+KUW59R39Hf284rnbL0FPkhDn8Z+u2B9uYbiWoL4cuPpk3isfK/tuR5a5rME/lVwoStlncwhxJoMwiBWM/ItZBoWrm5e1ZbQRx80r7QBhl90v/oGUhM4RNuUCXGbBtJQ7Jy6edl+wnC6HQvj5KzdpK0pCMGRsIgko5G7T1rVkxpQxIjSBkps4Ijjf5TiGsTwJRQKimJWnbUHY0x+bpLaczLgHmjAIXO/5xerlNPm2CzTfyAoWcNAgohc7dn7nFBwwqy2WWRa6empXleGG6e48skenKPU99IIigGLF/xRtyplDdoPBn6xhvqLE7K8e1NiHQK+Bgby2vEVVM4PNQo9xGpfTdhN91DkvbOivOTY/G16W5Lb6EJwFuvbYeX7+hMDR5nD47DGAnvkNWV1n4zrDQIDAQABFiQ2NWU0YTRmYi1iMzAzLTQwMTMtOGFkYS0xN2NlOWE3MWUzMmUwCgYGKyQDAwECBQADggIBACrIQrhwBin9Pf6wMkc9m+y8o2/7f/4hBl2wQktcmCAyE4eBc1tXmKoYtkwsRhtm/J1lOSZv6KU+c3nDSpOZeL9oc1EAKC6eQ3YUmxlQ/BjOQRuPkC88WZcg2d7o6Xat8skrBWAEQ5LMpaVJirscsuYNloD31PmYasr3995iCX14v1jUEleWeOOiDnpB+KH7iig7HBPYFJ4QDXJVuFxq4N52cyRd4moDaMiq+nj/0UO+WPNHYObh97u2c3Scrf+Q/nGw4opyi7Oxr7e0Zy8q0Isw5ka9NwVxiSbKfi3m21g8X4zBn5+x6DuPj7+BnqqXsm8Q72FiGFneFx4LagbbVMSgBspvEOopCIxdicyU+nHR35QCWGlQ7eFk2Fmn+oOqBTwSPXKf9ehQIhuCNYgxbaHTGbFBWrdm/ap5jq66IroiOMZaZJ1Jo2kfSc+LEPuVeB1Am7c6KD6Dw8dWeDCbygwa8EbFR63S47tYlUWB+M1FgCOr62eh/iMgcyJCHvRjuKhz7ef+KH00jiBwCsBRQidjkmGrzB/sxeO3/s3O69NSsBMe3+e97eL1LgEUufPQzU6lCT/+AJbKKnkgQQJMTRCywUgiqxu4AG5ZcsgDhCmVUBgH8t7fHYfdLSTaJI3U7uN3H2QxHhBqOwJSr0vccyrMjNSyyp/VVXApOzGMsrOG'
  }
};

var crypto = require('crypto');
var certificate = new crypto.Certificate();

crypto.DEFAULT_ENCODING = 'buffer';

for (var key in spkac) {
  console.log('Testing SPKAC data created with '+key+' key size');
  for (var k in spkac[key]) {
    console.log('Performing tests on SPKAC which used '+k+' hashing algorithm');
    var buf = new Buffer(spkac[key][k]);
    console.log('\tValid: '+certificate.verifySpkac(buf));
    console.log('\tChallenge: '+certificate.exportChallenge(buf).toString('utf-8'));
    console.log('\tPublic key: \n'+certificate.exportPublicKey(buf).toString('utf-8'));
  }
}