I'm working on project that involves reading public key data used to sign Android APKs. I am able to successfully extract the signatures as public keys. When I look inside the binary pubkey files produced by this, I see some plain-text such as a name and a city.
How can I safely extract this name/city information embedded inside the public key using PHP (or even Java or C#)? And hopefully do it in such a way that I know exactly what these fields are (i.e. not blindly grabbing text, but knowing which string is a city and which is a name)
For clarification: I don't have the private key or a certificate file. I'm currently not interested in signing or encrypting anything, I would just like to extract the plaintext inside the pubkey without using kludgy approaches like regex.
Update: Here's a sample (base64-encoded) public key from one of my APKs
MIICBzCCAXCgAwIBAgIES6KlazANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQGEwJVUzELMAkGA1UECBMCUkkxFTATBgNVBAcTDE5hcnJhZ2Fuc2V0dDEVMBMGA1UEAxMMQ29saW4gTydEZWxsMB4XDTEwMDMxODIyMTI1OVoXDTQ1MDMwOTIyMTI1OVowSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlJJMRUwEwYDVQQHEwxOYXJyYWdhbnNldHQxFTATBgNVBAMTDENvbGluIE8nRGVsbDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmPetcBW+ITURXY0LsI2ZfgM3R7K2kwicgpd0W+BYAXQBh76SXyN9MYvtfnUY3SNz37FW/lDQgAO3pbhEFqGwfADh2ctXlYmlE9DtcRQw0ojGVPIDlWBX+9IUxyL/89CPaN84R/1lvdosco4V0BqQYR300S9ZwmwFA2Vh9hSUZmsCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBezKu4G11Z68NTPIBro8xsnbkdYxObzW7BsSr6t9MS5x6EQVs75R/nnKrsMcQ+9ImdT940jhQgZT3ZrYla5VhdbelxnLhBVbJfBdipV3Hv2bG7MnXzFqHYwQqYp+UrP8zWm1YHQf5I/P9VBjlkgwFyNKr0TxP4t/qS08oGX2wvZg==
The string you put in is a base 64 encoded x509 certificate, not simply a public key.
You'll need to parse the Distinguished Name fields to get the desired info.
Here's a C# example:
using System;
using System.Security.Cryptography.X509Certificates;
namespace Sample
{
class Program
{
static void Main(string[] args)
{
string base64EncodedX509 =
"MIICBzCCAXCgAwIBAgIES6KlazANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQGEwJVUzELMAkGA1UECBMCUkkxFTATBgNVBAcTDE5hcnJhZ2Fuc2V0dDEVMBMGA1UEAxMMQ29saW4gTydEZWxsMB4XDTEwMDMxODIyMTI1OVoXDTQ1MDMwOTIyMTI1OVowSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlJJMRUwEwYDVQQHEwxOYXJyYWdhbnNldHQxFTATBgNVBAMTDENvbGluIE8nRGVsbDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmPetcBW+ITURXY0LsI2ZfgM3R7K2kwicgpd0W+BYAXQBh76SXyN9MYvtfnUY3SNz37FW/lDQgAO3pbhEFqGwfADh2ctXlYmlE9DtcRQw0ojGVPIDlWBX+9IUxyL/89CPaN84R/1lvdosco4V0BqQYR300S9ZwmwFA2Vh9hSUZmsCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBezKu4G11Z68NTPIBro8xsnbkdYxObzW7BsSr6t9MS5x6EQVs75R/nnKrsMcQ+9ImdT940jhQgZT3ZrYla5VhdbelxnLhBVbJfBdipV3Hv2bG7MnXzFqHYwQqYp+UrP8zWm1YHQf5I/P9VBjlkgwFyNKr0TxP4t/qS08oGX2wvZg==";
var rawBytes = Convert.FromBase64String(base64EncodedX509);
X509Certificate cert = new X509Certificate(rawBytes);
// Parse the distinguished name to get your desired fields
Console.WriteLine(cert.Subject); // writes CN=Colin O'Dell, L=Narragansett, S=RI, C=US
Console.WriteLine(cert.Issuer); // writes CN=Colin O'Dell, L=Narragansett, S=RI, C=US
}
}
}
The "binary pubkey files produced by this" is an X.509 certificate.
Just about any platform has support for reading X.509 certificates, and creating a structure from them, from which you can reliably extract the "subject name," and often extended information that includes an email address or host name.
For example, if you have OpenSSL installed, use the following command:
openssl x509 -text -noout -inform der -in <yourfilehere>
You can extract specific fields with additional options. For example, adding -subject yields:
subject= /C=US/ST=RI/L=Narragansett/CN=Colin O'Dell
In php, I am not sure but maybe this function could be your friend. There is a whole section of ssl related functions that could come in handy if you are playing with certificates.
Related
I have iText7 functions which I am using,
right now, I am trying to encrypt my PDF file using a certificate in .pfx format with password.
The thing is, the function cannot read .pfx because it does not provide the password as shown below
using System.IO;
using iText.Kernel.Pdf;
using Org.BouncyCastle.X509;
namespace iText.Samples.Sandbox.Security
{
public class EncryptWithCertificate
{
public static readonly String DEST = "results/sandbox/security/encrypt_with_certificate.pdf";
public static readonly String SRC = "../../../resources/pdfs/hello.pdf";
public static readonly String PUBLIC = "../../../resources/encryption/test.cer";
public static void Main(String[] args)
{
FileInfo file = new FileInfo(DEST);
file.Directory.Create();
new EncryptWithCertificate().ManipulatePdf(DEST);
}
public X509Certificate GetPublicCertificate(String path)
{
using (FileStream stream = File.Open(path, FileMode.Open))
{
X509CertificateParser parser = new X509CertificateParser();
X509Certificate readCertificate = parser.ReadCertificate(stream);
return readCertificate;
}
}
protected void ManipulatePdf(String dest)
{
// The file created by this example can not be opened, unless
// you import the private key stored in test.p12 in your certificate store.
// The password for the p12 file is kspass.
X509Certificate cert = GetPublicCertificate(PUBLIC);
PdfDocument document = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest,
new WriterProperties().SetPublicKeyEncryption(
new[] {cert},
new[] {EncryptionConstants.ALLOW_PRINTING},
EncryptionConstants.ENCRYPTION_AES_256)));
document.Close();
}
}
}
If i try to load a normal .cer file, it goes through normally for GetPublicCertificate. No issue there. But I am trying to encrypt it with .pfx file as adobe acrobat can only register Digital ID using .p12/.pkf format and the function does throws error.
The error
Org.BouncyCastle.Security.Certificates.CertificateException
HResult=0x80131500
Message=Failed to read certificate
Inner Exception 1:
ArgumentException: Unknown object in GetInstance: Org.BouncyCastle.Asn1.DerInteger
Parameter name: obj
I am hoping to encrypt the pdf using cert as the cert can be set to expire anytime according to what I set it to be and user can only view the PDF file based on the cert expiry.
Thanks in advance.
As per your original post, your intention is:
I am hoping to encrypt the pdf using cert as the cert can be set to expire anytime according to what I set it to be and user can only view the PDF file based on the cert expiry.
Basically, you want to grant a time-limited access to PDF to authorized user. The solution you try to build in code sample doesn't solve the problem. Certificate validity for data encryption is irrelevant, because certificate validity is not checked during decryption. In fact, even certificate is not necessary, it is sufficient to have just a private key to decrypt the data. In other words, certificate-based encryption is equal to password-based encryption. What certificate adds -- an easier way to locate decryption key (secret), nothing else.
In addition, once data is decrypted, a client can save data in an unencrypted form thus your restrictions are useless. Even if you try to put time constraints within JavaScript or whatever else locally (and JavaScript is executed only locally), it isn't a solution. As long as client can manipulate date/time on a device, client always can set desired date/time to violate your restrictions.
Your problem cannot be solved without inventing a 3rd party entity that will make decisions whether the requested operation is allowed, apply necessary restrictions and minimize chances that the data will leak in an unencrypted form (only minimize, not prevent). Such functionality is implemented in Digital Rights Management (DRM) or Rights Management Service (RMS) and you need to build your solution around these tools, not attempt to integrate them in your solution. There are plenty of vendors that offer DRM/RMS solutions you can look into and utilize their functionality to build the solution for your requirements.
I'm having difficulty loading an X509Certificate2 from XML, using the FromXmlString method. The exception I'm getting is m_safeCertContext is an invalid handle.
System.Security.Cryptography.CryptographicException occurred
HResult=-2146233296
Message=m_safeCertContext is an invalid handle.
Source=System
StackTrace:
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_HasPrivateKey()
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
...
To create the XML, I'm loading the .pfx file and using ToXmlString;
var certificate = new X509Certificate2(
#"D:\public_privatekey.pfx",
(string)null,
X509KeyStorageFlags.Exportable
);
var exportedPrivate = certificate.PrivateKey.ToXmlString(true);
This generate XML which starts like this...
<RSAKeyValue><Modulus>y0iuejYHYajI...
To recreate the certificate, I'm using...
var certificate = new X509Certificate2();
certificate.PrivateKey.FromXmlString(xml);
Where xml is a string containing the XML content.
The exception is thrown on the FromXmlString call.
I'm new to using certificates, but my best guess is that the .pfx contains both public and private keys, and possibly some other important data, and that I need all of that in order to have a valid X509 certificate.
However I couldn't find ToXmlString and FromXmlString on the X509Certificate2 directly. How should I do this? Thanks for any advice.
An X.509 certificate is described in a structured binary format called ASN.1/DER encoding. ASN.1 is a language to describe the contents of the certificate and DER is the encoding of the contents that comply with that ASN.1 structure.
Encoding your in-memory certificate separately from the private key can be done using the Export method using the content type X509ContentType.Cert. You can also export the certificate and private key back into a "pfx" by specifying Pfx or Pkcs12. If you require XML then you can encode the byte array result using base 64. You can then store it into an XML CDATA element.
Usually a private key is also stored in a binary PKCS#8 container format, also defined using ASN.1 / DER. Microsoft has however chosen to store the key into a Microsoft-proprietary XML format by default.
I have DER encoded RSA keypair created in Crypto++, as well as cipher. They are Base64Encoded string. I first decode the data from Base64 to byte array, but I am not sure how to load them into RSACryptoServiceProvider.
static void Main()
{
string pbkeystr = "mypublickey";
string pvkeystr = "myprivatekey";
string cipherstr = "mycipher";
byte[] pbkey = Convert.FromBase64String(pbkeystr);
byte[] pvkey = Convert.FromBase64String(pvkeystr);
byte[] cipher = Convert.FromBase64String(cipherstr);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
//Set keys here..
//Decrypt the cipher using private key
rsa.Decrypt(pvkey, false);
}
There are no functions to set keys. The only thing I found was ImportParameters method, which takes RSAParameters class which consists of p, q, n, modulus, exponent etc. I don't have access to these.
Is there any way I can load the keys as string? How can I load the key into RSACryptoServiceProvider?
Is there any way I can load the keys as string? How can I load the key into RSACryptoServiceProvider?
From your other Crypto++ question, How to load Base64 RSA keys in Crypto++, it looks like you have only the public and private keys because you used DEREncode and BERDecode. That is, you have the RSA parameters, and not the subject public key info and the private key info. Your keys lack the OID identifiers and version numbers. Things are fine that way.
From Cryptographic Interoperability: Keys on the Code Project, you will need a C# class that parses the ASN.1/DER after you Base64 decode it. The CodeProject article provides a C# class called AsnKeyParser to read the ASN.1/DER and returns a RSAParameters to load into a CSP.
The code for the AsnKeyParser class is about 800 lines, and there are five other supporting files to make it all happen, so its not really appropriate to place it here. You should download it yourself. The file of interest is called CSInteropKeys.zip.
Once you wire-in the AsnKeyParser class, it will be as simple as the following for a RSA Public key. The private key will be similar, and the code is given on the CodeProject site.
// Your ASN.1/DER parser class
AsnKeyParser keyParser = new AsnKeyParser("rsa-public.der");
RSAParameters publicKey = keyParser.ParseRSAPublicKey();
// .Net class
CspParameters csp = new CspParameters;
csp.KeyContainerName = "RSA Test (OK to Delete)";
csp.ProviderType = PROV_RSA_FULL; // 1
csp.KeyNumber = AT_KEYEXCHANGE; // 1
// .Net class
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
rsa.PersistKeyInCsp = false;
rsa.ImportParameters(publicKey);
Linking to files on another site is frowned upon, but I don't know how to provide the information otherwise. There's too much source code involved to place in an answer.
For completeness, .Net does not make interop easy. They do not accept ASN.1/DER or PEM. Rather, .Net accepts some XML representation of the keys. I believe you can find it in RFC 3275, XML-Signature Syntax and Processing. Microsoft does not state that for you. I kind of pieced it together when I wrote the Code Project article.
Maybe we should add a class to Crypto++ to regurgitate XML in addition to ASN.1/DER and PEM.
I want to create .pem file for the public key generated by this method
public static Tuple<string, string> CreateKeyPair()
{
CspParameters cspParams =
new CspParameters {
ProviderType = 1 /* PROV_RSA_FULL */
};
RSACryptoServiceProvider rsaProvider =
new RSACryptoServiceProvider(1024, cspParams);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
return new Tuple<string, string>(privateKey, publicKey);
}
Because I was generating this key for mobile application and they can not read it, they requested .pem file instead of public key as string
Please advice,
Recently I need to save PublicKey and PrivateKey generated in my C# application to file, and works with it later. I use for this purpose such library as CSharp-easy-RSA-PEM.
It is very simple and quick solution, so I will recommend this library to other guys.
I use following code to get PublicKey as string (and save it to pem file in format Base64):
string publicKeyStr = Crypto.ExportPublicKeyToX509PEM(_cryptoServiceProvider);
it returns something like this:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxnBvS8cdsnAev2sRDRYWxznm1
QxZzaypfNXLvK7CDGk8TR7K+Pzsa+tpJfoyN/Z4B6xdlpsERo2Cu6AzolvrDLx5w
ZoI0kgdfaBMbUkdOB1m97zFYjKWoPeTskFzWZ3GHcQ3EXT0NJXXFXAskY45vEpbc
5qFgEhcPy3BMqHRibwIDAQAB
-----END PUBLIC KEY-----
And I use following code to get PrivateKey as string:
string privateKeyStr = Crypto.ExportPrivateKeyToRSAPEM(_cryptoServiceProvider);
it returns something like this:
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCxnBvS8cdsnAev2sRDRYWxznm1QxZzaypfNXLvK7CDGk8TR7K+
Pzsa+tpJfoyN/Z4B6xdlpsERo2Cu6AzolvrDLx5wZoI0kgdfaBMbUkdOB1m97zFY
jKWoPeTskFzWZ3GHcQ3EXT0NJXXFXAskY45vEpbc5qFgEhcPy3BMqHRibwIDAQAB
AoGAAdwpqm7fxh0S3jOYpJULeQ45gL11dGX7Pp4CWHYzq1vQ14SDtFxYfnLWwGLz
499zvSoSHP1pvjPgz6lxy9Rw8dUxCgvh8VQydMQzaug2XD1tkmtcSWInwFKBAfQ7
rceleyD0aK8JHJiuzM1p+yIJ/ImGK0Zk2U/svqrdJrNR4EkCQQDo3d5iWcjd3OLD
38k1GALEuN17KNpJqLvJcIEJl0pcHtOiNnyy2MR/XUghDpuxwhrhudB/TvX4tuI0
MUeVo5fjAkEAw0D6m9jkwE5uuEYN/l/84rbQ79p2I7r5Sk6zbMyBOvgl6CDlJyxY
434DDm6XW7c55ALrnlratEW5HPiPxuHZBQJANnE4vtGy7nvn4Fd/mRQmAYwe695f
On1iefP9lxpx3huu6uvGN6IKPqS2alQZ/nMdCc0Be+IgC6fmNsGWtNtsdQJAJvB4
ikgxJqD9t8ZQ2CAwgM5Q0OTSlsGdIdKcOeB3DVmbxbV5vdw8RfJFjcVEbkgWRYDH
mKcp4rXc+wgfNFyqOQJATZ1I5ER8AZAn5JMMH9zK+6oFvhLUgKyWO18W+dbcFrBd
AzlTB+HHYEIyTmaDtXWAwgBvJNIHk4BbM1meCH4QnA==
-----END RSA PRIVATE KEY-----
Then you can use
RSACryptoServiceProvider publicX509key = Crypto.DecodeX509PublicKey(publicKeyStr);
RSACryptoServiceProvider privateRSAkey = Crypto.DecodeRsaPrivateKey(privateKeyStr);
to restore saved keys back to RSACryptoServiceProvider.
So, if someone need to resolve similar issue, you can just download this library, go to Solution Explorer -> (Right click on your project) -> Add -> Reference -> Overview in your Visual Studio to add this library in your project, and add using CSharp_easy_RSA_PEM; where you need it :)
First off, a so-called .pem file is not really a fixed specification or format. Several different kinds of distinct file formats are generally described as "PEM" files. When the SSLeay (now OpenSSL) project needed to produce a base64 encoded output file containing key information they borrowed formatting concepts from the old Privacy-Enhanced Mail RFCs 1421-1424 and they added the extension .pem to the end of these files. But such a file may contain public keys, private keys, certificate requests, certificates, certificate lists, and so on. Each is different. So if all you're told is to produce a .pem file you're going to have to guess what's really needed.
The easiest way to write such files is to use the Bouncycastle C# library. The package Org.BouncyCastle.OpenSsl contains a number of utilities including a PemWriter class that should help you.
I want to create 2 really simple dlls:
1) that will sign an xml document
2) that will check that the xml document hasnt been modified.
I tried using the RSACryptoServiceProvider and a key container. But when I move to a different machine this does not work as the key is being stored in the machine.
I want to store the key in the dlls im creating (I know this is not reccomended) but I just cannot work out how to write some code to simply sign an xml document and then verify that it hasn't been changed.
So do I need to use symmetric key to do what I want is this possible?
Pete
You already mention the problems with storing the private key in the dll, so I won't repeat that.
Do this:
On your own machine run this code:
var key = new RSACryptoServiceProvider(2048);
string publicKey = key.ToXmlString(false);
string privateKey = key.ToXmlString(true);
Console.WriteLine(publicKey);
Console.WriteLine(privateKey);
this outputs two (long) lines. Copy those into your code:
Sign:
var privateKey = new RSACryptoServiceProvider();
privateKey.FromXmlString(/* insert the private-key XML string here */ );
privateKey.SignData(/*...*/);
Verify:
var publicKey = new RSACryptoServiceProvider();
publicKey.FromXmlString(/* insert the public-key XML string here */ );
publicKey.VerifyData(/*...*/);
If it is just about to verify that your xml document hasn't been modified a simple MD5 checksum (or any other good hashing algorithm) would be easier to implement and is what you need. It would be also verifyable on different machines.