com.smedia.sqzserver.common.util.SerialNumber.java Source code

Java tutorial

Introduction

Here is the source code for com.smedia.sqzserver.common.util.SerialNumber.java

Source

package com.smedia.sqzserver.common.util;

//import gnu.crypto.cipher.Twofish;
//import gnu.crypto.hash.HashFactory;
//import gnu.crypto.hash.IMessageDigest;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.CRC32;

import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.TwofishEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;

public class SerialNumber {

    public static void main(String[] args) {
        try {
            SerialNumber serialNum = new SerialNumber();

            // Server 1.7 Auth sorenson Retail Install Ref 1
            boolean decoded = serialNum.decode("xFLXP9-DDK92T-9PSFTF-Z57VU2-TSPGP7", "sorenson");
            if (decoded && 1 == serialNum.getVersionMajor() && 7 == serialNum.getVersionMinor()
                    && 1 == serialNum.getReferenceNumber() && !serialNum.getEvalUse()) {
                System.out.println("test passed");
            }

            // Server 1.7 Auth Sorenson Evaluation DateLimit Install 17 Days 5
            // potency
            decoded = serialNum.decode("AEBSCE-PUMVTE-UFH2LK-A4Z2LL-3NZDP5", "sorenson");
            if (decoded & 1 == serialNum.getVersionMajor() & 7 == serialNum.getVersionMinor()
                    & 1 == serialNum.getReferenceNumber() & serialNum.getEvalUse() & 17 == serialNum.getEvalDays()
                    & DateType.ExpireFromInstall == serialNum.getDateType()) {
                System.out.println("test passed");
            }

            // Server 1.7 Auth Sorenson Evaluation DateLimit TOday 17 Days 5
            // potency
            decoded = serialNum.decode("M7QFCG-XEWXQA-AQUVV4-VTGRJ4-A6NMYG", "sorenson");
            assert (decoded);
            assert (1 == serialNum.getVersionMajor());
            assert (7 == serialNum.getVersionMinor());
            assert (1 == serialNum.getReferenceNumber());
            assert (serialNum.getEvalUse());
            assert (17 == serialNum.getEvalDays());
            assert (DateType.ExpireFromToday == serialNum.getDateType());

            // Server 2.8 Auth Sorenson Retail Ref 7 even flags
            decoded = serialNum.decode("5H4CR8-MGMBBV-QUKHNS-H9ZRKX-5ELSMR", "sorenson");
            assert (decoded);
            int value = serialNum.getVersionMajor();
            value = serialNum.getVersionMinor();
            value = serialNum.getReferenceNumber();
            value += 0; // remove read warning
            boolean flag = serialNum.getEvalUse();

            // Server 1.7 Auth Sorenson Evaluation DateLimit TOday 17 Days 5
            // potency flag 0
            decoded = serialNum.decode("99UXXF-9Y5TZZ-QZ8UDE-6Y5B35-SND7EC", "sorenson");
            flag = serialNum.getFlag0();
            flag = serialNum.getFlag1();
            flag = serialNum.getFlag2();
            flag = serialNum.getFlag3();
            flag = serialNum.getFlag4();
            flag = serialNum.getFlag5();
            flag = serialNum.getFlag6();
            flag = serialNum.getFlag7();
            flag = serialNum.getFlag8();
            flag = serialNum.getFlag9();
            flag = serialNum.getFlag10();
            flag = serialNum.getFlag11();
            flag = serialNum.getFlag12();
            flag = serialNum.getFlag13();
            flag = serialNum.getFlag14();
            flag = serialNum.getFlag15();

            // Server 1.7 Auth Sorenson Evaluation DateLimit TOday 17 Days 5
            // potency all 16 flags
            decoded = serialNum.decode("J6GMVW-3GL2ZX-JACTJN-MUF5KR-W2BSKL", "sorenson");
            flag = serialNum.getFlag0();
            flag = serialNum.getFlag1();
            flag = serialNum.getFlag2();
            flag = serialNum.getFlag3();
            flag = serialNum.getFlag4();
            flag = serialNum.getFlag5();
            flag = serialNum.getFlag6();
            flag = serialNum.getFlag7();
            flag = serialNum.getFlag8();
            flag = serialNum.getFlag9();
            flag = serialNum.getFlag10();
            flag = serialNum.getFlag11();
            flag = serialNum.getFlag12();
            flag = serialNum.getFlag13();
            flag = serialNum.getFlag14();
            flag = serialNum.getFlag15();

            // Server 1.7 Auth Sorenson Evaluation DateLimit TOday 17 Days 5
            // potency even flags
            decoded = serialNum.decode("ZRXG8A-JUS59U-6VV74B-8SFYA2-R2TS8F", "sorenson");
            flag = serialNum.getFlag0();
            flag = serialNum.getFlag1();
            flag = serialNum.getFlag2();
            flag = serialNum.getFlag3();
            flag = serialNum.getFlag4();
            flag = serialNum.getFlag5();
            flag = serialNum.getFlag6();
            flag = serialNum.getFlag7();
            flag = serialNum.getFlag8();
            flag = serialNum.getFlag9();
            flag = serialNum.getFlag10();
            flag = serialNum.getFlag11();
            flag = serialNum.getFlag12();
            flag = serialNum.getFlag13();
            flag = serialNum.getFlag14();
            flag = serialNum.getFlag15();

            // Server 1.7 Auth Sorenson Evaluation DateLimit TOday 17 Days 5
            // potency 8-15 flags
            decoded = serialNum.decode("V5P94M-3PYTVU-HMWNCT-DHSC9R-6A7QKJ", "sorenson");
            flag = serialNum.getFlag0();
            flag = serialNum.getFlag1();
            flag = serialNum.getFlag2();
            flag = serialNum.getFlag3();
            flag = serialNum.getFlag4();
            flag = serialNum.getFlag5();
            flag = serialNum.getFlag6();
            flag = serialNum.getFlag7();
            flag = serialNum.getFlag8();
            flag = serialNum.getFlag9();
            flag = serialNum.getFlag10();
            flag = serialNum.getFlag11();
            flag = serialNum.getFlag12();
            flag = serialNum.getFlag13();
            flag = serialNum.getFlag14();
            flag = serialNum.getFlag15();

            flag = !flag; // remove warning

            // Bad Auth
            decoded = serialNum.decode("M7QFCG-XEWXQA-AQUVV4-VTGRJ4-A6NMYG", "fred");

            assert (decoded);

            // Bad characters zero and oh
            decoded = serialNum.decode("0oQFCG-XEWXQA-AQUVV4-VTGRJ4-A6NMYG", "sorenson");

            // Bad Serial
            decoded = serialNum.decode("A7QFCG-XEWXQA-AQUVV4-VTGRJ4-A6NMYG", "sorenson");

            // Too Short
            decoded = serialNum.decode("A7QFCG-XEWXQA-AQUVV4-VTGRJ4", "sorenson");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public enum DateType {
        ExpireFromInstall, ExpireFromToday
    };

    private final String _kProductName = "Squeeze Server";
    private String _serialNumber = new String();
    private int _referenceNumber; // this is number available nodes for license
    private int _versionMajor;
    private int _versionMinor;
    private int _evalDays;
    private int _dateType;
    private int _potency;
    private int _evalType;
    private int _flags;

    public int getVersionMajor() {
        return _versionMajor;
    }

    public int getVersionMinor() {
        return _versionMinor;
    }

    public int getReferenceNumber() {
        return _referenceNumber;
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag15() {
        return ((_flags & 0x8000) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag14() {
        return ((_flags & 0x4000) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag13() {
        return ((_flags & 0x2000) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag12() {
        return ((_flags & 0x1000) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag11() {
        return ((_flags & 0x0800) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag10() {
        return ((_flags & 0x0400) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag9() {
        return ((_flags & 0x0200) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag8() {
        return ((_flags & 0x0100) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag7() {
        return ((_flags & 0x0080) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag6() {
        return ((_flags & 0x0040) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag5() {
        return ((_flags & 0x0020) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag4() {
        return ((_flags & 0x0010) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag3() {
        return ((_flags & 0x0008) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag2() {
        return ((_flags & 0x0004) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag1() {
        return ((_flags & 0x0002) != 0);
    }

    // This function should be renamed when the flag is given a name in the
    // serial number generator
    public boolean getFlag0() {
        return ((_flags & 0x0001) != 0);
    }

    public boolean getEvalUse() {
        return (_evalType != 0);
    }

    public DateType getDateType() {
        if (0 == _dateType) {
            return DateType.ExpireFromToday;
        } else {
            return DateType.ExpireFromInstall;
        }
    }

    public int getEvalDays() {
        return _evalDays;
    }

    public int getPotency() {
        return _potency;
    }

    public boolean decode(String serialNumber, String companyName) throws Exception {
        boolean decoded = false;

        _serialNumber = serialNumber.trim();

        _serialNumber = _serialNumber.replace("-", "");

        _serialNumber = _serialNumber.toUpperCase();

        if (checkSerialHash(_serialNumber)) {
            // Strip off last 4 checksum digits
            _serialNumber = _serialNumber.substring(0, 26);

            try {
                byte[] decryptedData = decrypt(_serialNumber, createKey(companyName));

                int index = 0;
                // Not currently using the time value
                // long ntpTime = byteToInt(decryptedData, index);
                index += 4;
                int product = ByteToShort(decryptedData, index);
                index += 2;
                _referenceNumber = ByteToShort(decryptedData, index);
                index += 2;
                long decodedHash = byteToInt(decryptedData, index);
                index += 4;
                int evalCode = ByteToShort(decryptedData, index);
                index += 2;
                _flags = ByteToShort(decryptedData, index);

                // Check hash to see we have a valid serial number
                int expectedHash = makeHash(_kProductName, companyName);

                int productId = (product & 0xfc00) >> 10;
                _versionMajor = (product & 0x3c0) >> 6;
                _versionMinor = (product & 0x3f);
                _evalDays = (evalCode & 0xff00) >> 8;
                _dateType = (evalCode & 0x0080) >> 7; // 0 = Expires N days
                // after today, 1 =
                // Expires N days after
                // Install
                _potency = (evalCode & 0x070) >> 4;
                _evalType = (evalCode & 0x0f);

                decoded = (13 == productId && decodedHash == expectedHash); // Squeeze
                // Server
            } catch (InvalidKeyException keyExcept) {
                decoded = false;
            } catch (UnsupportedEncodingException encodeExcept) {
                decoded = false;
            }
        }

        return decoded;
    }

    private boolean checkSerialHash(String serialNumber) {
        boolean valid = false;

        if (serialNumber.length() == 30) {
            CRC32 serialHash = new CRC32();
            String serialBaseStr = serialNumber.substring(0, 26);
            String serialHashStr = serialNumber.substring(26);

            serialHash.update(serialBaseStr.getBytes());
            long actualHash = serialHash.getValue();

            // This return 3 bytes
            byte[] threeByteExpected = sorensonBase32Decode(serialHashStr);
            // We need 4 bytes to convert to 32 bit number
            byte[] expectedHashBytes = new byte[4];
            for (int i = 0; i < 3 && threeByteExpected.length >= 3; i++) {
                expectedHashBytes[i] = threeByteExpected[i];
            }
            long expectedHash = byteToInt(expectedHashBytes, 0);

            // 4 Sorenson Base 32 characters (digits) only create 20 significant
            // bits
            actualHash = (actualHash & 0xFFFFF000);
            expectedHash = (expectedHash & 0x00FFFFF000);
            valid = (actualHash == expectedHash);
        }
        return valid;
    }

    private byte[] createKey(String companyName) throws NoSuchAlgorithmException {

        // MessageDigest md = HashFactory.getInstance("MD5");
        MessageDigest md = MessageDigest.getInstance("MD5");
        String product = _kProductName;
        md.update(product.getBytes(), 0, product.length());
        md.update(companyName.getBytes(), 0, companyName.length());

        return md.digest();
    }

    private int makeHash(String productName, String authorName) {
        CRC32 prodAuthCRC = new CRC32();
        prodAuthCRC.update(productName.getBytes());
        prodAuthCRC.update(authorName.getBytes());
        long hashValue = prodAuthCRC.getValue();
        long reorderedHash = ((hashValue & 0x000000FF) << 24) | ((hashValue & 0x0000FF00) << 8)
                | ((hashValue & 0x00FF0000) >> 8) | ((hashValue & 0xFF000000) >> 24);
        return (int) reorderedHash;
    }

    /*
     * convert the bytes starting at index to an long
     */
    private long byteToInt(byte[] byteData, int index) {
        long value = 0;

        for (int i = index; i < byteData.length && i < 4 + index; i++) {
            value = (value << 8) + (byteData[i] & 0xff);
        }
        return value;
    }

    /*
     * convert the bytes starting at index to an int
     */
    private int ByteToShort(byte[] byteData, int index) {
        Integer value = 0;

        for (int i = index; i < byteData.length && i < 2 + index; i++) {
            value = (value << 8) + (byteData[i] & 0xff);
        }

        return value;
    }

    private String _sorensonBase32EncodingTable = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";

    // Note that O,0,I, and 1 are omitted to avoid confusion. 26+10-4=32
    // entries.

    private byte[] sorensonBase32Decode(String encryptedText) {

        int bitLen = encryptedText.length() * 5;
        int byteLen = bitLen / 8;
        if (bitLen % 8 > 0) {
            byteLen += 1;
        }
        byte[] byteData = new byte[byteLen];

        int bitCount = 0; // bit used
        byte currentByte = 0;
        byte nextByte = 0;

        while (encryptedText.length() > 0) {
            Integer charValue = _sorensonBase32EncodingTable.indexOf(encryptedText.charAt(0));
            Integer shiftValue32Bit = 0;

            switch (bitCount % 8) {
            case 0: {
                shiftValue32Bit = charValue << 3;
                currentByte = shiftValue32Bit.byteValue();
                nextByte = 0;
                break;
            }
            case 1: {
                shiftValue32Bit = currentByte | charValue << 2;
                currentByte = shiftValue32Bit.byteValue();
                nextByte = 0;
                break;
            }
            case 2: {
                shiftValue32Bit = currentByte | (charValue << 1);
                currentByte = shiftValue32Bit.byteValue();
                nextByte = 0;
                break;
            }
            case 3: {
                shiftValue32Bit = currentByte | charValue;
                currentByte = shiftValue32Bit.byteValue();
                nextByte = 0;
                break;
            }
            case 4: {
                shiftValue32Bit = currentByte | (charValue >> 1);
                currentByte = shiftValue32Bit.byteValue();
                shiftValue32Bit = (charValue & 0x01) << 7;
                nextByte = shiftValue32Bit.byteValue();
                break;
            }
            case 5: {
                shiftValue32Bit = currentByte | (charValue >> 2);
                currentByte = shiftValue32Bit.byteValue();
                shiftValue32Bit = (charValue & 0x03) << 6;
                nextByte = shiftValue32Bit.byteValue();
                break;
            }
            case 6: {
                shiftValue32Bit = currentByte | (charValue >> 3);
                currentByte = shiftValue32Bit.byteValue();
                shiftValue32Bit = (charValue & 0x07) << 5;
                nextByte = shiftValue32Bit.byteValue();
                break;
            }
            case 7: {
                shiftValue32Bit = currentByte | (charValue >> 4);
                currentByte = shiftValue32Bit.byteValue();
                shiftValue32Bit = (charValue & 0x0F) << 4;
                nextByte = shiftValue32Bit.byteValue();
                break;
            }
            }

            if (bitCount % 8 >= 3) {
                // byte is complete
                byteData[bitCount / 8] = currentByte;
                currentByte = nextByte;
                nextByte = 0;
            }
            bitCount += 5; // We processed 5 bits
            encryptedText = encryptedText.substring(1);
        }

        if (bitCount % 8 != 0) {
            byteData[bitCount / 8] = currentByte;
        }

        return byteData;
    }

    public byte[] decrypt(String cookieValue, byte[] keyBytes)
            throws InvalidKeyException, UnsupportedEncodingException {
        byte[] encryptedData;
        byte[] decryptedData;
        TwofishEngine t = new TwofishEngine();

        // ///////////////////

        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new TwofishEngine()));

        cipher.init(false, new KeyParameter(keyBytes));

        // create the key
        // byte[] keyBytes = key.getBytes();
        // Object keyObject = twofish.makeKey(keyBytes, 16);
        // make the length of the string a multiple of
        // the block size
        while (cookieValue.length() < (32 * 8 / 5)) {
            cookieValue += "A";
        }
        // initialize byte arrays that will hold encrypted/decrypted
        // text

        encryptedData = sorensonBase32Decode(cookieValue);
        decryptedData = new byte[cookieValue.length() * 2];

        /*
         * int oLen = cipher.processBytes(encryptedData, 0,
         * encryptedData.length, decryptedData, 0); try {
         * cipher.doFinal(decryptedData, oLen); } catch (CryptoException ce) {
         * ce.printStackTrace(); }
         */
        // String test = new String(rv).trim();

        // ///

        encryptedData = sorensonBase32Decode(cookieValue);
        decryptedData = new byte[cookieValue.length() * 2];
        // Iterate over the byte arrays by 16-byte blocks and decrypt.
        for (int i = 0; i < Array.getLength(encryptedData); i += 16) {
            // twofish.decrypt(encryptedData, i, decryptedData, i, keyObject,
            // 16);

            cipher.processBytes(encryptedData, i, 16, decryptedData, 0);

        }

        return decryptedData;
    }
}