0

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 !

Mr MAmour
  • 23
  • 3
  • 1
    The code `KeyFactory .getInstance("RSA");` and the exception does not correspond because is using a DSAKeyFactory instead of RSAKeyFactory. Ensure you are executing the correct code – pedrofb Oct 12 '17 at 12:36

1 Answers1

0

Option 1: Same data, different format.

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.

Option 2: Same format, different parser.

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):

  • A byte, value 0x06 (PUBLICKEYBLOB)
  • A byte, value 0x02 (blob v2)
  • A short, value 0x0000 (reserved)
  • An integer (stored as little-endian) identifying the key as RSA (0x0000A400 or 0x00002400)
  • An integer (stored as little-endian) identifying the next segment as an RSA public key (a bit redundant, but technically a different structure now): 0x31415352

After all that comes the relevant data:

  • The bit-length of the modulus stored as a little-endian unsigned integer. For your 1024-bit example this will be 1024, aka 0x00000400, aka { 00 04 00 00 }(LE).
  • The public exponent, stored as a little-endian unsigned integer. This is almost always 0x00010001 (aka { 01 00 01 00 }), but since it's there you should respect it.
  • The next bitLen/8 bytes represent the modulus value. Since this is the public key that should be "the rest of the bytes in this array".

Option 3: Build a certificate

.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;
bartonjs
  • 30,352
  • 2
  • 71
  • 111