Decode Drivers Licence Barcode

There are many companies out there. I admire your resolve, but honestly, it is highly unlikely you will ever figure out how to render the image.

If you're interested in using a reliable off the shelf solution, drop me a mail on [email protected]
 
First I used the following code

private byte[] HexToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}

To take the hex to a byte array, the byte array should then be a length of 720 bytes.

Then I used the code sweettoe provided (skip 6 then take 128 bytes).

So in the end my code looks like the following

Code:
using System;
using System.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Math;

namespace Drivers
{
    class MainClass
    {
        private static byte[] GetPaddedValue (BigInteger value, int length)
        {
            byte[] result = value.ToByteArrayUnsigned();
            byte[] padded = new byte[length];
            Buffer.BlockCopy (result, 0, padded, (length - result.Length), result.Length);
            return padded;
        }

        private byte[] HexToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }

        private static byte[] EncryptValue (byte[] rgb, BigInteger e, BigInteger n, int size) 
        {
            BigInteger input = new BigInteger (rgb);
            BigInteger output = input.ModPow (e, n);
            byte[] result = GetPaddedValue (output, size);
            return result;
        }

        public static void Main (string[] args)
        {
            string longKey = @"-----BEGIN RSA PUBLIC KEY-----
MIGWAoGBAMqfGO9sPz+kxaRh/qVKsZQGul7NdG1gonSS3KPXTjtcHTFfexA4MkGA
mwKeu9XeTRFgMMxX99WmyaFvNzuxSlCFI/foCkx0TZCFZjpKFHLXryxWrkG1Bl9+
+gKTvTJ4rWk1RvnxYhm3n/Rxo2NoJM/822Oo7YBZ5rmk8NuJU4HLAhAYcJLaZFTO
sYU+aRX4RmoF
-----END RSA PUBLIC KEY-----";

            string input = "019b0945000060e9a27a1e475f89af17cb3a5ae86bd91152d22fbca1f462ba8bd39e9341bb26ad82e3ab1b4e68069247b75286edb64fed476c1a444bba7ffeca8ac9ddcf629ac582a865b8eefec682f33de40e68a6fc08265df86ad058376db56e4a9d2a2da285d11f408493fb4706abc346632a156b517c87b58372a3afda8be11dbae4a8f5a3d1881559ce0c69acd026770fd9e75c28c4abd0b2796e70eca894d8546bd8456bdcd9af0aa112f78b56523471b46805ae844a66108ba2f12a28180e5ddded48436239eae212c6193d351201e6a24613d2482ddae552bf7e21729246667a9cd5c730ba4b80736586f5f90894b1017fcea7b299cd6241a266c4b7b28b704de1f0266025358d134686e1cac2ba9af74ea16b9524f4fca20792d2618beec8499dbab596f5a56531f26462c996dad5e1278400a5266c5b5aed2dd3cae135821148bdfd3d564ac127c90d3a125031cb263e63879dfdd10725dc80225a700e5d259a5a1d2340d7bd2d3fecc9b078e1726661e2c51b945b56d762fa3067f2b926dd2707a74e1535abf1a7ee09b0ec40689cd3b3b9995dfdda9b5f33482f1441afa48304a68b24b942464d0334d4b20c17a6f09d5db119257212f7b2d48860e22c64924dc53c03557624a020b175ef47553946ce2e60c3f2ba95865d73b2d440c2fd3eb3c9850447c249c869444dbc103d8197c5556355e6c46455b61aa0b176bcc73290b532726d4339bb35b144fecf5c5d4c5e90aef23fc9c9375ae7206f9ea5a22255365f7e0728e3360cb78562c805a4f9a2b4a674eb5af30b598b98b16227a223505e2e013e426296e8f3d1ff03d6506fa431162438afabbc4e8b9626e8821d6bd264ff9d35a2df90e2be27e0297c162a0a6bbc937f349d31413484b52ca27255b2263abde2793cb946387685de9662777cdf6808a18c3a25759ed4e6ab6410f7881b655421be87e3f3df100062f12ae806059ca3e11248557080a64e4ec6e0a46697cae4c5c5197e3defdc"
            
            using (var reader = new StringReader(longKey))
            {
                var pem = new PemReader(reader);
                var o = (RsaKeyParameters)pem.ReadObject();

                var br = new BinaryReader (HexToByteArray(input));

                br.ReadBytes (6);
                byte[] data = br.ReadBytes (128);

                var decrypted = EncryptValue (data, o.Exponent, o.Modulus, 128);
                string base64 = Convert.ToBase64String (decrypted);
                string ascii = Encoding.ASCII.GetString(decrypted);
                Console.WriteLine ("As base64: " + base64);
                Console.WriteLine ("As ASCII: " + ascii );
            } 


        }
    }
}


At this point I am stuck at decoding the decrypted content

Have a look at the following link

https://github.com/ugommirikwe/sa-license-decoder

Hope it helps. I have done all the decryption, going to do the extraction now using the above as a sample.
 
Has anybody got this to work?

I used the C# code provided in this thread to get the base64 string and when I use the github sa-license-decoder all the fields are empty.

Here is my code:

private static byte[] GetPaddedValue(BigInteger value, int length)
{
byte[] result = value.ToByteArrayUnsigned();
byte[] padded = new byte[length];
Buffer.BlockCopy(result, 0, padded, (length - result.Length), result.Length);
return padded;
}


private static byte[] HexToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}


private static byte[] EncryptValue(byte[] rgb, BigInteger e, BigInteger n, int size)
{
BigInteger input = new BigInteger(rgb);
BigInteger output = input.ModPow(e, n);
byte[] result = GetPaddedValue(output, size);
return result;
}


public static void Main(string[] args)
{
string longKey = @"-----BEGIN RSA PUBLIC KEY-----
MIGWAoGBAMqfGO9sPz+kxaRh/qVKsZQGul7NdG1gonSS3KPXTjtcHTFfexA4MkGA
mwKeu9XeTRFgMMxX99WmyaFvNzuxSlCFI/foCkx0TZCFZjpKFHLXryxWrkG1Bl9+
+gKTvTJ4rWk1RvnxYhm3n/Rxo2NoJM/822Oo7YBZ5rmk8NuJU4HLAhAYcJLaZFTO
sYU+aRX4RmoF
-----END RSA PUBLIC KEY-----";


string input = "019b09450000..."; //Took the rest out due to security reasons


using (var reader = new StringReader(longKey))
{
var pem = new PemReader(reader);
var o = (RsaKeyParameters)pem.ReadObject();


var bytes = HexToByteArray(input);

byte[] newArray = new byte[128];
Array.Copy(bytes, 6, newArray, 0, 128); // Remove first 6 bytes, take next 128 bytes


var decrypted = EncryptValue(newArray, o.Exponent, o.Modulus, 128);
string base64 = Convert.ToBase64String(decrypted);
string ascii = Encoding.ASCII.GetString(decrypted);
Console.WriteLine("As base64: " + base64);
Console.WriteLine("As ASCII: " + ascii);
}
}


PS: This line var br = new BinaryReader(HexToByteArray(input)); gives an error Cannot convert byte[] to stream. So I made a slight change to remove the first 6 bytes and then take only the next 128 bytes.
 
Last edited:
Has anybody got this to work?

I used the C# code provided in this thread to get the base64 string and when I use the github sa-license-decoder all the fields are empty.

Here are my code:

private static byte[] GetPaddedValue(BigInteger value, int length)
{
byte[] result = value.ToByteArrayUnsigned();
byte[] padded = new byte[length];
Buffer.BlockCopy(result, 0, padded, (length - result.Length), result.Length);
return padded;
}

private static byte[] HexToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}

private static byte[] EncryptValue(byte[] rgb, BigInteger e, BigInteger n, int size)
{
BigInteger input = new BigInteger(rgb);
BigInteger output = input.ModPow(e, n);
byte[] result = GetPaddedValue(output, size);
return result;
}

public static void Main(string[] args)
{
string longKey = @"-----BEGIN RSA PUBLIC KEY-----
MIGWAoGBAMqfGO9sPz+kxaRh/qVKsZQGul7NdG1gonSS3KPXTjtcHTFfexA4MkGA
mwKeu9XeTRFgMMxX99WmyaFvNzuxSlCFI/foCkx0TZCFZjpKFHLXryxWrkG1Bl9+
+gKTvTJ4rWk1RvnxYhm3n/Rxo2NoJM/822Oo7YBZ5rmk8NuJU4HLAhAYcJLaZFTO
sYU+aRX4RmoF
-----END RSA PUBLIC KEY-----";

string input = "019b09450000..."; //Took the rest out due to security reasons


using (var reader = new StringReader(longKey))
{
var pem = new PemReader(reader);
var o = (RsaKeyParameters)pem.ReadObject();

var bytes = HexToByteArray(input);

byte[] newArray = new byte[128];
Array.Copy(bytes, 6, newArray, 0, 128); // Remove first 6 bytes, take next 128 bytes

var decrypted = EncryptValue(newArray, o.Exponent, o.Modulus, 128);
string base64 = Convert.ToBase64String(decrypted);
string ascii = Encoding.ASCII.GetString(decrypted);
Console.WriteLine("As base64: " + base64);
Console.WriteLine("As ASCII: " + ascii);
}
}


PS: This line var br = new BinaryReader(HexToByteArray(input)); gives an error Cannot convert byte[] to stream. So I made a slight change to remove the first 6 bytes and then take only the next 128 bytes.

That javascript library doesn't work. I'm sure we've spoken before, but anyways, pm me for a commercial solution
 
I have a requirement to capture the data from the drivers barcode.
My requirement is to simply read the information from the scanned barcode for a security checkpoint "clock-in" / "clock-out" system.
I have been asked to do this in a web browser client side.

My problem is that when scanning the barcode into a textarea on a webpage, everything goes completely "wonky" due to the browser thinking the there have been some CNTL+key or SHIFT+key combinations pressed.

The scanner works very well in that is can read almost any barcode you throw at it. If I have notepad open, I can scan almost anything, but as soon as I go browser based, the issues explained above arise.

Does anybody perhaps have a javascript/jquery function that will allow me to simply filter out the "nonsense" and allow me to capture the remaining information.
 
Has anyone perhaps managed to implement the above c# code in Java that can be used in Android development? I have done a shortcut wrapper for a PDF barcode scanner that can be used in B4X (compiled the Java code into a jar file). It scans the PDF on my drivers license and I can extract the hex string. But have had no luck to convert the above c# sample code for decryption to Java.
 
Off topic to the coding thread there, but I've noticed an ugly trend where gated communities on a public road was starting to enforce license scanning and storage of this information, preventing access (to public roads) if the information is not provided.

This is probably illegal and even worse, some security companies are storing this information in contradiction of the POPI Act. (private or public). Capturing people's PCI in the name of security is not a valid legal reason under the POPI Act, you have to store that information for a legal reason. (FICA as an example, or RICA)

More info here is needed, JPSA made a nice summary of the issue.
http://blog.jp-sa.org/post/do-you-have-to-present-your-driving-licence-to-a-security-guard
 
I want to jump into the fray here to see if we can get the barcode decrypted. There must be a proper way/algorithm that we can implement in java/c#/javascript or whatever. If stupid scanning devices can read all the barcode versions, I'm sure we can figure it out.

Did anyone come right with the C# version? Decoding the image?
 
Hi Ben,

Can you please assist me with code for decoding the Drivers License barcode. We currently using C# coding standards.
Do you have the Library available?

Thanks
[email protected]
 
A scan app I installed from the Microsoft Store reads the text info on the SA identity card. But the driver's license scan produces gibberish, as expected in light of above discussion.
 
This page is very interesting.

https://github.com/ugommirikwe/sa-license-decoder/blob/master/SPEC.md

I am some way down the road with this in Java. Got the RSA decryption sorted out And can see some of the info after Base64 conversion. Can also see the info in the nibbels after looking at the page in the above link.

Still some work to do and then of course have to search for the image in one / more of the blocks.
 
Hi,

Does anyone know off an app that can scan both License Disk as well as Drivers License. Its only used for verification purposes - so I am not worried to decrypt. I only need to make a comparison between two scans.

In simple terms - Scan Drivers License (first time) save whatever data it produces (can be gibberish), then scan it the second time and compare the two scans. If the gibberish is the same as first scan it passes, otherwise fails the check.

Thx

W
 
Hi,

Does anyone know off an app that can scan both License Disk as well as Drivers License. Its only used for verification purposes - so I am not worried to decrypt. I only need to make a comparison between two scans.

In simple terms - Scan Drivers License (first time) save whatever data it produces (can be gibberish), then scan it the second time and compare the two scans. If the gibberish is the same as first scan it passes, otherwise fails the check.

Thx

W
I can do it for Android
 
got it working. I have a fully functional Library for the .net framework that returns all data including the photo
PM me if you are interested

Hi Ben

I would love to see how this works in c#.
All i need is License number, Id and Names.

Thanks
Thomas
 
got it working. I have a fully functional Library for the .net framework that returns all data including the photo
PM me if you are interested
Ben - I am interested in your .net library to decode the drivers license. It seems that all the info is on this post on how to do it, but if you have done it already I would appreciate if I could get it from you.

Thanks
 
HI Ben,

I have been using the EXID library for decoding drivers licenses. The drawback is the limitation on the devices available to the SDK as well as the additional costs involved with this. Any chance you will be able to afford me access to your .Net libraries?
 
All driver's license with ID No similar to 02/7005xxxxxxxxx are decrypted pretty easily with the keys provided here.

However, I found some licenses with ID No of type: 01/100xxxxxxxxxx which do not get decrypted.
After scanning the PDF417 barcode, both of these types of licenses returned different data.
Below are the first few bytes returned by both:
Type 1 eg: 02/7005xxxxxxxxx ---> 01 9b 09 45 00 00 03 9F 6B​
Type 2 eg: 01/1005xxxxxxxxx ---> 01 9b 09 45 00 00 9E 6E 22.​

The code used for the decryption is below (Java):

Java:
byte[] key128block = Base64.decode(longKey.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----", "").replaceAll("\n", "").getBytes(), Base64.DEFAULT);

BigInteger[] keyBytes = parseASN1RsaPublicKey(key128block);



RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(keyBytes[0],keyBytes[1]);



byte[] decrypted = EncryptValue(block1, rsaPublicKeySpec.getPublicExponent(), rsaPublicKeySpec.getModulus(), 128);

Are there other keys that can decrypt Type 2 of drivers' license?
 
Thank you pPat for sharing this. There are a few companies/people who are suppressing the industry by keeping this a secret and they are contributing to the demise of the SA economy. They limit competition and charge exorbitant prices.
 
Top
Sign up to the MyBroadband newsletter
X