❔ X509 Certificate from base64 string

BBrunORC10/27/2022
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.
. The public key I got in base64 is this one:
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()));


C# version 1 from public key string into X509 cert with error:
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;


I have tried other options, all of which failed so far.
Mmtreit10/27/2022
I think you are misunderstanding something. An X509Certificate is a full certificate, not just the public key.
BBrunORC10/27/2022
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
BBrunORC10/27/2022
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;
}
BBrunORC10/27/2022
It did encrypt what I wanted but not in the way it was required by the server
BBrunORC10/27/2022
the size of the created encrypted base64 string for something like text@email.com was double the size the Java code would produce
BBrunORC10/27/2022
BBrunORC10/27/2022
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();
}

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).
Mmtreit10/27/2022
If you never saved the certificate to the certificate store then naturally it won't find it.
Mmtreit10/27/2022
You can load directly from a file on disk however.
Mmtreit10/27/2022
If you have the actual .pfx or .cer file
BBrunORC10/27/2022
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).
BBrunORC10/27/2022
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;
BBrunORC10/27/2022
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..
BBrunORC10/27/2022
All I have is the working Java code to follow
BBrunORC10/27/2022
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));
Mmtreit10/27/2022
I was not aware you can create an X509Certificate2 instance from just a public key.
BBrunORC10/27/2022
I'm still not sure it is possible as I can't understand how to translate the java thing into c#
CCisien10/27/2022
I don't think you can
CCisien10/27/2022
an X509Certificate holds the cert metadata
CCisien10/27/2022
and references the keys, public and (optionally) private
CCisien10/27/2022
that java code looks to only be dealing with a key pair
BBrunORC10/27/2022
But as I have the public key I could use it directly, no? Why would this produce a different answer than what is expected?
CCisien10/27/2022
it's reading the public key from the cert used in the response
CCisien10/27/2022
and using that to get something to encrypt
BBrunORC10/27/2022
the authorizationCodeResponse contains the key and other unrelated data
BBrunORC10/27/2022
like userInfo and such
CCisien10/27/2022
yeah, you can use it directly
BBrunORC10/27/2022
I tried using it directly but it ends up creating a different sized RSA encrypted string
BBrunORC10/27/2022
with double the expected size
BBrunORC10/27/2022
so the server refuses it saying it is to big
CCisien10/27/2022
it might be encoding it as unicode instead of utf-8
CCisien10/27/2022
strings by default in .net are wchar/utf-16/unicode/whatever other term is used to described 16 byte characters)
CCisien10/27/2022
it looks like that java code is producing something encoded with utf-8
BBrunORC10/27/2022
c#
using (RSACryptoServiceProvider RSA = ImportPublicKey(authResponse.PublicKey))
{
    byte[] userBytes = RSA.Encrypt(Encoding.UTF8.GetBytes(user), false);


byte[] userBytes = encryptCipher.doFinal(user.getBytes(StandardCharsets.UTF_8));

you mean before encrypting or after?
CCisien10/27/2022
yeah
CCisien10/27/2022
looks like before?
BBrunORC10/27/2022
because they are both set to UTF 8 befure encrypting
BBrunORC10/27/2022
but your guess sounds about right as it was double the size
CCisien10/27/2022
you'd have to examine the raw output between the two
CCisien10/27/2022
it could be that the server is complaining because it was encoded incorrectly in the request
CCisien10/27/2022
some things like to base64 a byte array when building a json request
BBrunORC10/27/2022
yeah, the userBytes is later encoded using Base64
in Java it woudl go like this:
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)
BBrunORC10/27/2022
will try finding if there are different encoding options in the Convert.ToBase64String method
CCisien10/27/2022
yeah
CCisien10/27/2022
encoding won't come into play at that point
CCisien10/27/2022
base64 is base64
CCisien10/27/2022
as long as the server expects it to be base64 encoded, there shouldn't be a problem
CCisien10/27/2022
next step is to compare the output of Encoding.UTF8.GetBytes(user) and user.getBytes(StandardCharsets.UTF_8) at the byte level
BBrunORC10/27/2022
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..
BBrunORC10/27/2022
thanks mate
CCisien10/27/2022
you might also look into using bouncy castle for .net, it's popular in the java world, so there may be significant overlap
BBrunORC10/27/2022
I'm using it already somewhere else in this project, you're telling me to use it for encryption using the public key?
CCisien10/27/2022
it's something to try
CCisien10/27/2022
especially if it's used by the java side as well
BBrunORC10/27/2022
will do it then. Thanks again
BBrunORC10/27/2022
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
BBrunORC10/27/2022
using bouncy castle did the trick
BBrunORC10/27/2022
thanks Cisien
BBrunORC10/27/2022
still can't wrap my head around why microsoft's encryption would not work
BBrunORC10/27/2022
now how do I close this post?
VJVINI JR1/18/2023
bro I know its a quiet time ago but are u still there?
VJVINI JR1/18/2023
Im dealing with it rn, need some help
BBrunORC1/18/2023
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
BBrunORC1/18/2023
What is it that you need some help with?
VJVINI JR1/18/2023
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 ... ?
BBrunORC1/18/2023
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
VJVINI JR1/18/2023
I appreciate
VJVINI JR1/18/2023
I still keep looking for something
VJVINI JR1/18/2023
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);
            }
AAccord1/19/2023
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.