diff --git a/build.gradle b/build.gradle index 26d24f74..ae1424e2 100644 --- a/build.gradle +++ b/build.gradle @@ -119,7 +119,7 @@ subprojects { 'com.fasterxml.jackson.core:jackson-databind:2.6.3', 'com.fasterxml.jackson.core:jackson-annotations:2.6.3'], jadira_usertype: 'org.jadira.usertype:usertype.core:4.0.0.GA', - jcommander: 'com.beust:jcommander:1.35', + jcommander: 'com.beust:jcommander:1.72', joda_time: 'joda-time:joda-time:2.9.4', jstl: [ 'org.apache.taglibs:taglibs-standard-impl:1.2.5', 'org.apache.taglibs:taglibs-standard-spec:1.2.5'], diff --git a/tools/tcg_rim_tool/build.gradle b/tools/tcg_rim_tool/build.gradle index fd331228..ba9d2398 100644 --- a/tools/tcg_rim_tool/build.gradle +++ b/tools/tcg_rim_tool/build.gradle @@ -7,6 +7,7 @@ repositories { dependencies { compile libs.minimal_json + compile libs.jcommander testCompile libs.testng } diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java index 9c83b043..f85a7813 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java @@ -1,51 +1,59 @@ package hirs.swid; import hirs.swid.utils.Commander; +import com.beust.jcommander.JCommander; + import java.io.IOException; -/* - * Command-line application for generating and validating SWID tags. - * Input arg: path to *.swidtag file - * - * If an argument is given it will be validated against the schema at http://standards.iso.org/iso/19770/-2/2015/schema.xsd - * If an argument is not given a SWID tag file will be generated. - */ public class Main { public static void main(String[] args) { - Commander commander = new Commander(args); + Commander commander = new Commander(); + JCommander jc = JCommander.newBuilder().addObject(commander).build(); + jc.parse(args); SwidTagGateway gateway = new SwidTagGateway(); - if (commander.hasArguments()) { - // we have arguments to work with - if (commander.isAttributesGiven()) { - gateway.setAttributesFile(commander.getAttributesFile()); - } - if (commander.isKeystoreGiven()) { - gateway.setKeystoreFile(commander.getKeystore()); - } - if (commander.isShowCert()) { - gateway.setShowCert(true); - } - - if (commander.create()) { - // parsing the arguments detected a create parameter (-c) - gateway.generateSwidTag(commander.getCreateOutFile()); - } - if (commander.validate()) { - // parsing the arguments detected a validation parameter (-v) + if (commander.isHelp()) { + jc.usage(); + System.out.println(commander.printHelpExamples()); + } else if (!commander.getVerifyFile().isEmpty()) { + System.out.println(commander.toString()); + String verifyFile = commander.getVerifyFile(); + String publicCertificate = commander.getPublicCertificate(); + if (!verifyFile.isEmpty() && !publicCertificate.isEmpty()) { try { - gateway.validateSwidTag(commander.getValidateFile()); + gateway.validateSwidTag(verifyFile); } catch (IOException e) { - System.out.println("Unable to validate file: " + e.getMessage()); + System.out.println("Error validating RIM file: " + e.getMessage()); } + } else { + System.out.println("Need both a RIM file to validate and a public certificate to validate with!"); } - if (commander.parse()) { - try { - gateway.parsePayload(commander.getParseFile()); - } catch (IOException e) { - System.out.println("Unable to parse file: " + e.getMessage()); - } + } else { + System.out.println(commander.toString()); + String createType = commander.getCreateType().toUpperCase(); + String attributesFile = commander.getAttributesFile(); + String privateKeyFile = commander.getPrivateKeyFile(); + String alias = commander.getAlias(); + String privateKeyPassword = commander.getPrivateKeyPassword(); + switch (createType) { + case "BASE": + if (!attributesFile.isEmpty()) { + gateway.setAttributesFile(attributesFile); + } + if (!privateKeyFile.isEmpty() && + !alias.isEmpty() && + !privateKeyPassword.isEmpty()) { + gateway.setKeystoreFile(privateKeyFile); + gateway.setPrivateKeyAlias(alias); + gateway.setPrivateKeyPassword(privateKeyPassword); + } + gateway.generateSwidTag(commander.getOutFile()); + break; + case "EVENTLOG": + break; + case "PCR": + break; } } } diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java index 8673ae36..0840efef 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java @@ -1,6 +1,5 @@ package hirs.swid; -import javax.xml.bind.JAXB; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; @@ -29,7 +28,6 @@ import javax.xml.crypto.XMLStructure; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.crypto.dsig.DigestMethod; import javax.xml.crypto.dsig.Reference; -import javax.xml.crypto.dsig.SignatureMethod; import javax.xml.crypto.dsig.SignedInfo; import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLSignature; @@ -61,7 +59,6 @@ import java.io.FileOutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.security.InvalidAlgorithmParameterException; @@ -85,30 +82,16 @@ import java.util.Properties; import java.math.BigInteger; import hirs.swid.utils.CsvParser; -import hirs.swid.utils.HashSwid; -import hirs.swid.xjc.BaseElement; -import hirs.swid.xjc.CanonicalizationMethodType; -import hirs.swid.xjc.DigestMethodType; import hirs.swid.xjc.Directory; import hirs.swid.xjc.Entity; import hirs.swid.xjc.Link; import hirs.swid.xjc.ObjectFactory; import hirs.swid.xjc.ResourceCollection; -import hirs.swid.xjc.ReferenceType; -import hirs.swid.xjc.SignatureType; -import hirs.swid.xjc.SignatureValueType; -import hirs.swid.xjc.SignatureMethodType; -import hirs.swid.xjc.SignedInfoType; import hirs.swid.xjc.SoftwareIdentity; import hirs.swid.xjc.SoftwareMeta; -import hirs.swid.xjc.TransformType; -import hirs.swid.xjc.TransformsType; import com.eclipsesource.json.Json; import com.eclipsesource.json.JsonObject; -import com.eclipsesource.json.JsonObject.Member; -import com.eclipsesource.json.JsonValue; -import com.eclipsesource.json.Location; import com.eclipsesource.json.ParseException; @@ -145,6 +128,8 @@ public class SwidTagGateway { * which would need to be passed in if not using the default keystore. */ private String keystoreFile; + private String privateKeyAlias; + private String privateKeyPassword; private boolean showCert; /** @@ -157,6 +142,8 @@ public class SwidTagGateway { unmarshaller = jaxbContext.createUnmarshaller(); attributesFile = SwidTagConstants.DEFAULT_ATTRIBUTES_FILE; keystoreFile = SwidTagConstants.DEFAULT_KEYSTORE_PATH; + privateKeyAlias = SwidTagConstants.DEFAULT_PRIVATE_KEY_ALIAS; + privateKeyPassword = SwidTagConstants.DEFAULT_KEYSTORE_PASSWORD; showCert = false; } catch (JAXBException e) { System.out.println("Error initializing jaxbcontext: " + e.getMessage()); @@ -173,12 +160,28 @@ public class SwidTagGateway { /** * Setter for String holding keystore path - * @param keystore + * @param keystoreFile */ public void setKeystoreFile(String keystoreFile) { this.keystoreFile = keystoreFile; } + /** + * Setter for String holding private key alias + * @param privateKeyAlias + */ + public void setPrivateKeyAlias(String privateKeyAlias) { + this.privateKeyAlias = privateKeyAlias; + } + + /** + * Setter for String holding private key password + * @param privateKeyPassword + */ + public void setPrivateKeyPassword(String privateKeyPassword) { + this.privateKeyPassword = privateKeyPassword; + } + /** * Setter for boolean to display certificate block in xml signature * @param showCert @@ -267,12 +270,11 @@ public class SwidTagGateway { /** * This method generates a base RIM from the values in a JSON file. * - * @param outputFile + * @param filename */ public void generateSwidTag(final String filename) { SoftwareIdentity swidTag = null; try { - System.out.println("Reading base rim values from " + attributesFile); BufferedReader jsonIn = Files.newBufferedReader(Paths.get(attributesFile), StandardCharsets.UTF_8); JsonObject configProperties = Json.parse(jsonIn).asObject(); //SoftwareIdentity @@ -319,11 +321,7 @@ public class SwidTagGateway { Document signedSoftwareIdentity = signXMLDocument(objectFactory.createSoftwareIdentity(swidTag)); System.out.println("Signature core validity: " + validateSignedXMLDocument(signedSoftwareIdentity)); - if (!filename.isEmpty()) { - writeSwidTagFile(signedSoftwareIdentity, new File(filename)); - } else { - writeSwidTagFile(signedSoftwareIdentity, generatedFile); - } + writeSwidTagFile(signedSoftwareIdentity, filename); } /** @@ -368,17 +366,18 @@ public class SwidTagGateway { * * @param swidTag */ - public void writeSwidTagFile(Document swidTag, File outputFile) { + public void writeSwidTagFile(Document swidTag, String output) { try { - OutputStream outStream = new FileOutputStream(outputFile); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); Source source = new DOMSource(swidTag); - System.out.println("Writing to file: " + outputFile.getName()); - transformer.transform(source, new StreamResult(outStream)); - transformer.transform(source, new StreamResult(System.out)); + if (output.isEmpty()) { + transformer.transform(source, new StreamResult(System.out)); + } else { + transformer.transform(source, new StreamResult(new FileOutputStream(output))); + } } catch (FileNotFoundException e) { System.out.println("Unable to write to file: " + e.getMessage()); } catch (TransformerConfigurationException e) { @@ -392,7 +391,7 @@ public class SwidTagGateway { * This method creates SoftwareIdentity element based on the parameters read in from * a properties file. * - * @param properties the Properties object containing parameters from file + * @param jsonObject the Properties object containing parameters from file * @return SoftwareIdentity object created from the properties */ private SoftwareIdentity createSwidTag(JsonObject jsonObject) { @@ -624,9 +623,9 @@ public class SwidTagGateway { Collections.singletonList(reference) ); KeyStore keystore = KeyStore.getInstance("JKS"); - keystore.load(new FileInputStream(keystoreFile), SwidTagConstants.DEFAULT_KEYSTORE_PASSWORD.toCharArray()); - KeyStore.PrivateKeyEntry privateKey = (KeyStore.PrivateKeyEntry) keystore.getEntry(SwidTagConstants.DEFAULT_PRIVATE_KEY_ALIAS, - new KeyStore.PasswordProtection(SwidTagConstants.DEFAULT_KEYSTORE_PASSWORD.toCharArray())); + keystore.load(new FileInputStream(keystoreFile), privateKeyPassword.toCharArray()); + KeyStore.PrivateKeyEntry privateKey = (KeyStore.PrivateKeyEntry) keystore.getEntry(privateKeyAlias, + new KeyStore.PasswordProtection(privateKeyPassword.toCharArray())); X509Certificate certificate = (X509Certificate) privateKey.getCertificate(); KeyInfoFactory kiFactory = sigFactory.getKeyInfoFactory(); ArrayList x509Content = new ArrayList(); diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java index 5c51e167..e3e2bba1 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java @@ -6,292 +6,131 @@ import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Paths; +import com.beust.jcommander.Parameter; + /** * Commander is a class that handles the command line arguments for the SWID - * Tags gateway. + * Tags gateway by implementing the JCommander package. */ public class Commander { - private static final String COMMAND_PREFIX = "-"; - private static final String FULL_COMMAND_PREFIX = "--"; - private static final String CREATE_STRING = "create"; - private static final String VERIFY_STRING = "verify"; - private static final String HELP_STRING = "help"; - private static final String PARSE_STRING = "parse"; - private static final String ATTRIBUTES_STRING = "attributes"; - private static final String KEYSTORE_STRING = "keystore"; - private static final String SHOW_CERT_STRING = "show-cert"; - - private boolean hasArguments = false; - private boolean validate = false; - private boolean create = false; - private boolean parse = false; - private boolean attributesGiven = false; - private boolean keystoreGiven = false; - private boolean showCert = false; - - private String validateFile; - private String createOutFile = ""; - private String parseFile; + @Parameter(names = {"-h", "--help"}, help = true, description = "Print this help text.") + private boolean help; + @Parameter(names = {"-c", "--create"}, order = 0, + description = "The type of RIM to create. A base RIM will be created by default.") + private String createType = "base";//other possible values: "eventlog" and "pcr" + @Parameter(names = {"-a", "--attributes"}, order = 1, + description = "The configuration file holding attributes to populate the base RIM with.") private String attributesFile = ""; - private String keystore = ""; - private String hashAlg = null; + @Parameter(names = {"-o", "--out"}, order = 2, + description = "The file to write the RIM out to. The RIM will be written to stdout by default.") + private String outFile = ""; + @Parameter(names = {"-v", "--verify"}, order = 3, + description = "Specify a RIM file to verify.") + private String verifyFile = ""; + @Parameter(names = {"-k", "--privateKeyFile"}, order = 4, + description = "File containing the private key used to sign the base RIM created by the create function.") + private String privateKeyFile = ""; + @Parameter(names = {"--alias"}, order = 5, + description = "The alias of the private key") + private String alias = ""; + @Parameter(names = {"--password"}, order = 6, + description = "Password for the private key", password = true) + private String privateKeyPassword = ""; + @Parameter(names = {"-p", "--publicCertificate"}, order = 7, + description = "The public key certificate used to verify a RIM file or to embed in a signed RIM. " + + "A signed RIM generated by this tool by default will not show the signing certificate without this parameter present.") + private String publicCertificate = ""; + @Parameter(names = {"-l", "--rimel"}, order = 8, + description = "The TCG eventlog file to use as a support RIM. By default the last system eventlog will be used.") + private String rimEventLog = ""; + @Parameter(names = {"-t", "--rimpcr"}, order = 9, + description = "The file containing TPM PCR values to use as a support RIM. By default the current platform TPM will be used.") + private String rimPcrs = ""; + //@Parameter(names = {}, order = 8, description = "") + private String toBeSigned = ""; + @Parameter(names = {"-s", "--addSignatureData"}, order = 10, + description = "Specify, in order, , , . The signature data in will be" + + "combined with the data in and written to , or will overwrite if " + + "is not given.") + private String signatureData = ""; - /** - * The main constructor for the Commander class - * - * @param args - */ - public Commander(final String[] args) { - hasArguments = args.length > 0; - - if (hasArguments) { - parseArguments(args); - } else { - printHelp(); - } - - if (create) { - if (hashAlg == null) { - hashAlg = "256"; - } - - if (!getCreateOutFile().isEmpty() && !isValidPath(getCreateOutFile())) { - printHelp(String.format("Invalid file path %s!", getCreateOutFile())); - } - } + public boolean isHelp() { + return help; } - /** - * The default blank constructor - */ - public Commander() { - + public String getCreateType() { + return createType; } - /** - * This method is called if an empty Commander was created, and later gets - * args. Will be used by the main constructor. - * - * @param args - */ - public final void parseArguments(final String[] args) { - String tempValue; - - for (int i = 0; i < args.length; i++) { - tempValue = args[i]; - - switch (tempValue) { - case FULL_COMMAND_PREFIX + CREATE_STRING: - case COMMAND_PREFIX + "c": - create = true; - if (i+1 < args.length && !args[i+1].substring(0,1).equals(COMMAND_PREFIX)) { - createOutFile = args[++i]; - } - break; - case FULL_COMMAND_PREFIX + ATTRIBUTES_STRING: - case COMMAND_PREFIX + "a": - attributesGiven = true; - if (i+1 < args.length && !args[i+1].substring(0,1).equals(COMMAND_PREFIX)) { - attributesFile = args[++i]; - } - break; - case FULL_COMMAND_PREFIX + VERIFY_STRING: - case COMMAND_PREFIX + "v": - validate = true; - validateFile = args[++i]; - break; - case FULL_COMMAND_PREFIX + PARSE_STRING: - case COMMAND_PREFIX + "p": - parse = true; - parseFile = args[++i]; - break; - case FULL_COMMAND_PREFIX + SHOW_CERT_STRING: - showCert = true; - break; - case FULL_COMMAND_PREFIX + HELP_STRING: - case COMMAND_PREFIX + "h": - default: - printHelp(); - } - } - } - - /** - * Getter for the input validate file associated with the validate flag - * - * @return - */ - public final String getValidateFile() { - return validateFile; - } - - /** - * Getter for the output file for the create flag - * - * @return - */ - public final String getCreateOutFile() { - return createOutFile; - } - - /** - * Getter for the property that indicates if something was given at the - * commandline. - * - * @return - */ - public final boolean hasArguments() { - return hasArguments; - } - - /** - * Getter for the validate command flag. - * - * @return - */ - public final boolean validate() { - return validate; - } - - /** - * Getter for the create command flag. - * - * @return - */ - public final boolean create() { - return create; - } - - /** - * Getter for the hash algorithm to be used for hash functions. - * - * @return - */ - public final String getHashAlg() { - return hashAlg; - } - - /** - * Getter for the parse command flag - * - * @return - */ - public final boolean parse() { - return parse; - } - - /** - * Getter for the file to be parsed by the parse command flag - * - * @return - */ - public final String getParseFile() { - return parseFile; - } - - /** - * Getter for the attributes file given flag - * @return - */ - public boolean isAttributesGiven() { - return attributesGiven; - } - - /** - * Getter for the file containing attribute key-value pairs - * @return - */ public String getAttributesFile() { return attributesFile; } - /** - * Getter for the keystore given flag - * @return - */ - public boolean isKeystoreGiven() { - return keystoreGiven; + public String getOutFile() { + return outFile; } - /** - * Getter for the keystore used for digital signatures - * @return - */ - public String getKeystore() { - return keystore; + public String getVerifyFile() { + return verifyFile; } - /** - * Getter for boolean to show certificate data or not - * @return - */ - public boolean isShowCert() { - return showCert; + public String getPrivateKeyFile() { + return privateKeyFile; } - /** - * Default no parameter help method. - */ - private void printHelp() { - printHelp(null); + public String getAlias() { + return alias; } - /** - * This method is used to inform the user of the allowed functionality of - * the program. - */ - private void printHelp(String message) { + public String getPrivateKeyPassword() { + return privateKeyPassword; + } + + public String getPublicCertificate() { + return publicCertificate; + } + + public String getRimEventLog() { + return rimEventLog; + } + + public String getRimPcrs() { + return rimPcrs; + } + + public String getToBeSigned() { + return toBeSigned; + } + + public String getSignatureData() { + return signatureData; + } + + public String printHelpExamples() { StringBuilder sb = new StringBuilder(); + sb.append("Create a base RIM using the values in attributes.json; " + + "sign it with the default keystore, alias, and password;\n"); + sb.append("and write the data to base_rim.swidtag:\n\n"); + sb.append("\t\t-c base -a attributes.json -o base_rim.swidtag\n\n\n"); - if (message != null && !message.isEmpty()) { - sb.append(String.format("ERROR: %s\n\n", message)); - } - sb.append("Usage: HIRS_SwidTag\n"); - sb.append(" -c, --create \t\tCreate a base rim and write to\n" - + " \t\t\t\tthe given file. If no file is given the default is\n" - + " \t\t\t\tgenerated_swidTag.swidtag\n\n"); - sb.append(" -a, --attributes \tSpecify the JSON file that contains\n" - + " \t\t\t\tthe xml attributes to add to the RIM\n\n"); - sb.append(" -v, --verify\t\t\tTakes the provided input file and\n" - + " \t\t\t\tvalidates it against the schema at\n" - + " \t\t\t\thttp://standards.iso.org/iso/19770/-2/2015/schema.xsd\n\n"); - sb.append(" -p, --parse \t\tParse the given swidtag's payload\n\n"); -/* - sb.append(" -k, --keystore \tSpecify the keystore and its location to use\n" - + " \t\t\t\tfor digital signatures\n"); - */ - sb.append(" --show-cert\t\t\tPrint the certificate in the signature block of\n" - + " \t\t\t\tthe base RIM\n\n"); - sb.append(" -h, --help, \tPrints this command help information.\n"); - sb.append(" \t\t\t\tListing no command arguments will also\n" - + " \t\t\t\tprint this help text.\n\n"); - sb.append("Example commands: \n" - + " Create a base rim from the default attribute file and write the rim\n" - + " to generated_swidTag.swidtag:\n\n" - + " \t\tjava -jar tcg_rim_tool-1.0.jar -c\n\n" - + " Create a base rim from the values in config.json and write the rim\n" - + " to base_rim.swidtag:\n\n" - + " \t\tjava -jar tcg_rim_tool-1.0.jar -c base_rim.swidtag -a config.json\n\n" - + " "); - - System.out.println(sb.toString()); - System.exit(1); + return sb.toString(); } - - /** - * Checks that the file given to create a new swidtag is a valid path. - * @param filepath - * @return - */ - public static boolean isValidPath(String filepath) { - try { - System.out.println("Checking for a valid creation path..."); - File file = new File(filepath); - file.createNewFile(); - } catch (IOException | InvalidPathException | NullPointerException ex) { - return false; - } - return true; + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Creating: " + getCreateType() + System.lineSeparator()); + sb.append("Using attributes file: " + getAttributesFile() + System.lineSeparator()); + sb.append("Write to: " + getOutFile() + System.lineSeparator()); + sb.append("Verify file: " + getVerifyFile() + System.lineSeparator()); + sb.append("Private key file: " + getPrivateKeyFile() + System.lineSeparator()); + sb.append("Private key alias: " + getAlias() + System.lineSeparator()); + sb.append("Private key password: " + getPrivateKeyPassword() + System.lineSeparator()); + sb.append("Public certificate: " + getPublicCertificate() + System.lineSeparator()); + sb.append("Event log support RIM: " + getRimEventLog() + System.lineSeparator()); + sb.append("TPM PCRs support RIM: " + getRimPcrs() + System.lineSeparator()); + sb.append("Base RIM to be signed: " + getToBeSigned() + System.lineSeparator()); + sb.append("External signature file: " + getSignatureData() + System.lineSeparator()); + + return sb.toString(); } }