Merge branch 'master' into issue-252

This commit is contained in:
chubtub 2020-06-09 07:06:45 -04:00 committed by GitHub
commit 40da3cea59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 594 additions and 737 deletions

View File

@ -4,8 +4,8 @@ project(cpr-download NONE)
include(ExternalProject)
ExternalProject_Add(cpr
GIT_REPOSITORY https://github.com/whoshuu/cpr
GIT_TAG 1.3.0
URL https://github.com/whoshuu/cpr/archive/1.3.0.zip
URL_HASH SHA1=d669d94b41ffaa2de478923c35a83074e34fdc12
SOURCE_DIR "${CMAKE_BINARY_DIR}/lib/cpr-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/lib/cpr-build"
CONFIGURE_COMMAND ""

View File

@ -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'],

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDoTCCAomgAwIBAgIJAPB+r6VBhBn5MA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UECgwHRXhhbXBsZTERMA8GA1UECwwI
UENDbGllbnQxEjAQBgNVBAMMCUV4YW1wbGVDQTAeFw0yMDAzMTExODExMjJaFw0z
MDAxMTgxODExMjJaMFwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UE
CgwHRXhhbXBsZTERMA8GA1UECwwIUENDbGllbnQxGzAZBgNVBAMMEmV4YW1wbGUu
UklNLnNpZ25lcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKd1lWGk
SRuxAAY2wHag2GVxUk1dZx2PTpfQOflvLeccAVwa8mQhlsRERq+QK8ilj8Xfqs44
/nBaccZDOjdfIxIUCMfwhGXjxCaqZbgTucNsExDnu4arTGraoAwzHg0cVLiKT/Cx
j9NL4dcMgxRXsPdHfXb0923C7xYd2t2qfW05umgaj7qeQl6c68CFNsGX4JA8rWFQ
ZvvGx5DGlK4KTcjPuQQINs5fxasNKqLY2hq+z82x/rqwr2hmyizD6FpFSyIABPEM
PfB036GEhRwu1WEMkq8yIp2jgRUoFYke9pB3ph9pVow0Hh4mNFSKD4pP41VSKY1n
us83mdkuukPy5o0CAwEAAaNvMG0wHQYDVR0OBBYEFC/euOfQMKIgnaoBhhqWT+3s
8rzBMB8GA1UdIwQYMBaAFEahuO3bpnFf0NLneoo8XW6aw5Y4MAkGA1UdEwQCMAAw
CwYDVR0PBAQDAgbAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUA
A4IBAQBl2Bu9xpnHCCeeebjx+ILQXJXBd6q5+NQlV3zzBrf0bleZRtsOmsuFvWQo
KQxsfZuk7QcSvVd/1v8mqwJ0PwbFKQmrhIPWP+iowiBNqpG5PH9YxhpHQ1osOfib
NLOXMhudIQRY0yAgqQf+MOlXYa0stX8gkgftVBDRutuMKyOTf4a6d8TUcbG2Rnyz
O/6S9bq4cPDYLqWRBM+aGN8e00UWTKpBl6/1EU8wkJA6WdllK2e8mVkXUPWYyHTZ
0qQnrYiuLr36ycAznABDzEAoj4tMZbjIAfuscty6Ggzxl1WbyZLI6YzyXALwaYvr
crTLeyFynlKxuCfDnr1SAHDM65BY
-----END CERTIFICATE-----

View File

@ -7,6 +7,8 @@ repositories {
dependencies {
compile libs.minimal_json
compile libs.jcommander
compile libs.bouncy_castle
testCompile libs.testng
}
@ -17,6 +19,7 @@ jar {
)
}
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {}
exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA'
}
uploadArchives {

View File

@ -1,7 +1,10 @@
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:output indent="no" />
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Binary file not shown.

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCndZVhpEkbsQAG
NsB2oNhlcVJNXWcdj06X0Dn5by3nHAFcGvJkIZbEREavkCvIpY/F36rOOP5wWnHG
Qzo3XyMSFAjH8IRl48QmqmW4E7nDbBMQ57uGq0xq2qAMMx4NHFS4ik/wsY/TS+HX
DIMUV7D3R3129Pdtwu8WHdrdqn1tObpoGo+6nkJenOvAhTbBl+CQPK1hUGb7xseQ
xpSuCk3Iz7kECDbOX8WrDSqi2Noavs/Nsf66sK9oZsosw+haRUsiAATxDD3wdN+h
hIUcLtVhDJKvMiKdo4EVKBWJHvaQd6YfaVaMNB4eJjRUig+KT+NVUimNZ7rPN5nZ
LrpD8uaNAgMBAAECggEAcnG8npd9U0x7HMQMcsZoPaPdwHvF/gCzkLNA+8RM1bZh
A4ZzA5WlCQs0V8Wq9pyXjn7Wp8txsG1PdlT5k2AUgsVoXuR0R4IKyvYHQG9StEjH
GvWURmwJdLlnSg8hSYqEJ/52taNUDO6+MI8fgiaQDd8w0ryF4OCpLy9GJdnfkGYZ
Ayemb3USFUdj/S67NVqxnvAfFMM5FqkKGhkoy7wBRgO6eOeJvoTq8LMiPiponwwF
DW409ZStbrk1f1Oszst/UvFUWA9BdDfeoPmFR61y3eB5zlMQG8Mhr2v5hvkj9TPX
FU4Fm4EzZ1h/60cdWoP6XYCP7F2NqZ8N8u4UBQNAIQKBgQDcGIw5GJEvRF+FFTTR
hYatMRn80DGTVjdT32MgajdKx05OWxBmQsFob34fiSnr0wAXPJeDXG4ruMBE2bSk
EC8rCO08G8ihQoH8x0cvuERe1fpVWk3RWNucVGIiJSEXAIwWrlYZLTfYd5GqBkPE
OQxxo4MtOyqeHmVH1mOywk9ABQKBgQDCxt95luzqQZV9Xl78QQvOIbjOdHLjY23Z
yp8sGt9birL/WZ33TCRgmH1e61BdrSqO7Om/ail2Y59XM5UU6kLbDj0IgmOPTsrJ
JmIVf8r3bKltVUaLePgr4yex7dmtHRH8OkLXKnE0RCO0kCi9kJMB12yE3pWxk+Pu
zztQd3a66QKBgBNJd2g9deONe01fOVyu9clRhzR3ThDaOkj4R2h8xlGgO4V0R3Ce
ovIy6vt6epj2yYg/wAs720+rhfXCmijSXj/ILXnZ+W/gMyHimKNe42boG2LFYhJZ
Vg1R+7OAS3EHlD8ckeDs7Hrkp3gdymx0j1mZ+ZHKIIbwpPFxoRT2IBm9AoGBAI0Z
bIK0puP8psKvPrgWluq42xwUl7XKLaX8dtqIjQ3PqGP7E8g2TJP9Y7UDWrDB5Xas
gZi821R8Ts3o/DKukcgGxIgJjP4f4h9dwug4L1yWRxaBFB2tgHqqj/MBjxMtX/4M
Zqdgg6mNQyBm3lyVAynuWRrX9DE0JYa2cQ2VvVkhAoGBAMBv/oT813w00759PmkO
Uxv3LXTJuYBbq0Rmga25jN3ow8LrGQdSVg7F/af3I5KUF7mLiegDy1pkRfauyXH7
+WhEqnf86vDrzPpytDMxinWOQZusCqeWHb+nuVTuL3Fv+GxEdwVGYI/7lFJ7B//h
P5rU93ZoYY7sWcGVqaaEkMRU
-----END PRIVATE KEY-----

View File

@ -0,0 +1,213 @@
package hirs.swid;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.encoders.Base64;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
/**
* This class parses private key, public key, and certificate for use in their respective java.security objects.
*/
public class CredentialParser {
private static final String X509 = "X.509";
private static final String JKS = "JKS";
private static final String PEM = "PEM";
private static final String PKCS1_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
private static final String PKCS1_FOOTER = "-----END RSA PRIVATE KEY-----";
private static final String PKCS8_HEADER = "-----BEGIN PRIVATE KEY-----";
private static final String PKCS8_FOOTER = "-----END PRIVATE KEY-----";
private X509Certificate certificate;
private PrivateKey privateKey;
private PublicKey publicKey;
public X509Certificate getCertificate() {
return certificate;
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public PublicKey getPublicKey() {
return publicKey;
}
public void parseJKSCredentials() {
KeyStore.PrivateKeyEntry privateKeyEntry =
parseKeystorePrivateKey(SwidTagConstants.DEFAULT_KEYSTORE_PATH,
SwidTagConstants.DEFAULT_PRIVATE_KEY_ALIAS,
SwidTagConstants.DEFAULT_KEYSTORE_PASSWORD);
certificate = (X509Certificate) privateKeyEntry.getCertificate();
privateKey = privateKeyEntry.getPrivateKey();
publicKey = certificate.getPublicKey();
}
public void parsePEMCredentials(String certificateFile, String privateKeyFile) throws FileNotFoundException {
certificate = parsePEMCertificate(certificateFile);
privateKey = parsePEMPrivateKey(privateKeyFile, "RSA");
publicKey = certificate.getPublicKey();
}
/**
* This method returns the X509Certificate found in a PEM file.
* @param filename
* @return
* @throws FileNotFoundException
*/
private X509Certificate parsePEMCertificate(String filename) throws FileNotFoundException {
X509Certificate certificate = null;
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(filename);
bis = new BufferedInputStream(fis);
CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);
while (bis.available() > 0) {
certificate = (X509Certificate) certificateFactory.generateCertificate(bis);
}
bis.close();
} catch (CertificateException e) {
System.out.println("Error in certificate factory: " + e.getMessage());
} catch (IOException e) {
System.out.println("Error reading from input stream: " + e.getMessage());
} finally {
try {
if (fis != null) {
fis.close();
}
if (bis != null) {
bis.close();
}
} catch (IOException e) {
System.out.println("Error closing input stream: " + e.getMessage());
}
}
return certificate;
}
/**
* This method extracts the private key from a PEM file.
* Both PKCS1 and PKCS8 formats are handled.
* Algorithm argument is present to allow handling of multiple encryption algorithms,
* but for now it is always RSA.
* @param filename
* @return
*/
private PrivateKey parsePEMPrivateKey(String filename, String algorithm) {
PrivateKey privateKey = null;
FileInputStream fis = null;
DataInputStream dis = null;
try {
File file = new File(filename);
fis = new FileInputStream(file);
dis = new DataInputStream(fis);
byte[] key = new byte[(int) file.length()];
dis.readFully(key);
dis.close();
String privateKeyStr = new String(key);
if (privateKeyStr.contains(PKCS1_HEADER)) {
privateKey = getPKCS1KeyPair(filename).getPrivate();
} else if (privateKeyStr.contains(PKCS8_HEADER)) {
privateKeyStr = privateKeyStr.replace(PKCS8_HEADER, "");
privateKeyStr = privateKeyStr.replace(PKCS8_FOOTER, "");
byte[] decodedKey = Base64.decode(privateKeyStr);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
privateKey = keyFactory.generatePrivate(spec);
}
} catch (FileNotFoundException e) {
System.out.println("Unable to locate private key file: " + filename);
} catch (NoSuchAlgorithmException e) {
System.out.println("Unable to instantiate KeyFactory with algorithm: " + algorithm);
} catch (IOException e) {
System.out.println("IOException: " + e.getMessage());
} catch (InvalidKeySpecException e) {
System.out.println("Error instantiating PKCS8EncodedKeySpec object: " + e.getMessage());
} finally {
try {
if (fis != null) {
fis.close();
}
if (dis != null) {
dis.close();
}
} catch (IOException e) {
System.out.println("Error closing input stream: " + e.getMessage());
}
}
return privateKey;
}
/**
* This method reads a PKCS1 keypair from a PEM file.
* @param filename
* @return
*/
private KeyPair getPKCS1KeyPair(String filename) throws IOException {
Security.addProvider(new BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new FileReader(filename));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair keyPair = converter.getKeyPair((PEMKeyPair) pemParser.readObject());
return keyPair;
}
/**
* This method returns the private key from a JKS keystore.
* @param keystoreFile
* @param alias
* @param password
* @return KeyStore.PrivateKeyEntry
*/
private KeyStore.PrivateKeyEntry parseKeystorePrivateKey(String keystoreFile, String alias, String password) {
KeyStore keystore = null;
KeyStore.PrivateKeyEntry privateKey = null;
try {
keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(keystoreFile), password.toCharArray());
privateKey = (KeyStore.PrivateKeyEntry) keystore.getEntry(alias,
new KeyStore.PasswordProtection(password.toCharArray()));
} catch (FileNotFoundException e) {
System.out.println("Cannot locate keystore " + keystoreFile);
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableEntryException | CertificateException | IOException e) {
e.printStackTrace();
}
return privateKey;
}
/**
* Utility method for extracting the subjectKeyIdentifier from an X509Certificate.
* The subjectKeyIdentifier is stored as a DER-encoded octet and will be converted to a String.
* @return
*/
public String getCertificateSubjectKeyIdentifier() throws IOException {
String decodedValue = null;
byte[] extension = certificate.getExtensionValue(SwidTagConstants.CERTIFICATE_SUBJECT_KEY_IDENTIFIER);
if (extension != null) {
decodedValue = JcaX509ExtensionUtils.parseExtensionValue(extension).toString();
}
//If there is a # symbol at the beginning of the string, remove it
if (decodedValue.startsWith("#")) {
decodedValue = decodedValue.substring(1);
}
return decodedValue;
}
}

View File

@ -1,50 +1,60 @@
package hirs.swid;
import hirs.swid.utils.Commander;
import com.beust.jcommander.JCommander;
import java.io.FileNotFoundException;
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)
try {
gateway.validateSwidTag(commander.getValidateFile());
} catch (IOException e) {
System.out.println("Unable to validate file: " + e.getMessage());
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(verifyFile);
} catch (IOException e) {
System.out.println("Error validating RIM file: " + e.getMessage());
System.exit(1);
}
} else {
System.out.println("Need both a RIM file to validate and a public certificate to validate with!");
System.exit(1);
}
}
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 certificateFile = commander.getPublicCertificate();
String privateKeyFile = commander.getPrivateKeyFile();
switch (createType) {
case "BASE":
if (!attributesFile.isEmpty()) {
gateway.setAttributesFile(attributesFile);
}
if (!certificateFile.isEmpty() && !privateKeyFile.isEmpty()) {
gateway.setDefaultCredentials(false);
gateway.setPemCertificateFile(certificateFile);
gateway.setPemPrivateKeyFile(privateKeyFile);
}
gateway.generateSwidTag(commander.getOutFile());
break;
case "EVENTLOG":
break;
case "PCR":
break;
}
}
}

View File

@ -20,15 +20,10 @@ public class SwidTagConstants {
public static final String SIGNATURE_ALGORITHM_RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
public static final String SCHEMA_STATEMENT = "ISO/IEC 19770-2:2015 Schema (XSD 1.0) "
+ "- September 2015, see http://standards.iso.org/iso/19770/-2/2015/schema.xsd";
public static final String SCHEMA_PACKAGE = "hirs.swid.xjc";
public static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
public static final String SCHEMA_URL = "swid_schema.xsd";
public static final String HIRS_SWIDTAG_HEADERS = "hirsSwidTagHeader.properties";
public static final String EXAMPLE_PROPERTIES = "swidExample.properties";
public static final String SOFTWARE_IDENTITY = "SoftwareIdentity";
public static final String ENTITY = "Entity";
public static final String LINK = "Link";
@ -147,48 +142,5 @@ public class SwidTagConstants {
"http://csrc.nist.gov/ns/swid/2015-extensions/1.0",
"pathSeparator", "n8060");
//Below properties can probably be deleted
public static final String SOFTWARE_IDENTITY_NAME = "softwareIdentity.name";
public static final String SOFTWARE_IDENTITY_TAGID = "softwareIdentity.tagId";
public static final String SOFTWARE_IDENTITY_VERSION = "softwareIdentity.version";
public static final String SOFTWARE_IDENTITY_CORPUS = "softwareIdentity.corpus";
public static final String SOFTWARE_IDENTITY_PATCH = "softwareIdentity.patch";
public static final String SOFTWARE_IDENTITY_SUPPLEMENTAL = "softwareIdentity.supplemental";
public static final String ENTITY_NAME = "entity.name";
public static final String ENTITY_REGID = "entity.regid";
public static final String ENTITY_ROLE = "entity.role";
public static final String ENTITY_THUMBPRINT = "entity.thumbprint";
public static final String LINK_HREF = "link.href";
public static final String LINK_REL = "link.rel";
public static final String META_PCURILOCAL = "softwareMeta.pcUriLocal";
public static final String META_BINDINGSPEC = "softwareMeta.bindingSpec";
public static final String META_BINDINGSPECVERSION = "softwareMeta.bindingSpecVersion";
public static final String META_PLATFORMMANUFACTURERID = "softwareMeta.platformManufacturerId";
public static final String META_PLATFORMMANUFACTURERSTR = "softwareMeta.platformManufacturerStr";
public static final String META_PLATFORMMODEL = "softwareMeta.platformModel";
public static final String META_COMPONENTCLASS = "softwareMeta.componentClass";
public static final String META_COMPONENTMANUFACTURER = "softwareMeta.componentManufacturer";
public static final String META_COMPONENTMANUFACTURERID = "softwareMeta.componentManufacturerId";
public static final String META_RIMLINKHASH = "softwareMeta.rimLinkHash";
public static final String PAYLOAD_ENVVARPREFIX = "n8060.envvarprefix";
public static final String PAYLOAD_ENVVARSUFFIX = "n8060.envvarsuffix";
public static final String PAYLOAD_PATHSEPARATOR = "n8060.pathseparator";
public static final String DIRECTORY_KEY = "directory.key";
public static final String DIRECTORY_LOCATION = "directory.location";
public static final String DIRECTORY_NAME = "directory.name";
public static final String DIRECTORY_ROOT = "directory.root";
public static final String FILE_KEY = "file.key";
public static final String FILE_LOCATION = "file.location";
public static final String FILE_NAME = "file.name";
public static final String FILE_ROOT = "file.root";
public static final String FILE_SIZE = "file.size";
public static final String FILE_VERSION = "file.version";
public static final int PCR_NUMBER = 0;
public static final int PCR_VALUE = 1;
public static final String CERTIFICATE_SUBJECT_KEY_IDENTIFIER = "2.5.29.14";
}

View File

@ -1,18 +1,19 @@
package hirs.swid;
import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.UnmarshalException;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
@ -29,7 +30,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;
@ -37,78 +37,48 @@ import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.io.FileInputStream;
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;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
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;
@ -120,32 +90,16 @@ import com.eclipsesource.json.ParseException;
*/
public class SwidTagGateway {
private static final QName _DEFAULT_QNAME = new QName(
"http://www.w3.org/2000/09/xmldsig#", "SHA256", "ds");
private static final QName _SHA1Value_QNAME = new QName(
"http://www.w3.org/2000/09/xmldsig#", "SHA1", "ds");
private static final QName _SHA384Value_QNAME = new QName(
"http://www.w3.org/2000/09/xmldsig#", "SHA384", "ds");
private static final QName _SHA512Value_QNAME = new QName(
"http://www.w3.org/2000/09/xmldsig#", "SHA512", "ds");
private static final QName _SHA256_HASH = new QName(
"http://www.w3.org/2001/04/xmlenc#sha256", "hash", "SHA256");
private final ObjectFactory objectFactory = new ObjectFactory();
private final File generatedFile = new File("generated_swidTag.swidtag");
private QName hashValue = null;
private JAXBContext jaxbContext;
private Marshaller marshaller;
private Unmarshaller unmarshaller;
private String attributesFile;
/**
* The keystoreFile is used in signXMLDocument() to pass in the keystore path.
* The same method requires the keystore password and the alias of the private key,
* which would need to be passed in if not using the default keystore.
*/
private String keystoreFile;
private boolean showCert;
private boolean defaultCredentials;
private String pemPrivateKeyFile;
private String pemCertificateFile;
/**
* Default constructor initializes jaxbcontext, marshaller, and unmarshaller
@ -156,8 +110,8 @@ public class SwidTagGateway {
marshaller = jaxbContext.createMarshaller();
unmarshaller = jaxbContext.createUnmarshaller();
attributesFile = SwidTagConstants.DEFAULT_ATTRIBUTES_FILE;
keystoreFile = SwidTagConstants.DEFAULT_KEYSTORE_PATH;
showCert = false;
defaultCredentials = true;
pemCertificateFile = "";
} catch (JAXBException e) {
System.out.println("Error initializing jaxbcontext: " + e.getMessage());
}
@ -172,107 +126,37 @@ public class SwidTagGateway {
}
/**
* Setter for String holding keystore path
* @param keystore
* Setter for boolean governing signing credentials
* @param defaultCredentials
* @return
*/
public void setKeystoreFile(String keystoreFile) {
this.keystoreFile = keystoreFile;
public void setDefaultCredentials(boolean defaultCredentials) {
this.defaultCredentials = defaultCredentials;
}
/**
* Setter for boolean to display certificate block in xml signature
* @param showCert
* Setter for private key file in PEM format
* @param pemPrivateKeyFile
*/
public void setShowCert(boolean showCert) {
this.showCert = showCert;
public void setPemPrivateKeyFile(String pemPrivateKeyFile) {
this.pemPrivateKeyFile = pemPrivateKeyFile;
}
/**
* default generator method that has no parameters
/** Setter for certificate file in PEM format
* @param pemCertificateFile
*/
public void generateSwidTag() {
generateSwidTag("");
}
/**
* This generator method is used by the create method.
*
* This method should be updated to incorporate the RIM fields that are implemented
* in generateSwidTag(final File outputFile) below.
*
* @param inputFile - the file in csv format that is used as data
* @param outputFile - output specific to the given file
* @param hashType - the optional labeling of the hash type
*/
public void generateSwidTag(final String inputFile,
final String outputFile, final String hashType) {
// create file instances
File input = new File(inputFile);
File output = new File(outputFile);
List<String> tempList = new LinkedList<>();
// I need to go over this again about which needs to be checked.
if (input.exists()) {
// parse the csv file
CsvParser parser = new CsvParser(input);
for (String line : parser.getContent()) {
tempList.add(line);
}
if (hashType.contains("256")) {
hashValue = _DEFAULT_QNAME;
} else if (hashType.contains("384")) {
hashValue = _SHA384Value_QNAME;
} else if (hashType.contains("512")) {
hashValue = _SHA512Value_QNAME;
} else if (hashType.contains("1")) {
hashValue = _SHA1Value_QNAME;
} else {
hashValue = _DEFAULT_QNAME;
}
// generate a swid tag
Properties properties = new Properties();
InputStream is = null;
try {
is = SwidTagGateway.class.getClassLoader().getResourceAsStream(SwidTagConstants.HIRS_SWIDTAG_HEADERS);
properties.load(is);
SoftwareIdentity swidTag = createSwidTag(new JsonObject());
JAXBElement<Entity> entity = objectFactory.createSoftwareIdentityEntity(createEntity(new JsonObject()));
swidTag.getEntityOrEvidenceOrLink().add(entity);
// we should have resources, there for we need a collection
JAXBElement<ResourceCollection> resources = objectFactory.createSoftwareIdentityPayload(createPayload(tempList, hashValue));
swidTag.getEntityOrEvidenceOrLink().add(resources);
JAXBElement<SoftwareIdentity> jaxbe = objectFactory.createSoftwareIdentity(swidTag);
writeSwidTagFile(jaxbe, output);
} catch (IOException e) {
System.out.println("Error reading properties file: ");
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
// ignore
}
}
}
}
public void setPemCertificateFile(String pemCertificateFile) {
this.pemCertificateFile = pemCertificateFile;
}
/**
* 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
@ -318,12 +202,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);
}
/**
@ -334,51 +213,33 @@ public class SwidTagGateway {
* @param path the location of the file to be validated
*/
public boolean validateSwidTag(String path) throws IOException {
JAXBElement jaxbe = unmarshallSwidTag(path);
SoftwareIdentity swidTag = (SoftwareIdentity) jaxbe.getValue();
String output = String.format("name: %s;\ntagId: %s\n%s",
swidTag.getName(), swidTag.getTagId(),
SwidTagConstants.SCHEMA_STATEMENT);
System.out.println("SWID Tag found: ");
System.out.println(output);
Document document = unmarshallSwidTag(path);
Element softwareIdentity = (Element) document.getElementsByTagName("SoftwareIdentity").item(0);
StringBuilder si = new StringBuilder("Base RIM detected:\n");
si.append("SoftwareIdentity name: " + softwareIdentity.getAttribute("name") + "\n");
si.append("SoftwareIdentity tagId: " + softwareIdentity.getAttribute("tagId") + "\n");
System.out.println(si.toString());
System.out.println("Signature core validity: " + validateSignedXMLDocument(document));
return true;
}
/**
* This method calls the marshal() method that writes the swidtag data to the output file.
*
* @param jaxbe
* @param outputFile
*/
public void writeSwidTagFile(JAXBElement<SoftwareIdentity> jaxbe, File outputFile) {
JAXBContext jaxbContext;
try {
jaxbContext = JAXBContext.newInstance(SwidTagConstants.SCHEMA_PACKAGE);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jaxbe, outputFile);
} catch (JAXBException e) {
System.out.println("Error generating xml: ");
e.printStackTrace();
}
}
/**
* This method writes a Document object out to the file specified by generatedFile.
*
* @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 +253,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) {
@ -426,7 +287,7 @@ public class SwidTagGateway {
* This method creates an Entity object 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 Entity object created from the properties
*/
private Entity createEntity(JsonObject jsonObject) {
@ -463,7 +324,7 @@ public class SwidTagGateway {
/**
* Thsi method creates a Link 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 Link element created from the properties
*/
private Link createLink(JsonObject jsonObject) {
@ -483,7 +344,7 @@ public class SwidTagGateway {
/**
* This method creates a Meta 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 the Meta element created from the properties
*/
private SoftwareMeta createSoftwareMeta(JsonObject jsonObject) {
@ -514,15 +375,15 @@ public class SwidTagGateway {
/**
* This method creates a Payload from 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 the Payload object created
*/
private ResourceCollection createPayload(JsonObject jsonObject) {
ResourceCollection payload = objectFactory.createResourceCollection();
Map<QName, String> attributes = payload.getOtherAttributes();
addNonNullAttribute(attributes, SwidTagConstants._N8060_ENVVARPREFIX, jsonObject.getString(SwidTagConstants.PAYLOAD_ENVVARPREFIX, ""));
addNonNullAttribute(attributes, SwidTagConstants._N8060_ENVVARSUFFIX, jsonObject.getString(SwidTagConstants.PAYLOAD_ENVVARSUFFIX, ""));
addNonNullAttribute(attributes, SwidTagConstants._N8060_PATHSEPARATOR, jsonObject.getString(SwidTagConstants.PAYLOAD_PATHSEPARATOR, ""));
addNonNullAttribute(attributes, SwidTagConstants._N8060_ENVVARPREFIX, jsonObject.getString(SwidTagConstants._N8060_ENVVARPREFIX.getLocalPart(), ""));
addNonNullAttribute(attributes, SwidTagConstants._N8060_ENVVARSUFFIX, jsonObject.getString(SwidTagConstants._N8060_ENVVARSUFFIX.getLocalPart(), ""));
addNonNullAttribute(attributes, SwidTagConstants._N8060_PATHSEPARATOR, jsonObject.getString(SwidTagConstants._N8060_PATHSEPARATOR.getLocalPart(), ""));
return payload;
}
@ -530,7 +391,7 @@ public class SwidTagGateway {
/**
* This method creates a Directory from 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 Directory object created from the properties
*/
private Directory createDirectory(JsonObject jsonObject) {
@ -540,13 +401,7 @@ public class SwidTagGateway {
addNonNullAttribute(attributes, SwidTagConstants._SUPPORT_RIM_TYPE, jsonObject.getString(SwidTagConstants.SUPPORT_RIM_TYPE, ""));
addNonNullAttribute(attributes, SwidTagConstants._SUPPORT_RIM_FORMAT, jsonObject.getString(SwidTagConstants.SUPPORT_RIM_FORMAT, ""));
addNonNullAttribute(attributes, SwidTagConstants._SUPPORT_RIM_URI_GLOBAL, jsonObject.getString(SwidTagConstants.SUPPORT_RIM_URI_GLOBAL, ""));
/*
directory.setLocation(jsonObject.getString(SwidTagConstants.DIRECTORY_LOCATION));
String directoryRoot = jsonObject.getString(SwidTagConstants.DIRECTORY_ROOT);
if (!directoryRoot.isEmpty()) {
directory.setRoot(directoryRoot);
}
*/
return directory;
}
@ -554,8 +409,7 @@ public class SwidTagGateway {
* This method creates a hirs.swid.xjc.File from three arguments, then calculates
* and stores its hash as an attribute in itself.
*
* @param filename
* @param location
* @param jsonObject
* @return hirs.swid.xjc.File object from File object
*/
private hirs.swid.xjc.File createFile(JsonObject jsonObject) {
@ -577,32 +431,6 @@ public class SwidTagGateway {
}
}
/**
* This method creates a Payload from a list of Strings and a hash algorithm.
* The Strings in the list are expected to be in the form of "[PCR_NUMBER],[PCR_VALUE]"
* and the hash algorithm is attached as the file's xml namespace identifier.
*
* @param populate
* @return
*/
private ResourceCollection createPayload(List<String> populate, QName hashStr) {
ResourceCollection rc = objectFactory.createResourceCollection();
hirs.swid.xjc.File xjcFile = null;
String[] tempArray = null;
for (String item : populate) {
xjcFile = objectFactory.createFile();
tempArray = item.split(",");
xjcFile.setName(tempArray[SwidTagConstants.PCR_NUMBER]);
xjcFile.getOtherAttributes().put(hashStr, tempArray[SwidTagConstants.PCR_VALUE]);
rc.getDirectoryOrFileOrProcess().add(xjcFile);
}
return rc;
}
/**
* This method signs a SoftwareIdentity with an xmldsig in compatibility mode.
* Current assumptions: digest method SHA256, signature method SHA256, enveloped signature
@ -623,34 +451,47 @@ public class SwidTagGateway {
sigFactory.newSignatureMethod(SwidTagConstants.SIGNATURE_ALGORITHM_RSA_SHA256, null),
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()));
X509Certificate certificate = (X509Certificate) privateKey.getCertificate();
List<XMLStructure> keyInfoElements = new ArrayList<XMLStructure>();
KeyInfoFactory kiFactory = sigFactory.getKeyInfoFactory();
ArrayList<Object> x509Content = new ArrayList<Object>();
x509Content.add(certificate.getSubjectX500Principal().getName());
if (showCert) {
PrivateKey privateKey;
PublicKey publicKey;
CredentialParser cp = new CredentialParser();
if (defaultCredentials) {
cp.parseJKSCredentials();
privateKey = cp.getPrivateKey();
publicKey = cp.getPublicKey();
} else {
cp.parsePEMCredentials(pemCertificateFile, pemPrivateKeyFile);
X509Certificate certificate = cp.getCertificate();
privateKey = cp.getPrivateKey();
publicKey = cp.getPublicKey();
ArrayList<Object> x509Content = new ArrayList<Object>();
x509Content.add(certificate.getSubjectX500Principal().getName());
x509Content.add(certificate);
X509Data data = kiFactory.newX509Data(x509Content);
keyInfoElements.add(data);
}
X509Data data = kiFactory.newX509Data(x509Content);
KeyInfo keyinfo = kiFactory.newKeyInfo(Collections.singletonList(data));
KeyName keyName = kiFactory.newKeyName(cp.getCertificateSubjectKeyIdentifier());
keyInfoElements.add(keyName);
KeyValue keyValue = kiFactory.newKeyValue(publicKey);
keyInfoElements.add(keyValue);
KeyInfo keyinfo = kiFactory.newKeyInfo(keyInfoElements);
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
marshaller.marshal(swidTag, doc);
DOMSignContext context = new DOMSignContext(privateKey.getPrivateKey(), doc.getDocumentElement());
DOMSignContext context = new DOMSignContext(privateKey, doc.getDocumentElement());
XMLSignature signature = sigFactory.newXMLSignature(signedInfo, keyinfo);
signature.sign(context);
} catch (FileNotFoundException e) {
System.out.println("Keystore not found! " + e.getMessage());
} catch (IOException e) {
System.out.println("Error loading keystore: " + e.getMessage());
} catch (NoSuchAlgorithmException | KeyStoreException | InvalidAlgorithmParameterException |
ParserConfigurationException | UnrecoverableEntryException e) {
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException |
ParserConfigurationException e) {
System.out.println(e.getMessage());
} catch (CertificateException e) {
System.out.println("Certificate error: " + e.getMessage());
} catch (KeyException e) {
System.out.println("Error setting public key in KeyValue: " + e.getMessage());
} catch (JAXBException e) {
System.out.println("Error marshaling signed swidtag: " + e.getMessage());
} catch (MarshalException | XMLSignatureException e) {
@ -733,92 +574,26 @@ public class SwidTagGateway {
}
/**
* Given an input swidtag at [path] parse any PCRs in the payload into an InputStream object.
* This method will be used in a following pull request.
*
* @param path
* @return
* @throws IOException
*/
public ByteArrayInputStream parsePayload(String path) throws IOException {
JAXBElement jaxbe = unmarshallSwidTag(path);
SoftwareIdentity softwareIdentity = (SoftwareIdentity) jaxbe.getValue();
String pcrs = "";
if (!softwareIdentity.getEntityOrEvidenceOrLink().isEmpty()) {
List<Object> swidtag = softwareIdentity.getEntityOrEvidenceOrLink();
for (Object obj : swidtag) {
try {
JAXBElement element = (JAXBElement) obj;
String elementName = element.getName().getLocalPart();
if (elementName.equals(SwidTagConstants.PAYLOAD)) {
ResourceCollection rc = (ResourceCollection) element.getValue();
if (!rc.getDirectoryOrFileOrProcess().isEmpty()) {
pcrs = parsePCRs(rc.getDirectoryOrFileOrProcess());
}
}
} catch (ClassCastException e) {
System.out.println("Found a non-JAXBElement object!" + e.getMessage());
throw new IOException("Found an invalid element in the swidtag file!");
}
}
}
return new ByteArrayInputStream(pcrs.getBytes(StandardCharsets.UTF_8));
}
/**
* This method traverses a hirs.swid.xjc.Directory recursively until it finds at
* least one hirs.swid.xjc.File. This File is expected to have an attribute of the form
* "[hash algorithm]=[hash value]."
*
* @param list of swidtag elements
* @return the hash value(s) parsed from the File object(s)
*/
private String parsePCRs(List list) {
final String newline = System.lineSeparator();
StringBuilder sb = new StringBuilder();
for (Object listItem : list) {
if (listItem instanceof Directory) {
Directory dir = (Directory) listItem;
if (!dir.getDirectoryOrFile().isEmpty()) {
parsePCRs(dir.getDirectoryOrFile());
}
} else if (listItem instanceof hirs.swid.xjc.File){
hirs.swid.xjc.File pcr = (hirs.swid.xjc.File) listItem;
String pcrHash = "";
if (!pcr.getOtherAttributes().isEmpty()) {
Object[] fileAttributes = pcr.getOtherAttributes().values().toArray();
pcrHash = (String) fileAttributes[0];
}
if (pcrHash.isEmpty()) {
pcrHash = "null";
}
sb.append(pcr.getName() + "," + pcrHash);
}
}
System.out.println(sb.toString());
return sb.toString();
}
/**
* This method unmarshalls the swidtag found at [path] into a JAXBElement object
* This method unmarshalls the swidtag found at [path] into a Document object
* and validates it according to the schema.
*
* @param path to the input swidtag
* @return the SoftwareIdentity element at the root of the swidtag
* @throws IOException if the swidtag cannot be unmarshalled or validated
*/
private JAXBElement unmarshallSwidTag(String path) throws IOException {
File input = null;
InputStream is = null;
JAXBElement swidtag = null;
try {
input = new File(path);
is = SwidTagGateway.class.getClassLoader().getResourceAsStream(SwidTagConstants.SCHEMA_URL);
SchemaFactory schemaFactory = SchemaFactory.newInstance(SwidTagConstants.SCHEMA_LANGUAGE);
Schema schema = schemaFactory.newSchema(new StreamSource(is));
unmarshaller.setSchema(schema);
swidtag = (JAXBElement) unmarshaller.unmarshal(input);
} catch (SAXException e) {
private Document unmarshallSwidTag(String path) {
InputStream is = null;
Document document = null;
try {
document = removeXMLWhitespace(path);
is = SwidTagGateway.class.getClassLoader().getResourceAsStream(SwidTagConstants.SCHEMA_URL);
SchemaFactory schemaFactory = SchemaFactory.newInstance(SwidTagConstants.SCHEMA_LANGUAGE);
Schema schema = schemaFactory.newSchema(new StreamSource(is));
unmarshaller.setSchema(schema);
unmarshaller.unmarshal(document);
} catch (IOException e) {
System.out.println(e.getMessage());
} catch (SAXException e) {
System.out.println("Error setting schema for validation!");
} catch (UnmarshalException e) {
System.out.println("Error validating swidtag file!");
@ -827,18 +602,46 @@ public class SwidTagGateway {
} catch (JAXBException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
System.out.println("Error closing input stream");
}
}
if (swidtag != null) {
return swidtag;
} else {
throw new IOException("Invalid swidtag file!");
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
System.out.println("Error closing input stream");
}
}
}
return document;
}
/**
* This method strips all whitespace from an xml file, including indents and spaces
* added for human-readability.
* @param path
* @return
*/
private Document removeXMLWhitespace(String path) throws IOException {
TransformerFactory tf = TransformerFactory.newInstance();
Source source = new StreamSource(new File("identity_transform.xslt"));
Document document = null;
File input = new File(path);
if (input.length() > 0) {
try {
Transformer transformer = tf.newTransformer(source);
DOMResult result = new DOMResult();
transformer.transform(new StreamSource(input), result);
document = (Document) result.getNode();
} catch (TransformerConfigurationException e) {
System.out.println("Error configuring transformer!");
e.printStackTrace();
} catch (TransformerException e) {
System.out.println("Error transforming input!");
e.printStackTrace();
}
} else {
throw new IOException("Input file is empty!");
}
return document;
}
}

View File

@ -6,292 +6,119 @@ 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 \"base\""}, order = 0,
description = "The type of RIM to create. A base RIM will be created by default.")
private String createType = "";//other possible values: "eventlog" and "pcr"
@Parameter(names = {"-a", "--attributes <path>"}, order = 1,
description = "The configuration file holding attributes to populate the base RIM with.")
private String attributesFile = "";
private String keystore = "";
private String hashAlg = null;
/**
* 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()));
}
}
@Parameter(names = {"-o", "--out <path>"}, 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 <path>"}, order = 3,
description = "Specify a RIM file to verify.")
private String verifyFile = "";
@Parameter(names = {"-k", "--privateKeyFile <path>"}, order = 4,
description = "File containing the private key used to sign the base RIM created by the create function.")
private String privateKeyFile = "";
@Parameter(names = {"-p", "--publicCertificate <path>"}, order = 5,
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 <path>"}, order = 6,
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 <path>"}, order = 7,
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 <originalBaseRIM> <signatureFile> <outputFile>"}, order = 8,
description = "The signature data in <signatureFile> will be combined with the data in <originalBaseRIM>" +
"and written to <outputFile>, or will overwrite <originalBaseRIM> if <outputFile> is not given.")
private String signatureData = "";
*/
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 getPublicCertificate() {
return publicCertificate;
}
/**
* This method is used to inform the user of the allowed functionality of
* the program.
*/
private void printHelp(String message) {
StringBuilder sb = new StringBuilder();
if (message != null && !message.isEmpty()) {
sb.append(String.format("ERROR: %s\n\n", message));
}
sb.append("Usage: HIRS_SwidTag\n");
sb.append(" -c, --create <file>\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 <file>\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 <file>\t\tParse the given swidtag's payload\n\n");
/*
sb.append(" -k, --keystore <file>\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, <no args>\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);
public String getRimEventLog() {
return rimEventLog;
}
/**
* 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 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");
sb.append("Create a base RIM using the default attribute values; sign it using privateKey.pem;\n");
sb.append("and write the data to console output, to include cert.pem in the signature block:\n\n");
sb.append("\t\t-c base -k privateKey.pem -p cert.pem\n\n\n");
return sb.toString();
}
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("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();
}
}

View File

@ -14,17 +14,16 @@ import org.testng.annotations.Test;
public class TestSwidTagGateway {
private SwidTagGateway gateway;
private String inputFile, outputFile, hashType;
private final String DEFAULT_OUTPUT = "generated_swidTag.swidtag";
private final String DEFAULT_WITH_CERT = "generated_with_cert.swidtag";
private final String DEFAULT_NO_CERT = "generated_no_cert.swidtag";
private final String certificateFile = "RimSignCert.pem";
private final String privateKeyFile = "privateRimKey.pem";
private InputStream expectedFile;
@BeforeClass
public void setUp() throws Exception {
gateway = new SwidTagGateway();
inputFile = TestSwidTagGateway.class.getClassLoader().getResource("examplecsv.csv").getFile();
hashType = "SHA256";
}
@AfterClass
@ -35,29 +34,35 @@ public class TestSwidTagGateway {
}
/**
* Creating a base RIM with default attributes with an X509Certificate element.
* This test corresponds to the arguments:
* -c base -k privateRimKey.pem -p RimSignCert.pem
*/
@Test
public void testGenerateDefaultWithCert() {
gateway.setShowCert(true);
gateway.generateSwidTag();
public void testCreateBaseWithCert() {
gateway.setDefaultCredentials(false);
gateway.setPemCertificateFile(certificateFile);
gateway.setPemPrivateKeyFile(privateKeyFile);
gateway.generateSwidTag(DEFAULT_OUTPUT);
expectedFile = (InputStream) TestSwidTagGateway.class.getClassLoader().getResourceAsStream(DEFAULT_WITH_CERT);
Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT));
}
/**
* Create a base RIM with default attributes without an X509Certificate element.
* This test corresponds to the arguments:
* -c base
* -c base -a <path>
*/
@Test
public void testGenerateDefaultNoCert() {
gateway.setShowCert(false);
gateway.generateSwidTag();
public void testCreateBaseWithoutCert() {
gateway.setDefaultCredentials(true);
gateway.generateSwidTag(DEFAULT_OUTPUT);
expectedFile = (InputStream) TestSwidTagGateway.class.getClassLoader().getResourceAsStream(DEFAULT_NO_CERT);
Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT));
}
/**
* Validate a base RIM with default attributes with an X509Certificate element.
* This test corresponds to the arguments:
* -v <path> -p RimSignCert.pem
*/
@Test
public void testValidateSwidTag() {
@ -68,35 +73,6 @@ public class TestSwidTagGateway {
}
}
/**
* Verify expected values of a File element in a Payload element.
*/
@Test
public void testParsePayload() {
InputStream is = null;
outputFile = TestSwidTagGateway.class.getClassLoader().getResource(DEFAULT_WITH_CERT).getPath();
try {
is = gateway.parsePayload(outputFile);
Scanner scanner = new Scanner(is, "UTF-8");
String test = "Example.com.iotBase.bin,688e293e3ccb522f6cf8a027c9ade7960f84bd0bf3a0b99812bc1fa498a2db8d";
String temp = "";
while (scanner.hasNext()) {
temp = scanner.next();
Assert.assertEquals(temp, test, "temp: " + temp + ", test: " + test);
}
} catch (IOException e) {
Assert.fail("Error parsing test file!");
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
Assert.fail("Failed to close input stream!");
}
}
}
}
/**
* This method compares two files by bytes to determine if they are the same or not.
* @param file to be compared to the expected value.

View File

@ -26,9 +26,17 @@ zu3HTmQfeRYs/c6Ck1k3bL1jnyWoNzhBqCuPYrZtPbv9opVP0YOxM5IjRkRgkZIDgYbh1k4WXw8O
/iIMZuVJDfKQJSNCTAZsIbUatGDQc/nOihLHdI90wG8zu9amgrl1AEKzH8z864Fan5uuXolfAaak
sLJl6RPCNcp+JNCXMMZiS8bmYPQnVJc1ze0I1A==</SignatureValue>
<KeyInfo>
<X509Data>
<X509SubjectName>CN=example.RIM.signer,OU=PCClient,O=Example,ST=VA,C=US</X509SubjectName>
</X509Data>
<KeyName>2fdeb8e7d030a2209daa01861a964fedecf2bcc1</KeyName>
<KeyValue>
<RSAKeyValue>
<Modulus>p3WVYaRJG7EABjbAdqDYZXFSTV1nHY9Ol9A5+W8t5xwBXBryZCGWxERGr5AryKWPxd+qzjj+cFpx
xkM6N18jEhQIx/CEZePEJqpluBO5w2wTEOe7hqtMatqgDDMeDRxUuIpP8LGP00vh1wyDFFew90d9
dvT3bcLvFh3a3ap9bTm6aBqPup5CXpzrwIU2wZfgkDytYVBm+8bHkMaUrgpNyM+5BAg2zl/Fqw0q
otjaGr7PzbH+urCvaGbKLMPoWkVLIgAE8Qw98HTfoYSFHC7VYQySrzIinaOBFSgViR72kHemH2lW
jDQeHiY0VIoPik/jVVIpjWe6zzeZ2S66Q/LmjQ==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</SoftwareIdentity>

View File

@ -28,23 +28,35 @@ sLJl6RPCNcp+JNCXMMZiS8bmYPQnVJc1ze0I1A==</SignatureValue>
<KeyInfo>
<X509Data>
<X509SubjectName>CN=example.RIM.signer,OU=PCClient,O=Example,ST=VA,C=US</X509SubjectName>
<X509Certificate>MIIDYTCCAkmgAwIBAgIJAPB+r6VBhBn4MA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAlVTMQsw
<X509Certificate>MIIDoTCCAomgAwIBAgIJAPB+r6VBhBn5MA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIDAJWQTEQMA4GA1UECgwHRXhhbXBsZTERMA8GA1UECwwIUENDbGllbnQxEjAQBgNVBAMM
CUV4YW1wbGVDQTAeFw0yMDAyMTAxODE1MzRaFw0yOTEyMTkxODE1MzRaMFwxCzAJBgNVBAYTAlVT
CUV4YW1wbGVDQTAeFw0yMDAzMTExODExMjJaFw0zMDAxMTgxODExMjJaMFwxCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJWQTEQMA4GA1UECgwHRXhhbXBsZTERMA8GA1UECwwIUENDbGllbnQxGzAZBgNV
BAMMEmV4YW1wbGUuUklNLnNpZ25lcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKd1
lWGkSRuxAAY2wHag2GVxUk1dZx2PTpfQOflvLeccAVwa8mQhlsRERq+QK8ilj8Xfqs44/nBaccZD
OjdfIxIUCMfwhGXjxCaqZbgTucNsExDnu4arTGraoAwzHg0cVLiKT/Cxj9NL4dcMgxRXsPdHfXb0
923C7xYd2t2qfW05umgaj7qeQl6c68CFNsGX4JA8rWFQZvvGx5DGlK4KTcjPuQQINs5fxasNKqLY
2hq+z82x/rqwr2hmyizD6FpFSyIABPEMPfB036GEhRwu1WEMkq8yIp2jgRUoFYke9pB3ph9pVow0
Hh4mNFSKD4pP41VSKY1nus83mdkuukPy5o0CAwEAAaMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMC
BsAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAGuJ+dasb3/Mb7TBJ1Oe
al5ISq8d2LQD5ke5qnjgSQWKXfQ9fcUy3dWnt3Oked/i8B/Tyk3jCdTZJU3J3iRNgTqFfMLP8rU1
w2tPYBjjuPKiiK4YRBHPxtFxPdOL1BPmL4ZzNs33Lv6H0m4aff9p6QpMclX5b/CRjl+80JWRLiLj
U3B0CejZB9dJrPr9SBaC31cDoeTpja9Cl86ip7KkqrZZIYeMuNF6ucWyWtjrW2kr3UhmEy8x/6y4
KigsK8sBwmNv4N2Pu3RppeIcpjYj5NVA1hwRA4eeMgJp2u+urm3l1oo1UNX1HsSSBHp1Owc9zZLm
07Pl8T46kpIA4sroCAU=</X509Certificate>
Hh4mNFSKD4pP41VSKY1nus83mdkuukPy5o0CAwEAAaNvMG0wHQYDVR0OBBYEFC/euOfQMKIgnaoB
hhqWT+3s8rzBMB8GA1UdIwQYMBaAFEahuO3bpnFf0NLneoo8XW6aw5Y4MAkGA1UdEwQCMAAwCwYD
VR0PBAQDAgbAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQBl2Bu9xpnH
CCeeebjx+ILQXJXBd6q5+NQlV3zzBrf0bleZRtsOmsuFvWQoKQxsfZuk7QcSvVd/1v8mqwJ0PwbF
KQmrhIPWP+iowiBNqpG5PH9YxhpHQ1osOfibNLOXMhudIQRY0yAgqQf+MOlXYa0stX8gkgftVBDR
utuMKyOTf4a6d8TUcbG2RnyzO/6S9bq4cPDYLqWRBM+aGN8e00UWTKpBl6/1EU8wkJA6WdllK2e8
mVkXUPWYyHTZ0qQnrYiuLr36ycAznABDzEAoj4tMZbjIAfuscty6Ggzxl1WbyZLI6YzyXALwaYvr
crTLeyFynlKxuCfDnr1SAHDM65BY</X509Certificate>
</X509Data>
<KeyName>2fdeb8e7d030a2209daa01861a964fedecf2bcc1</KeyName>
<KeyValue>
<RSAKeyValue>
<Modulus>p3WVYaRJG7EABjbAdqDYZXFSTV1nHY9Ol9A5+W8t5xwBXBryZCGWxERGr5AryKWPxd+qzjj+cFpx
xkM6N18jEhQIx/CEZePEJqpluBO5w2wTEOe7hqtMatqgDDMeDRxUuIpP8LGP00vh1wyDFFew90d9
dvT3bcLvFh3a3ap9bTm6aBqPup5CXpzrwIU2wZfgkDytYVBm+8bHkMaUrgpNyM+5BAg2zl/Fqw0q
otjaGr7PzbH+urCvaGbKLMPoWkVLIgAE8Qw98HTfoYSFHC7VYQySrzIinaOBFSgViR72kHemH2lW
jDQeHiY0VIoPik/jVVIpjWe6zzeZ2S66Q/LmjQ==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</SoftwareIdentity>