/* * Copyright (C) 2014 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.privacyca.v2.rpc; import com.intel.dcsg.cpg.x509.X509Util; import com.intel.mtwilson.My; import com.intel.mtwilson.launcher.ws.ext.RPC; import com.intel.mtwilson.tpm.endorsement.jdbi.TpmEndorsementDAO; import com.intel.mtwilson.tpm.endorsement.jdbi.TpmEndorsementJdbiFactory; import com.intel.mtwilson.tpm.endorsement.model.TpmEndorsement; import gov.niarl.his.privacyca.TpmIdentityProof; import gov.niarl.his.privacyca.TpmIdentityRequest; import gov.niarl.his.privacyca.TpmKeyParams; import gov.niarl.his.privacyca.TpmPubKey; import gov.niarl.his.privacyca.TpmSymmetricKey; import gov.niarl.his.privacyca.TpmUtils; import gov.niarl.his.privacyca.TpmUtils.TpmUnsignedConversionException; import java.io.File; import java.util.List; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.Collection; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.Callable; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import org.apache.commons.codec.binary.Hex; import org.apache.commons.io.IOUtils; import org.apache.shiro.authz.annotation.RequiresPermissions; /** * * @author jbuhacoff */ @RPC("aik_request_get_challenge") public class IdentityRequestGetChallenge implements Callable<byte[]> { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(IdentityRequestGetChallenge.class); private byte[] identityRequest; private byte[] endorsementCertificate; public void setIdentityRequest(byte[] identityRequest) { this.identityRequest = identityRequest; } public void setEndorsementCertificate(byte[] endorsementCertificate) { this.endorsementCertificate = endorsementCertificate; } public byte[] getIdentityRequest() { return identityRequest; } public byte[] getEndorsementCertificate() { return endorsementCertificate; } private Map<String, X509Certificate> getEndorsementCertificates() throws IOException, CertificateException { Map<String, X509Certificate> endorsementCerts = new HashMap<>(); File ekCacertsPemFile = My.configuration().getPrivacyCaEndorsementCacertsFile(); try(FileInputStream in = new FileInputStream(ekCacertsPemFile)) { String ekCacertsPem = IOUtils.toString(in); // throws IOException List<X509Certificate> ekCacerts = X509Util.decodePemCertificates(ekCacertsPem); // throws CertificateException for(X509Certificate ekCacert : ekCacerts) { log.debug("Adding issuer {}", ekCacert.getSubjectX500Principal().getName()); endorsementCerts.put(ekCacert.getSubjectDN().getName(), ekCacert); } } return endorsementCerts; } @Override @RequiresPermissions("host_aiks:certify") public byte[] call() throws Exception { log.debug("PrivacyCA.p12: {}", My.configuration().getPrivacyCaIdentityP12().getAbsolutePath()); RSAPrivateKey caPrivKey = TpmUtils.privKeyFromP12(My.configuration().getPrivacyCaIdentityP12().getAbsolutePath(), My.configuration().getPrivacyCaIdentityPassword()); X509Certificate caPubCert = TpmUtils.certFromP12(My.configuration().getPrivacyCaIdentityP12().getAbsolutePath(), My.configuration().getPrivacyCaIdentityPassword()); // load the trusted ek cacerts Map<String, X509Certificate> endorsementCerts = getEndorsementCertificates(); //decrypt identityRequest and endorsementCertificate TpmIdentityRequest idReq = new TpmIdentityRequest(identityRequest); TpmIdentityProof idProof = idReq.decrypt(caPrivKey); TpmIdentityRequest tempEC = new TpmIdentityRequest(endorsementCertificate); X509Certificate ekCert = TpmUtils.certFromBytes(tempEC.decryptRaw(caPrivKey)); log.debug("Validating endorsement certificate"); if( !isEkCertificateVerifiedByAuthority(ekCert, endorsementCerts.get(ekCert.getIssuerDN().getName().replaceAll("\\x00", ""))) && !isEkCertificateVerifiedByAnyAuthority(ekCert, endorsementCerts.values()) && !isEkCertificateRegistered(ekCert)) { // cannot trust the EC because it's not signed by any of our trusted EC CAs and is not in the mw_tpm_ec table log.debug("EC is not trusted"); throw new RuntimeException("Invalid identity request"); } //check out the endorsement certificate //if the cert is good, issue challenge byte[] identityRequestChallenge = TpmUtils.createRandomBytes(32); //check the rest of the identity proof if(!idProof.checkValidity((RSAPublicKey)caPubCert.getPublicKey())){ log.error("TPM IDPROOF failed validity check"); throw new RuntimeException("Invalid identity request"); } // save the challenge and idproof for use in identity request submit response if the client successfully answers the challenge // the filename is the challenge (in hex) and the content is the idproof File datadir = new File(My.filesystem().getBootstrapFilesystem().getVarPath() + File.separator + "privacyca-aik-requests"); if( !datadir.exists() ) { datadir.mkdirs(); } String filename = TpmUtils.byteArrayToHexString(identityRequestChallenge); //Hex.encodeHexString(identityRequestChallenge) log.debug("Filename: {}", filename); try(FileOutputStream out = new FileOutputStream(datadir.toPath().resolve(filename).toFile())) { IOUtils.write(idProof.toByteArray(), out); } String optionsFilename = filename + ".opt"; try(FileOutputStream out = new FileOutputStream(datadir.toPath().resolve(optionsFilename).toFile())) { // and save the 3 trousers mode options into a second file because they are not included Util.TpmIdentityProofOptions options = new Util.TpmIdentityProofOptions(); options.TrousersModeIV = idProof.getIVmode(); options.TrousersModeSymkeyEncscheme = idProof.getSymkeyEncscheme(); options.TrousersModeBlankOeap = idProof.getOeapMode(); String hexOptions = Util.encodeTpmIdentityProofOptionsToHex(options); IOUtils.write(hexOptions, out); } // also save the ekcert for the identity request submit response String ekcertFilename = filename + ".ekcert"; try(FileOutputStream out = new FileOutputStream(datadir.toPath().resolve(ekcertFilename).toFile())) { IOUtils.write(ekCert.getEncoded(), out); } //encrypt the challenge and return log.debug("Phase 1 details:"); log.debug(" AIK blob: " + TpmUtils.byteArrayToHexString(idProof.getAik().toByteArray())); log.debug(" challenge: " + TpmUtils.byteArrayToHexString(identityRequestChallenge)); byte[] toReturn = createReturn(idProof.getAik(), (RSAPublicKey)ekCert.getPublicKey(), identityRequestChallenge); log.debug(" toReturn: " + TpmUtils.byteArrayToHexString(toReturn)); return toReturn; } private static byte[] createReturn(TpmPubKey aik, RSAPublicKey pubEk, byte[] challengeRaw) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, TpmUnsignedConversionException, IOException{ byte [] key = TpmUtils.createRandomBytes(16); byte [] iv = TpmUtils.createRandomBytes(16); byte [] encryptedBlob = TpmUtils.concat(iv, TpmUtils.TCGSymEncrypt(challengeRaw, key, iv)); byte [] credSize = TpmUtils.intToByteArray(encryptedBlob.length); TpmSymmetricKey symKey = new TpmSymmetricKey(); symKey.setKeyBlob(key); symKey.setAlgorithmId(TpmKeyParams.TPM_ALG_AES); symKey.setEncScheme(TpmKeyParams.TPM_ES_SYM_CBC_PKCS5PAD); TpmKeyParams keyParms = new TpmKeyParams(); keyParms.setAlgorithmId(TpmKeyParams.TPM_ALG_AES); keyParms.setEncScheme(TpmKeyParams.TPM_ES_NONE); keyParms.setSigScheme((short)0); keyParms.setSubParams(null); keyParms.setTrouSerSmode(true); byte [] asymBlob = TpmUtils.TCGAsymEncrypt(TpmUtils.concat(symKey.toByteArray(), TpmUtils.sha1hash(aik.toByteArray())), pubEk); byte [] symBlob = TpmUtils.concat(TpmUtils.concat(credSize, keyParms.toByteArray()), encryptedBlob); return TpmUtils.concat(asymBlob, symBlob); } private boolean isEkCertificateVerifiedByAuthority(X509Certificate ekCert, X509Certificate authority) { if( authority != null ) { try { ekCert.verify(authority.getPublicKey()); // throws SignatureException return true; } catch(Exception e) { log.debug("Failed to verify EC using CA {}: {}", ekCert.getIssuerDN().getName().replaceAll("\\x00", ""), e.getMessage()); } } return false; } private boolean isEkCertificateVerifiedByAnyAuthority(X509Certificate ekCert, Collection<X509Certificate> authorities) { for(X509Certificate authority : authorities) { try { ekCert.verify(authority.getPublicKey()); // throws SignatureException log.debug("Verified EC with authority: {}", authority.getSubjectX500Principal().getName()); return true; } catch(Exception e) { log.debug("Failed to verify EC with authority: {}", authority.getSubjectX500Principal().getName()); } } return false; } private boolean isEkCertificateRegistered(X509Certificate ekCert) { try(TpmEndorsementDAO dao = TpmEndorsementJdbiFactory.tpmEndorsementDAO()) { TpmEndorsement tpmEndorsement = dao.findTpmEndorsementByIssuerEqualTo(ekCert.getIssuerDN().getName().replaceAll("\\x00", "")); // SHOULD REALLY BE BY CERT SHA256 if(tpmEndorsement == null ) { return false; } log.debug("EC is registered: {}", tpmEndorsement.getId().toString()); return true; } catch(IOException e) { log.debug("Cannot check if EC is registered", e); return false; } } }