I am new to verification and certificates etc .. I am facing an issue , that I need to sign a message on c# then verify the signature on java , the issue I ma facing that I am unable to load the public key on java on a (PublicKey) object using the Base64 string generated on c# , I used the following code to generate the private and public key on c# side
CspParameters cspParams = new CspParameters { ProviderType = 1 };
cspParams.KeyContainerName = "MyKeyContainer";
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
System.Diagnostics.Debug.WriteLine("pub:" + publicKey);
System.Diagnostics.Debug.WriteLine("pri:" + privateKey);
Console.WriteLine("Key added to container: \n {0}", rsaProvider.ToXmlString(true));
then I used the following code to create a public key on Java side :
X509EncodedKeySpec specc = new X509EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64("BgIAAACkAABSU0ExAAQAAA......"));
KeyFactory xx = KeyFactory .getInstance("RSA");
PublicKey ssx= xx.generatePublic(specc);
note that I copied the base64 public key string from the c# console . When I try to run the code on java side the I get the following exception :
java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)
I need to find a way to generate private and public key on c# (and generate a .cer file for the public key) to load it on java side , or find a way to load the base64 public key string into a (Publickey) object on java side . please help !
The easiest way to transmit a public RSA key from .NET is to check that the public exponent value is { 01 00 01 }
and then send the modulus value. On the receiver side you accept the modulus and assert the public exponent.
RSAParameters keyParams = rsa.ExportParameters(false);
if (!keyParams.Exponent.SequenceEqual(new byte[] { 0x01, 0x00, 0x01 }))
throw new InvalidOperationException();
Send(keyParams.Modulus);
Then Creating RSA keys from known parameters in Java says you can straightforwardly recover it on the Java side.
The next option you have is to keep using the CSP blob, but writing a parser in Java. The data is the result of calling CryptExportKey
with PUBLICKEYBLOB
, making your data layout as described at https://msdn.microsoft.com/en-us/library/ee442238.aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx#pub_BLOB.
In summary:
A header (which you could decide to skip, or just test it for equal to the fixed value(s) that you expect):
0x06
(PUBLICKEYBLOB
)0x02
(blob v2)0x0000
(reserved)0x0000A400
or 0x00002400
)0x31415352
After all that comes the relevant data:
0x00000400
, aka { 00 04 00 00 }
(LE).0x00010001
(aka { 01 00 01 00 }
), but since it's there you should respect it.bitLen/8
bytes represent the modulus value. Since this is the public key that should be "the rest of the bytes in this array"..NET Framework doesn't have this capability built-in (as of the current version, 4.7). You can P/Invoke to CertCreateSelfSignCertificate
, but that would involve quite a lot of change (since RSACryptoServiceProvider won't let you get at the key handle, so you'll have to P/Invoke all of that, too).
You could "bit bang" out the DER-encoded certificate yourself. While fun, this is hard to get right, and probably not a viable path.
If you can move to .NET Core, the ability to create certificates has been added to .NET Core 2.0 via the CertificateRequest class. For a very simple certificate from your key:
var certReq = new CertificateRequest(
"CN=SubjectCN",
rsaProvider,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
// add any extensions you want. I'm not adding any because I said "simple".
DateTimeOffset now = DateTimeOffset.UtcNow;
X509Certificate2 cert = certReq.CreateSelfSigned(now, now.AddMinutes(90));
byte[] xfer = cert.RawData;