001package com.bitbucket.thinbus.srp6.js;
002
003import java.math.BigInteger;
004import java.nio.charset.Charset;
005import java.nio.file.Files;
006import java.nio.file.Paths;
007import java.security.MessageDigest;
008import java.util.ArrayList;
009import java.util.List;
010import java.util.regex.Matcher;
011import java.util.regex.Pattern;
012
013import com.nimbusds.srp6.SRP6Routines;
014
015/**
016 * A class to parse the output of 'openssl dhparam -text bits' where bits is the
017 * prime number bit length. Will output 'N', 'g', 'k' in bases 10, 10, 16
018 * respectively. Note that k is derived from 'N' and 'g' but Nimbus 1.4.x
019 * currently uses a the byte array constructor of BigInteger to computes 'k'
020 * which is not available in Javascript so the value genenerated by Java needs
021 * to be configure in the Javascript.
022 */
023public class OpenSSLCryptoConfigConverter {
024
025        final SRP6Routines srp6Routines = new SRP6Routines();
026
027        public List<String> run(String hash, List<String> lines) throws Exception {
028                int generator = 0;
029                StringBuilder hexparts = new StringBuilder();
030                boolean capture = false;
031
032                for (String line : lines) {
033                        // skip everything up to and including 'prime:' then enable capture
034            if( !capture ) {
035                                if (line.endsWith("prime:")) {
036                                        capture = true;
037                                }
038                                continue;
039                        }
040
041                        // if we see 'generator' we are done. capture it and break
042                        if(line.contains("generator")) {
043                                try {
044                                        generator = generator(line.trim());
045                                        break;
046                                } catch (Exception e) {
047                                        throw new AssertionError(
048                                                        "could not parse 'generator: x' number out of line containing 'generator': "
049                                                                        + line);
050                                }
051                        }
052
053                        // if we got this far its the prime
054                        hexparts.append(line.trim());
055                }
056
057                if (generator <= 0) {
058                        throw new AssertionError(
059                                        "could not parse 'generator: x' number out of line containing 'generator'");
060                }
061
062                String primeHex = hexparts.toString().replace(":", "");
063
064                List<String> output = new ArrayList<String>();
065
066                BigInteger N = new BigInteger(primeHex, 16);
067                if( ! N.isProbablePrime(1)) throw new AssertionError("parsed N isn't prime. Aborting.");
068                BigInteger g = new BigInteger(generator + "");
069
070                output.add("hashing to create 'k' using " + hash);
071
072                MessageDigest digest = MessageDigest.getInstance(hash);
073                BigInteger k = srp6Routines.computeK(digest, N, g);
074
075                output.add("computing...");
076                output.add("N base10: " + N.toString(10));
077                output.add("g base10: " + g.toString(10));
078                output.add("k base16: " + k.toString(16));
079
080                return output;
081        }
082
083        public static void main(String[] args) throws Exception {
084
085                if (args.length != 2) {
086                        System.err.println("Arguments: file hash ");
087                        System.err.println("Example  : /tmp/my_dhparam.txt SHA-256 ");
088                        System.exit(1);
089                }
090
091                final String file = args[0];
092                final String hash = args[1];
093
094                System.out
095                                .println(String
096                                                .format("Attempting to load 'openssl dhparam -text <bitlength>' output text file at: %s",
097                                                                file));
098
099                final List<String> lines = Files.readAllLines(Paths.get(args[0]),
100                                Charset.forName("UTF8"));
101
102                System.out.println(String.format("Loaded %s lines.", lines.size()));
103
104                System.out.println(String.format(
105                                "Creating configuration parmeters using hash algorithm %s.",
106                                hash));
107
108                for (String output : (new OpenSSLCryptoConfigConverter()).run(hash,
109                                lines)) {
110                        System.out.println(output);
111                }
112        }
113
114        static Pattern generatorPattern = Pattern
115                        .compile(".*generator: (\\d*) \\(.*");
116
117        private static int generator(String line) {
118                Matcher matcher = generatorPattern.matcher(line);
119                matcher.matches();
120                String number = matcher.group(1);
121                return Integer.valueOf(number);
122        }
123}