Encrypting mdm profile - c#

I have seen question on signing and encrypting final mdm profile here:
iOS MDM profile signing, which certificate to use?
I am using Bouncy Castle library for encryption. Currently I am stuck while encrypting the final profile using the scep identitiy certificate.
I am facing the following issue.
The public key retrieved from with scep response certificate is not 16byte(128 bit) so encryption is failing with a message Key should be 128 bit.
If I can change the public key to 16byte using the following code the device throws invalid profile dailog.
public static string getKeyMessageDigest(string key)
{
byte[] ByteData = Encoding.UTF8.GetBytes(key);
//MD5 creating MD5 object.
MD5 oMd5 = MD5.Create();
byte[] HashData = oMd5.ComputeHash(ByteData);
//convert byte array to hex format
StringBuilder oSb = new StringBuilder();
for (int x = 0; x < HashData.Length; x++)
{
//hexadecimal string value
oSb.Append(HashData[x].ToString("x2"));
}
return Convert.ToString(oSb);
}
Can some one help me with some blog or sample code to encrypt the profile? Appreciate your help.

I had a similar problem. PFB the working code that I'm using to encrypt now. I'm retrieving the signing certificate from the device response, retrieving the public key from it and using the same to encrypt.
byte[] request = StreamToByte(ResponseFromDevice);
var signer = new SignedCms();
signer.Decode(request);
X509Certificate2 certificate = signer.Certificates[0];
string xmlData = "payload string to encrypt";
Byte[] cleartextsbyte = UTF8Encoding.UTF8.GetBytes(xmlData);
ContentInfo contentinfo = new ContentInfo(cleartextsbyte);
EnvelopedCms envelopedCms = new EnvelopedCms(contentinfo);
CmsRecipient recipient = new CmsRecipient(certificate);
envelopedCms.Encrypt(recipient);
string data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plist version=\"1.0\"><dict><key>EncryptedPayloadContent</key><data>[ENCRYPTEDDATA]</data><key>PayloadDescription</key><string>For profile enrollment</string><key>PayloadDisplayName</key><string>ProfileName</string><key>PayloadIdentifier</key><string>YourIdentifier</string><key>PayloadOrganization</key><string>YourOrg</string><key>PayloadRemovalDisallowed</key><false/><key>PayloadType</key><string>Configuration</string><key>PayloadUUID</key><string>YourUDID/string><key>PayloadVersion</key><integer>1</integer></dict></plist>";
data = data.Replace("[ENCRYPTEDDATA]", Convert.ToBase64String(envelopedCms.Encode()));
HttpContext.Current.Response.Write(data);
WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-apple-aspen-config";
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;

I answered in comments on your previous question:
"I would recommend to take a look on OS X Server MDM implementation.
Generally speaking to encrypt profile, as I remember you should use PKCS7 wrapping. So, you should look at this: http://www.cs.berkeley.edu/~jonah/bc/org/bouncycastle/jce/PKCS7SignedData.html
BTW. I would recommend to read up a little bit on cryptography, if you want to get general understanding. Very-very high level overview of your problem: you are trying to use RSA key directly to encrypt the data. However, it should be used to encrypt a symmetric key which in its turn is used to encrypt the data."
You can also take a look here:
PKCS#7 Encryption
Your code won't work, because it's
- not PKCS7
- you are trying to use MD5(public certificate key) which doesn't make any sense
I would really-really recommend to read again MDM documentation and something on cryptopraphy. It's quite easy to make it wrong (both non working or unsecure implementation).

In bouncycastle you have to encrypt it using CMSAlgorithm.DES_EDE3_CBC. Then signed the data as you done in the previous step. Make sure you Base64 encode the encrypted payload before signing.

Related

How to decrypt an OpenPGP RSA Encrypted text file in C#?

I am hoping that this question can be met with some guidance for someone who is beginning to work with encryption/decryption in C#. There are existing examples on the web regarding this, but I am truthfully struggling to put it all into practice for my given situation.
If given a text file that has been encrypted using OpenPGP with RSA, what is the best method to decrypt this in C#?
This is what I am attempting:
Using Kleopatra OpenPGP, I am generating a key pair using 2048bit RSA. This generates a private and public key.
I am then encrypting/signing a text file with a few word in it as a test.
In C#, I want to decrypt this text file.
Current code:
byte[] encryptedData = File.ReadAllBytes("C:\\PGP Encryption\\test.txt.gpg"); // The encrypted text file generated by Kleopatra.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This needs
//to include the private key information.
RSA.ImportParameters(RSAKeyInfo);
//Decrypt the passed byte array and specify OAEP padding.
decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
}
return decryptedData;
Unfortunately, the RSA.Decrypt() call throws an exception that reads "The data to be decrypted exceeds the maximum for this modulus of 128 bytes."
I also do not believe that my private key is actually being loaded, as I'm not explicitly stating where the key is. But I don't see how the RSAParameters object is supposed to get populated otherwise.
If anyone can point me in the right direction to decrypt a file in this way, thank you in advance for your time and information.
It's looks like you need this library (see Decrypt section) https://github.com/mattosaurus/PgpCore

how to use p7b and p12 certificates

Hello my goal is it to encrypt the password of a zipfile. As for now I only had to generate a SHA Hash which will be my password and used the RSA-Cryptoprovider with me private key to encrypt the password.
Now I need to use .p7b and .p12 certificates to do this task.
But I dont understand what do I need those files for after some research I figured out that .p12 will be probably the file that I use to encrypt my password but what do I need .p7b for?
That is what I did so far with it, it seems to work because I can read the string but still what do I need my .p7b file for?
var password = #"test";
var p12FilePath = #"key\Test.p12";
var text = #"myFutureZipPassword";
X509Certificate2 cert = new X509Certificate2(p12FilePath, password, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
byte[] certData = cert.Export(X509ContentType.Pfx, password);
RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)cert.PrivateKey;
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
var sign = rsaKey.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
var str = System.Text.Encoding.Default.GetString(sign);
Neither p12 - the extension for PKCS#12 key/certificate stores - nor .p7b - the extension for PKCS#7 binary Cryptographic Message Syntax - is a certificate.
PKCS#12 can be used to store multiple private key / certificate chains (required to sign or decrypt) or just trusted certificates (used to verify or encrypt). Other information can be stored as well, but for your assignment you should just use it as key / certificate store.
The PKCS#7 standard specifies the Cryptographic Message Syntax or CMS. It specifies a container format which can be used to specify signed or enveloped (read: encrypted) messages / files. Whereas RSA by itself can encrypt small files, PKCS#7 allows you to use a well specified form of hybrid encryption.
The container format indicates and may contain the certificates used to perform the encryption so you can choose which key to use. It also specifies the algorithms used to perform the encryption. To use it you need a higher level API than RSACryptoServiceProvider; it is provided in the PKCS namespace within the .NET framework (start with EnvelopedCms). PKCS stands for Public Key Cryptography Standards, which were first created by RSA Labs and then copied and continued in RFC's.
The structure of PKCS#12 and PKCS#7 have been defined in a language that defines data structures called ASN.1. The structures are then encoded using a binary encoding format called BER / DER. However, to make sure that the messages are correctly transmitted over a text based interface, the PKCS#7 file is often ASCII armored using PEM text encoding, which is just basically a few header / footer lines with base 64 encoded binary in between. The b is there in the filename to show that the PEM encoding should not be performed.
Note that sometimes the PKCS#7 / CMS is also used to simply store certificates, which could be used for encryption as you only need the public key for that. However, it is much nicer to use PKCS#12 for that.

Export RSA Public Key in DER Format and decrypt data

I need to get the bytes of my RSA Public Key in the DER format and send this key to an API. This API will then send me something encrypted using this Public Key back and i need to decrypt it.
First problem, it seems that C# doesn't have the ability to export RSA Keys in the DER Format. So it seems i have to rely on a 3rd party package like BouncyCastle. I also found a way to generate and export my Public Key in the DER Format with this package:
var generator = new RsaKeyPairGenerator();
generator.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
var keyPair = generator.GenerateKeyPair();
var publicKeyParam = (RsaKeyParameters)keyPair.Public;
var publicKey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKeyParam).GetDerEncoded();
So now i only had to Base64 encode those bytes and send it to the API and the API is happy. But now i'm stuck trying to figure out how i can decrypt the dat i received. First of all i decoded the base64 string and now i'm stuck with the bytes of the data. I can get the private key from the BouncyCastle "thing", but i cant seem to convert it to RsaCryptoServiceProvider correctly and the BouncyCastle documentation is kind of weird.
If anyone has an idea or another approach i would be more than happy.
Based on the comments i came up with this solution:
var rsa = new RSACryptoServiceProvider(2048); // my rsa to decrypt and encrypt data
var publicKeyParam = DotNetUtilities.GetRsaPublicKey(rsa.ExportParameters(false));
var publicKey = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKeyParam).GetDerEncoded(); // my exported public key in DER format
Thanks alot James K Polk!

What's the simplest way to distribute an RSA public key with software?

I'm working on some software that exchanges XML documents with a server. The server signs the XML using XMLDSIG and the client should verify the signature before trusting the XML. I'm using RSACryptoServiceProvider to do this. The XML is signed, but not encrypted.
I'm following the basic procedure explained in:
How to Sign XML Documents with Digital Signatures
How to Verify the Digital Signatures of XML Documents
This requires that the client software has the public key available. I want the distribution of the client software to be as simple as possible and I don't want the client to have to deal with certificates. The pair of documents referenced above conveniently skirt around the subject of distributing the public key, simply stating that the user "needs to have the same key". I don't particularly want the end user to even be aware that they have a public key, so asking them to mess around with certificates is out of the question. Since the public key is public, what I would like to do is somehow embed it within the client software. As I see it, my options are:
Install the public key during the setup process
Somehow embed the public key into the software itself, possibly within the App.config file
Is this feasible in practice? What is the simplest way of achieving this that doesn't require any user interaction or awareness?
You don't have to distribute the certificate. One of common approaches is to include the certificate in the signed document, in the KeyInfo/X509Data node.
The validation can use the embedded certificate easily and the only required infrastructure element at the client side is the certificate thumbprint and subject name. In other words, client validates the document using included certificate and then easily checks the certificate agaist the subject name and thumbprint. In case of a match, there is the assumption that a correct certificate has been provided.
Read more about technical details in one of my blog entries (this is a 3 part tutorial so you can also take a look at all other entries). Anyway, no importing certificates and no including certificates with your software, rather you have two string configuration parameters.
The embedded certificate inside the XmlDsigned document has a const size and usually the overhead is neglectable.
http://www.wiktorzychla.com/2012/12/interoperable-xml-digital-signatures-c.html
http://www.wiktorzychla.com/2012/12/interoperable-xml-digital-signatures-c_20.html
Am not sure what problem you're facing without seeing your code but, could this answer from Ji Zhou help?
public static void Main()
{
try
{ //initialze the byte arrays to the public key information.
byte[] PublicKey = {214,46,220,83,160,73,40,39,201,155,19,202,3,11,191,178,56,
74,90,36,248,103,18,144,170,163,145,87,54,61,34,220,222,
207,137,149,173,14,92,120,206,222,158,28,40,24,30,16,175,
108,128,35,230,118,40,121,113,125,216,130,11,24,90,48,194,
240,105,44,76,34,57,249,228,125,80,38,9,136,29,117,207,139,
168,181,85,137,126,10,126,242,120,247,121,8,100,12,201,171,
38,226,193,180,190,117,177,87,143,242,213,11,44,180,113,93,
106,99,179,68,175,211,164,116,64,148,226,254,172,147};
//Values to store encrypted symmetric keys.
byte[] EncryptedSymmetricKey;
byte[] EncryptedSymmetricIV;
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Get an instance of RSAParameters from ExportParameters function.
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = PublicKey;
//Import key parameters into RSA.
RSA.ImportParameters(RSAKeyInfo);
//Create a new instance of the RijndaelManaged class.
RijndaelManaged RM = new RijndaelManaged();
//Encrypt the symmetric key and IV.
EncryptedSymmetricKey = RSA.Encrypt(RM.Key, false);
EncryptedSymmetricIV = RSA.Encrypt(RM.IV, false);
Console.WriteLine("RijndaelManaged Key and IV have been encrypted with RSACryptoServiceProvider.");
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
}
}

C# RSA Encrypt Private Decrypt Public?

Here is the problem I am trying to solve. Let's say I was releasing some web software built on ASP.NET MVC and I only wanted the end user to be able to add X users to the system. The software would be hosted on their servers.
I want to include an encrypted file that when the user tries to add a new user, it goes out and reads from the file an encrypted string. When the website decodes it, the clear text will be the number of allowed users.
What is the best/simplest way on my end to encrypt to generate this string on my end then decode it back to clear text in my application? Obviously I want to ensure that the end user cannot be spinning up their own encrypted string and just replace mine. I don't want to worry about having to try and obfuscate my source so that they would not be able to see how I decode the string.
Is it possible to encrypt with a private rsa key, then decrypt it with the public one? I haven't had luck with that in the code below:
var rsa = new RSACryptoServiceProvider();
var pubicKey = rsa.ToXmlString(false);
var privateKey = rsa.ToXmlString(true);
var test = "this string needs to be encrypted then decrypted";
var rsa2 = new RSACryptoServiceProvider();
rsa2.FromXmlString(privateKey);
var encryptedBytes = rsa2.Encrypt(Encoding.UTF8.GetBytes(test), false);
var encryptedString = Convert.ToBase64String(encryptedBytes);
var rsa3 = new RSACryptoServiceProvider();
rsa3.FromXmlString(pubicKey);
encryptedBytes = Convert.FromBase64String(encryptedString);
var decryptedString = Encoding.UTF8.GetString(rsa3.Decrypt(encryptedBytes, false));
You can use a signature strategy, where the private key is used to generate a signature that verifies that your message is authentic.
// Create message and signature on your end
string message = "Here is the license message";
var converter = new ASCIIEncoding();
byte[] plainText = converter.GetBytes(secret);
var rsaWrite = new RSACryptoServiceProvider();
var privateParams = rsaWrite.ExportParameters(true);
// Generate the public key / these can be sent to the user.
var publicParams = rsaWrite.ExportParameters(false);
byte[] signature =
rsaWrite.SignData(plainText, new SHA1CryptoServiceProvider());
// Verify from the user's side. Note that only the public parameters
// are needed.
var rsaRead = new RSACryptoServiceProvider();
rsaRead.ImportParameters(publicParams);
if (rsaRead.VerifyData(plainText,
new SHA1CryptoServiceProvider(),
signature))
{
Console.WriteLine("Verified!");
}
else
{
Console.WriteLine("NOT verified!");
}
This example was largely copied from Microsoft's site:
RSACryptoServiceProvider.SignData Method (Byte[], Object)
And here is web page that explains the concept:
Using RSA for Signing Messages
I think what you are looking for is digital signature. It doesn't matter if the content is encrypted or not, since the user has the (public) key to decrypt it. All that matters is if the content's source is you.
Since you have a config file I reckon it is XML, so you are looking for XMLDSIG.
You can easily achieve this using the SignedXml class in .Net. Then all you need to do is verify the signature when loading the config file. This method allows you to eaisly use any X509 certificates you may have. You can even embed the public key in the signed file, so the user does not need to install your cert (public key).
Your idea is correct, but I wonder about unforeseen consequences.
You state they will be running software on their servers, so this means they are hosting a service for themselves. But you also mention this service has to connect out tot he internet to add a user by validating with your server. What happens when the internet goes down or they want to have a secure system and a firewall blocks internet access to the servers? Will they completely lose their ability to function?
Just giving you a question to ask yourself :p
You don't use a public key to 'decrypt' the file. you can only decrypt the file with the private key.
In your case you could store the number of users as well a signature of the data which you create with your private key.
On the clients sever, you use your public key to verify the signature matches the data in the file (number of users)
However, it is possible that an advanced user could swap out your public key with their own and sign the file themselves.
As stated in your comment in the question, your approach is using a key from a client and a key from yourself. This will not work, as the prime numbers used in RSA are meant for use with only the corresponding private/public key.
What you need to do use your two keys and nothing from the client or the client's two keys and nothing of yours. For example,
You could sign it using your two keys by encrypting with your private key and allowing the client to decrypt using your public key.
You could encrypt it using your client's public key and have them decrypt is using their (the client's) private key.
Hope this helps!

Categories

Resources