C
C#3y ago
BrunORC

❔ X509 Certificate from base64 string

I am trying to transform a base64 string I receive from a previous webrequest into a x509 certificate btu all I get is a
CryptographicException: Unable to decode certificate.
CryptographicException: Unable to decode certificate.
. The public key I got in base64 is this one:
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALOexE7WXZM/aq8S0umcDGptu7BHSF5KNq/O5PA1jP54HzNQuSDjyd/3Fp3SOWUr6RC8nQKfYqPRXkOVce73h50CAwEAAQ==
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALOexE7WXZM/aq8S0umcDGptu7BHSF5KNq/O5PA1jP54HzNQuSDjyd/3Fp3SOWUr6RC8nQKfYqPRXkOVce73h50CAwEAAQ==
and I intend to later use it in an RSACryptoServiceProvider to encrypt some data. I have the same code in Java working properly and can't understand what might be wrong in the C# version. Java code from public key string into X509 cert.
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString.getBytes()));
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString.getBytes()));
C# version 1 from public key string into X509 cert with error:
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
X509Certificate2 cert = new X509Certificate2(publicKeyBytes);
return cert;
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
X509Certificate2 cert = new X509Certificate2(publicKeyBytes);
return cert;
C# version 2 from public key string into X509 cert with error:
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
X509Certificate2 cert = null;
string certFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
try {
File.WriteAllBytes(certFile, publicKeyBytes);
string certText = File.ReadAllText(certFile);
cert = new X509Certificate2(certFile);
} finally {
File.Delete(certFile);
}
return cert;
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
X509Certificate2 cert = null;
string certFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
try {
File.WriteAllBytes(certFile, publicKeyBytes);
string certText = File.ReadAllText(certFile);
cert = new X509Certificate2(certFile);
} finally {
File.Delete(certFile);
}
return cert;
I have tried other options, all of which failed so far.
32 Replies
mtreit
mtreit3y ago
I think you are misunderstanding something. An X509Certificate is a full certificate, not just the public key.
BrunORC
BrunORCOP3y ago
I tried it earlier using the key as a public key for the RSACryptoServiceProvider but it ended up creating larger encrypted files as the Java counterpart
public static RSACryptoServiceProvider ImportPublicKey(string base64publicKey)
{
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
RSAParameters rsaParams = csp.ExportParameters(false);
rsaParams.Modulus = publicKeyBytes;
csp.ImportParameters(rsaParams);
return csp;
}
public static RSACryptoServiceProvider ImportPublicKey(string base64publicKey)
{
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
RSAParameters rsaParams = csp.ExportParameters(false);
rsaParams.Modulus = publicKeyBytes;
csp.ImportParameters(rsaParams);
return csp;
}
It did encrypt what I wanted but not in the way it was required by the server the size of the created encrypted base64 string for something like [email protected] was double the size the Java code would produce
BrunORC
BrunORCOP3y ago
Stack Overflow
Read RSA Public Key from x509 Certificate Bytes in C#
In C#, I'm retrieving an RSA public key from a HTTP request and it gives me the key encoded in base64. WebClient webClient = new WebClient(); string rsaPublicKeyBase64 = webClient.DownloadString("...
BrunORC
BrunORCOP3y ago
but when I tried using
string certFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
File.WriteAllBytes(certFile, certBytes);
X509Store store = new X509Store(StoreLocation.CurrentUser);
try {
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates;
return certCollection[0];
} finally {
store.Close();
}
string certFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
File.WriteAllBytes(certFile, certBytes);
X509Store store = new X509Store(StoreLocation.CurrentUser);
try {
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates;
return certCollection[0];
} finally {
store.Close();
}
it would get a null reference as the store couldn't find the certificate file previously saved (probably because I only have the public key data as well).
mtreit
mtreit3y ago
If you never saved the certificate to the certificate store then naturally it won't find it. You can load directly from a file on disk however. If you have the actual .pfx or .cer file
BrunORC
BrunORCOP3y ago
I'm trying to create the cert file and I was just following the answer from stackoverflow where they save the certFile and it magically loads into the store god knows where from (not depicted in the answer) and I can't find how to save it if I can't create the cert from the data I have (the base64 string I receive). I don't have the cer file, that's why the example I ended up following was this simpler one:
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
X509Certificate2 cert = new X509Certificate2(publicKeyBytes);
return cert;
byte[] publicKeyBytes = Convert.FromBase64String(base64publicKey);
X509Certificate2 cert = new X509Certificate2(publicKeyBytes);
return cert;
All I need is to get that public key into the RSACryptoServiceProvider, but somehow it also needs to know it is from a x509 cert? I really am lost here.. All I have is the working Java code to follow
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(authorizationCodeResponse.getPublicKey().getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, keyFactory.generatePublic(keySpec));
byte[] userBytes = encryptCipher.doFinal(user.getBytes(StandardCharsets.UTF_8));
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(authorizationCodeResponse.getPublicKey().getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, keyFactory.generatePublic(keySpec));
byte[] userBytes = encryptCipher.doFinal(user.getBytes(StandardCharsets.UTF_8));
mtreit
mtreit3y ago
I was not aware you can create an X509Certificate2 instance from just a public key.
BrunORC
BrunORCOP3y ago
I'm still not sure it is possible as I can't understand how to translate the java thing into c#
Cisien
Cisien3y ago
I don't think you can an X509Certificate holds the cert metadata and references the keys, public and (optionally) private that java code looks to only be dealing with a key pair
BrunORC
BrunORCOP3y ago
But as I have the public key I could use it directly, no? Why would this produce a different answer than what is expected?
Cisien
Cisien3y ago
it's reading the public key from the cert used in the response and using that to get something to encrypt
BrunORC
BrunORCOP3y ago
the authorizationCodeResponse contains the key and other unrelated data like userInfo and such
Cisien
Cisien3y ago
yeah, you can use it directly
BrunORC
BrunORCOP3y ago
I tried using it directly but it ends up creating a different sized RSA encrypted string with double the expected size so the server refuses it saying it is to big
Cisien
Cisien3y ago
it might be encoding it as unicode instead of utf-8 strings by default in .net are wchar/utf-16/unicode/whatever other term is used to described 16 byte characters) it looks like that java code is producing something encoded with utf-8
BrunORC
BrunORCOP3y ago
c#
using (RSACryptoServiceProvider RSA = ImportPublicKey(authResponse.PublicKey))
{
byte[] userBytes = RSA.Encrypt(Encoding.UTF8.GetBytes(user), false);
using (RSACryptoServiceProvider RSA = ImportPublicKey(authResponse.PublicKey))
{
byte[] userBytes = RSA.Encrypt(Encoding.UTF8.GetBytes(user), false);
byte[] userBytes = encryptCipher.doFinal(user.getBytes(StandardCharsets.UTF_8));
byte[] userBytes = encryptCipher.doFinal(user.getBytes(StandardCharsets.UTF_8));
you mean before encrypting or after?
Cisien
Cisien3y ago
yeah looks like before?
BrunORC
BrunORCOP3y ago
because they are both set to UTF 8 befure encrypting but your guess sounds about right as it was double the size
Cisien
Cisien3y ago
you'd have to examine the raw output between the two it could be that the server is complaining because it was encoded incorrectly in the request some things like to base64 a byte array when building a json request
BrunORC
BrunORCOP3y ago
yeah, the userBytes is later encoded using Base64 in Java it woudl go like this:
Base64.getEncoder().encodeToString(userBytes)
Base64.getEncoder().encodeToString(userBytes)
which probably uses the default encoding of the platform in C# it probably also uses a default encoder but they may differ
string identifier = Convert.ToBase64String(userBytes)
string identifier = Convert.ToBase64String(userBytes)
will try finding if there are different encoding options in the Convert.ToBase64String method
Cisien
Cisien3y ago
yeah encoding won't come into play at that point base64 is base64 as long as the server expects it to be base64 encoded, there shouldn't be a problem next step is to compare the output of Encoding.UTF8.GetBytes(user) and user.getBytes(StandardCharsets.UTF_8) at the byte level
BrunORC
BrunORCOP3y ago
yeah, will request the Java dude to hand me the input public key + user info and output of the java one for me to try and replicate in c# and compare byte data then.. thanks mate
Cisien
Cisien3y ago
you might also look into using bouncy castle for .net, it's popular in the java world, so there may be significant overlap
BrunORC
BrunORCOP3y ago
I'm using it already somewhere else in this project, you're telling me to use it for encryption using the public key?
Cisien
Cisien3y ago
it's something to try especially if it's used by the java side as well
BrunORC
BrunORCOP3y ago
will do it then. Thanks again binary data shows that the c# one has more than 64 bytes of data while java ones always have exactly 64 bytes and 90 chars using bouncy castle did the trick thanks Cisien still can't wrap my head around why microsoft's encryption would not work now how do I close this post?
Vinicius
Vinicius2y ago
bro I know its a quiet time ago but are u still there? Im dealing with it rn, need some help
BrunORC
BrunORCOP2y ago
Hey man, I am here but probably won’t remember much about what I ended up doing with this certificate thing. I believe I switched the lib I was using to decode it What is it that you need some help with?
Vinicius
Vinicius2y ago
I havent the access from pfx directory file and on google is all about it, retrieve file from path, but I cant do it using C#, its only on frontend and send to my api my first doubt is: what format the pfx should come? FromBase64String | File ... ?
BrunORC
BrunORCOP2y ago
I will open this project up euther later today or tomorrow and sse if I’ve got something to help you with. I can’t for nothing remeber it properly but I’m sure it is there
Vinicius
Vinicius2y ago
I appreciate I still keep looking for something I think I did
using (var ms = new MemoryStream())
{
command.File.CopyTo(ms);
var fileBytes = ms.ToArray();
var certificate = new X509Certificate2(fileBytes, command.Password, X509KeyStorageFlags.MachineKeySet);
}
using (var ms = new MemoryStream())
{
command.File.CopyTo(ms);
var fileBytes = ms.ToArray();
var certificate = new X509Certificate2(fileBytes, command.Password, X509KeyStorageFlags.MachineKeySet);
}
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server