I am building a Hololens Unity App and I am trying to load in connection certification files for a server I connect to in my app. There is a .crt and a .pfx file located at the Assets/StreamingAssets folder.
On the unity editor and when running the app on unity, the files are read correctly and the connection works.
Here is the Code:
X509Certificate caCert = new X509Certificate(Application.streamingAssetsPath + "/a.crt");
UnityEngine.Debug.Log(caCert + "TRUE");
X509Certificate2 clientCert = new X509Certificate2(Application.streamingAssetsPath + "/b.pfx");
UnityEngine.Debug.Log(clientCert + "TRUE");
MqttClient client = new MqttClient(broker, 8883, true, caCert, clientCert, MqttSslProtocols.TLSv1_2, MyRemoteCertificateValidationCallback);
My problem is that when I deploy this to thew hololens, I am unable to access the .pfx file and it does not appear to be deployed onto the hololens saying that the file is not found (but the .crt file and the rest of the folder are there...).
Another issue I have:
I have also manually entered the certifications into the hololens' c: drive and they show up in its file explorer. I do not know how to access them, since that would be an easier workaround if I could just find the location of the file on the hololens drive itself and load it in from my app.
If anyone has experience with loading in files onto hololens unity-based applications and can help me with loading in the files while using the application on the hololens2, I would appreciate it very much.
Since HoloLens APP is a UWP APP, so make sure you use backslashes () as a path separator character. And the best practice is to use Path.Combine() instead of constructing paths yourself. It will take care of all the platform specific prefixes, appendices, delimiters, slashes, backslashes etc.
X509Certificate caCert = new X509Certificate(Application.streamingAssetsPath + "/a.crt");
=>
X509Certificate caCert = new X509Certificate(Path.Combine(Application.streamingAssetsPath + "a.crt"));
My app creates mails with attachments, and uses an intent with Intent.ACTION_SEND to launch a mail app.
It works with all the mail apps I tested with, except for the new Gmail 5.0 (it works with Gmail 4.9), where the mail opens without attachment, showing the error: "Permission denied for the attachment".
There are no useful messages from Gmail on logcat. I only tested Gmail 5.0 on Android KitKat, but on multiple devices.
I create the file for the attachment like this:
String fileName = "file-name_something_like_this";
FileOutputStream output = context.openFileOutput(
fileName, Context.MODE_WORLD_READABLE);
// Write data to output...
output.close();
File fileToSend = new File(context.getFilesDir(), fileName);
I'm aware of the security concerns with MODE_WORLD_READABLE.
I send the intent like this:
public static void compose(
Context context,
String address,
String subject,
String body,
File attachment) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("message/rfc822");
emailIntent.putExtra(
Intent.EXTRA_EMAIL, new String[] { address });
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, body);
emailIntent.putExtra(
Intent.EXTRA_STREAM,
Uri.fromFile(attachment));
Intent chooser = Intent.createChooser(
emailIntent,
context.getString(R.string.send_mail_chooser));
context.startActivity(chooser);
}
Is there anything I do wrong when creating the file or sending the intent? Is there a better way to start a mail app with attachment? Alternatively - has someone encountered this problem and found a workaround for it?
Thanks!
I was able to pass a screenshot .jpeg file from my app to GMail 5.0 through an Intent. The key was in this answer.
Everything I have from #natasky 's code is nearly identical but instead, I have the file's directory as
context.getExternalCacheDir();
Which "represents the external storage directory where you should save cache files" (documentation)
GMail 5.0 added some security checks to attachments it receives from an Intent. These are unrelated to unix permissions, so the fact that the file is readable doesn't matter.
When the attachment Uri is a file://, it'll only accept files from external storage, the private directory of gmail itself, or world-readable files from the private data directory of the calling app.
The problem with this security check is that it relies on gmail being able to find the caller app, which is only reliable when the caller has asked for result. In your code above, you do not ask for result and therefore gmail does not know who the caller is, and rejects your file.
Since it worked for you in 4.9 but not in 5.0, you know it's not a unix permission problem, so the reason must be the new checks.
TL;DR answer:
replace startActivity with startActivityForResult.
Or better yet, use a content provider.
Use getExternalCacheDir() with File.createTempFile.
Use the following to create a temporary file in the external cache directory:
File tempFile = File.createTempFile("fileName", ".txt", context.getExternalCacheDir());
Then copy your original file's content to tempFile,
FileWriter fw = new FileWriter(tempFile);
FileReader fr = new FileReader(Data.ERR_BAK_FILE);
int c = fr.read();
while (c != -1) {
fw.write(c);
c = fr.read();
}
fr.close();
fw.flush();
fw.close();
now put your file to intent,
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
You should implement a FileProvider, which can create Uris for your app's internal files. Other apps are granted permission to read these Uris. Then, simply instead of calling Uri.fromFile(attachment), you instantiate your FileProvider and use:
fileProvider.getUriForFile(attachment);
Google have an answer for that issue:
Store the data in your own ContentProvider, making sure that other apps have the correct permission to access your provider. The preferred mechanism for providing access is to use per-URI permissions which are temporary and only grant access to the receiving application. An easy way to create a ContentProvider like this is to use the FileProvider helper class.
Use the system MediaStore. The MediaStore is primarily aimed at video, audio and image MIME types, however beginning with Android 3.0 (API level 11) it can also store non-media types (see MediaStore.Files for more info). Files can be inserted into the MediaStore using scanFile() after which a content:// style Uri suitable for sharing is passed to the provided onScanCompleted() callback. Note that once added to the system MediaStore the content is accessible to any app on the device.
Also you can try set permissions for your file:
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
And finally you can copy/store your files in external storage - permissions not needed there.
I tested it and I found out that it was definitely private storage access problem.
When you attach some file to Gmail (over 5.0) do not use the file from private storage such as /data/data/package/. Try to use /storage/sdcard.
You can successfully attach your file.
Not sure why GMail 5.0 doesn't like certain file paths (which I've confirmed it does have read access to), but an apparently better solution is to implement your own ContentProvider class to serve the file. It's actually somewhat simple, and I found a decent example here: http://stephendnicholas.com/archives/974
Be sure to add the tag to your app manifest, and include a "android:grantUriPermissions="true"" within that. You'll also want to implement getType() and return the appropriate MIME type for the file URI, otherwise some apps wont work with this... There's an example of that in the comment section on the link.
I was having this problem and finally found an easy way to send email with attachment. Here is the code
public void SendEmail(){
try {
//saving image
String randomNameOfPic = Calendar.DAY_OF_YEAR+DateFormat.getTimeInstance().toString();
File file = new File(ActivityRecharge.this.getCacheDir(), "slip"+ randomNameOfPic+ ".jpg");
FileOutputStream fOut = new FileOutputStream(file);
myPic.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
//sending email
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"zohabali5#gmail.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Recharge Account");
intent.putExtra(Intent.EXTRA_TEXT, "body text");
//Uri uri = Uri.parse("file://" + fileAbsolutePath);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(Intent.createChooser(intent, "Send email..."),12);
}catch (Exception e){
Toast.makeText(ActivityRecharge.this,"Unable to open Email intent",Toast.LENGTH_LONG).show();
}
}
In this code "myPic" is bitmap which was returned by camera intent
Step 1: Add authority in your attached URI
Uri uri = FileProvider.getUriForFile(context, ""com.yourpackage", file);
Same as your manifest file provide name
android:authorities="com.yourpackage"
Step 2`; Add flag for allow to read
myIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
I tried in many ways to access to a HTTPS server using Windows.Web.Http.HttpClient on a portable class library for a windows phone 8.1 App
I need a portable class library and install the ssl certificate on the library whithout action of the user.
Is that possible and if yes, how to do it ?
You can load your certificate from an application file and install it:
Uri uri = new Uri("ms-appx:///Assets/tempRootCa.cer");
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
IBuffer buffer = await FileIO.ReadBufferAsync(file);
Certificate rootCert = new Certificate(buffer);
CertificateStore rootStore = CertificateStores.TrustedRootCertificationAuthorities;
rootStore.Add(rootCert);
I've read through this question and answer: "
Is it Possible to Dynamically Return an SSL Certificate in NodeJS?"... but it uses .key and .crt files for the domains and the server.
On a Windows 2008 R2 machine, I can't find the domain1.key, server.key and server.crt files. Instead I've created a domain1.pfx file by exporting the SSL certficate from IIS.
I am able to successfully run an https node.js server using this one PFX file with one domain like this:
var fs = require('fs');
var https = require('https');
var crypto = require('crypto');
function getSecureContext(domain) {
return crypto.createCredentials({
pfx: fs.readFileSync('/path/to/' + domain + '.pfx'),
passphrase: 'passphrase'
}).context
}
var secureContext = {
'domain1': getSecureContext('domain1')
}
var options = {
SNICallback: function (domain) {
return (secureContext.hasOwnProperty(domain) ? secureContext[domain] : {});
},
pfx: fs.readFileSync('/path/to/domain1.pfx'); // for the server certificate
};
var server = https.createServer(
options,
requestListener).listen(443);
However what if I have a multiple domain certificate plus another certificate for a single domain, how would the SNICallback and the getSecureContext functions be configured to have each domain name use the correct certificate?
I think the server certificate should be the same for both PFX files since they are on the same server so I'm using only the first PFX file (for domain1) as the server certificate.
I've tried changing the secureContext object like this:
var secureContext = {
'domain1': getSecureContext('domain1'),
'domain2': getSecureContext('domain2'),
.
.
}
This gives me the error "listen EACCES'.
In my specific situation I have two SSL certificates. One is an extended validation certificate for one domain name, and the second is a multiple domain certificate supporting five domain names.
I've found it very difficult to debug the EACCES error. There doesn't seem to be more detail as to what is causing the EACCES. Is my configuration wrong, is there a problem with the certificates? I do know that these certificates work correctly when I use them in IIS running an IIS server (instead of a node.js server) on the same Windows 2008 R2 server.
I would like to stay with a pure windows and node.js configuration. (Not nginx, iisnode or any other libraries if possible).
Solved it. The EACCES error was due to my not listing all the sites that need to use the two certificates. Since I was testing, I only was working with two site names, but the multi-domain certificate includes some other sites. Each site needs to be listed as below. Otherwise one or more of the sites will not have a certificate associated with it causing the EACCES error.
var secureContext = {
'domain1': getSecureContext('domain1'),
'domain2': getSecureContext('domain2'),
'domain3': getSecureContext('domain2'),
'domain4': getSecureCOntext('domain2')
}
I'm writing a server application that uses CryptoAPI and Schannel for setting up a secure SSL connection to clients. The server requires the clients to submit a certificate for verification (by setting the ASC_REQ_MUTUAL_AUTH flag in AcceptSecurityContext).
The problem I have is that some clients (namely clients using javax.net.ssl) does not pass along their client certificate (even though it's been configured to do so). I suspect this is because the CA certificate used for signing the client certificates are not in the list of CA's passed to the client during the handshake.
I've tried to do variations of the following to add the CA certificate to this list:
PCERT_CONTEXT caCertContext = ...; /* Imported from a DER formatted file */
HCERTSTORE systemStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
0,
CERT_STORE_OPEN_EXISTING_FLAG |
CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"ROOT");
bool ok = CertAddCertificateContextToStore(
systemStore,
caCertContext,
CERT_STORE_ADD_USE_EXISTING,
NULL);
if (!ok)
{
std::cerr << "Could not add certificate to system store!" << std::endl;
}
In the above example CertAddCertificateContextToStorealways fails. If I change CERT_SYSTEM_STORE_LOCAL_MACHINEto CERT_SYSTEM_STORE_CURRENT_USER I am presented with a popup asking me to confirm the certificate, but even if I accept the CA certificate will not appear in the list sent to the client.
I also tried extending the system store collection with a temporary memory store (something I picked up from here) but to no avail.
Anyone know of a way to solve this? Ideally programmatically without using any GUI or external tool?
You are getting that error because you don't have permission to access the store as read and write, you can only access it as read. So what you have to do is add CERT_STORE_READONLY_FLAG so it will be:
HCERTSTORE systemStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
0,
CERT_STORE_OPEN_EXISTING_FLAG |
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG ,
L"ROOT");
If you which to make changes to your store and not have it read only that means you will require administration elevation when you are running your C++ application.
If you don't want to add it systemwide (as you mentioned in the comment) you can open with the CERT_SYSTEM_STORE_CURRENT_USER flag.
CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_STORE_OPEN_EXISTING_FLAG |
CERT_SYSTEM_STORE_CURRENT_USER, L"MY");