How does public and primary keys look like in rsa c#? - c#

I am trying to use Asymmetric key algorithm rsa to secure my APIs.
suppose i create a Key pair for a user Who is using my APIs
as
RSA = new RSACryptoServiceProvider();
string PublicKey = RSA.ToXmlString(false);
string PrivateKey = RSA.ToXmlString(true);
now my private key look like
<RSAKeyValue>
<Modulus>tMvvUeHhggKAVex8JzYKXYQ32HVmr05PdtT1KV3kTkKE26jO/9IVmg+bWwxR1vuMzmY7spwguSJQsnjutJXamH0 mblNgYHmWwhyhJMSTtnZp57VDNNedjCFQnLOn211yk/PpCQHEiDDvt84hnmcdXNBlfZfkQzQ+UO/elhP5NH0=</Modulus>
<Exponent>AQAB</Exponent>
<P>22SSx3JpSqYVUWuxiSKwmh/8RDDcgDvq8l+4dMlQ/F+BJhthTQ3UJGupWaxiJyXX95AYAJIJJvWVvmvI7tqbxw==</P><Q>0va2WFy0oUwX4eJtZElRbot9TOrwGcqI64rMAYjvxl/mayCRXf0ypwKofKWOsmjK/pX0xWaqnFWB/NdLFt4Fmw==</Q><DP>VbIYPz2qcRUkmJQnWbiqINnDkONBDfnZkOjgxQVp09p+OONTA2UWa09+a9+Qy1fV3wZyya5BUu10m1fAucO8Ow==</DP>
<DQ>Lm8hOZfGJk6SXySwgUdmBhfrz3dSu8qJkpatSpUyeY54MBIuDOsDMCF0pmLmYryQGbM1+hEb8mcbwmQ84d6iiw==</DQ>
<InverseQ>L67OLIIK+M3OF1nxSHTjZ+Kv/hwOHJPvdHHSuh9VEmw93kPGn6Qt6GudEreFb6xlFsuR6UM19LUIweapgaUagQ==</InverseQ>
<D>YbaGlZ6bHoTzj3zMbPTMDVbUR+zLnpuYXwUhq0XPimxxGbbWiXSlsCoXMNIruSEjLLocMaAoH2bobkzl1jvXdAI30NdZ/rpG5SRl dpaeIundLfHkSnZfHwHuP9OGXJSXmbmLCrO8dq7BjLauS4JiTRgjiXoq8c995MEF+vhw9hE=</D>
</RSAKeyValue>
And this is my public key
<RSAKeyValue>
<Modulus>lF1Yof6FXaY9M/StHwPC1voW5K5JOqSu8BXndS4WH06L9mOfrNI9Et0cRaHjiYd2bQNFZB9+ AZmN4ODc36DgVPqQqYY2tbH+9UY0hcdi+WnDuUZe7sgvWKfGwBKTcy7g8uUAzpTWEP1W0FqeMunmXljD8Iouoqp5oHtTW1GHIlE=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Now my doubt is here Private key string also contain public key
what part of private or public key string should i send to user as it is in
XmlFormat
and there are multiple tags like modulus ,D,Q in the string what is actually a key in the string which i send to a user as his public , pvt keys
as my user will be on android platform
will he be able to use those keys in xml format ?
like in c# when we have to use keys
i do rsa.FromXmlString(Key);
where public or private key is in XmlFormat
Can that user will able to do this on android as well

Related

How to validate an RSA public key?

If I receive a PEM-encoded key, I can import it like so:
using var rsa = new RSACryptoServiceProvider(2048);
rsa.ImportFromPem(keyString.AsSpan());
// do stuff with it
However, the documentation for ImportFromPem states that it will indiscriminately accept four key types: PUBLIC KEY, PRIVATE KEY, RSA PRIVATE KEY, RSA PUBLIC KEY, and that "Unsupported or malformed PEM-encoded objects will be ignored."
If I want to verify that 1) what I received is a public key and not a private key, and 2) it's a valid public key and not an "Unsupported or malformed PEM-encoded object," how would I go about doing that?
The successful import with the posted code also means a formal validation of the RSA key. The key can be imported:
if it is an RSA key.
if the key contains one of the labels supported for RSA (i.e. PUBLIC KEY (SPKI), RSA PUBLIC KEY (public PKCS#1), PRIVATE KEY (PKCS#8), RSA PRIVTE KEY (private PKCS#1)).
if the body consists of a valid (Base64 encoded) ASN.1/DER.
if the label is consistent with the key contained in the body (both by type (public/private) and format (PKCS#1/PKCS#8/SPKI)).
There is no guarantee that the parameters are validated contentwise for consistency (e.g. in the case of a private key, the modulus might well not be equal to the product of p and q). Among other things, this depends on the platform (e.g. Windows or Unix). Thus, if guaranteed validation of the parameters is required, the parameters must be explicitly validated.
Explicit validation of the parameters is possible by exporting the RSA parameters with ExportParameters() and then validating them. This is relevant for private keys, where the parameters are dependent on each other, so that these consistencies can be tested (e.g. whether the modulus is equal to the product of the two primes). There are no such consistency checks for public keys.
Since this question is about public keys, the checks for private keys are not necessary (and could be omitted).
For a key imported this way, PublicOnly can be used to check whether it is a private or public key.
This makes it relatively easy to create a logic for RSA key validation, e.g.:
using System.Numerics;
using System.Security.Cryptography;
...
private static void ImportPublicKey(string key)
{
using var rsa = new RSACryptoServiceProvider();
try
{
rsa.ImportFromPem(key.AsSpan());
if (rsa.PublicOnly)
{
Console.WriteLine("Public RSA key");
}
else
{
// Explicit check of the consistency of the parameters for private keys, for example N = p*q
var rsaParams = rsa.ExportParameters(true);
BigInteger m = new BigInteger(rsaParams.Modulus, true, true);
BigInteger p = new BigInteger(rsaParams.P, true, true);
BigInteger q = new BigInteger(rsaParams.Q, true, true);
Console.WriteLine("Private RSA key - params " + (p*q==m ? "consistent" : "not consistent"));
}
}
catch
{
Console.WriteLine("Invalid or inconsistent RSA key");
}
}
First of all, it depends on why you want to validate the public key. You should not expect an adversary to deliberately send a bad key. If they can do that they can simply send you the wrong key. To prevent that you need a better way of managing keys, such as using certificates within a Public Key Infrastructure or PKI.
Great, that out of the way, I'd like to indicate that all the defined PEM keys contain a public key. RSA PUBLIC KEY is a PKCS#1 defined public key and RSA PRIVATE KEY is a PKCS#1 private key. However, the definition of the private key also contains the public exponent. Similarly, PUBLIC KEY is a SubjectPublicKeyInfo structure and PRIVATE KEY is a PKCS#8 defined private key. Both contain PKCS#1 key structures inside and therefore the public key.
Maybe you suspect that the public key in the instance is not replaced. That is certainly a possible option if I read the documentation correctly (the quality of the Microsoft documentation is often, uh, questionable though). In that case you might want to validate that the modulus changes value. The modulus is specific for each key pair and it is contained in both public and private keys. You can access the modulus using ExportParameters(false).Modulus.

Create CSR with Bouncy Castle (C#) inclusive EC Curve Name

I create a CSR with Bouncy Castle and sign it on HSM (EC Key). It works fine! But in the CSR content (and in the certificate later) there is no EC curve name. Instead, there are the parameters of the curve. Some clients have problems with this, e.g .: Phyton. On the screenshot you can see how OpenSSL does it. How can I force Bouncy Castle to write the curve name in the CSR?
The code for the Adding the Public Key to CSR:
IList oids = new ArrayList();
IList values = new ArrayList();
..........
SubjectKeyIdentifier subjectKeyIdentifier = new SubjectKeyIdentifierStructure(publicKey);
X509Extension ski = new X509Extension(true, new DerOctetString(subjectKeyIdentifier));
oids.Add(X509Extensions.SubjectKeyIdentifier);
values.Add(ski);
AttributePkcs attribute = new AttributePkcs(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest,
new DerSet(new X509Extensions(oids, values)));
//End Extensions
// SIGN on HSM
Pkcs10CertificationRequestDelaySigned csr = new Pkcs10CertificationRequestDelaySigned(
signatureAlgorithmStr,
subject,
publicKey,
new DerSet(attribute)
);
......
Screenshot: CSR with Bouncy Castle und OpenSSL
The issue isn't with BouncyCastle, but rather how the public key is being generated / initialized.
You can either opt for named curve or as expanded group parameters, but not both.
Technically they are equivalent, but usage wise, they don't mix well with one another. So you will have to generate both if you want to cater to both of those scenarios.
I don't know how you are initializing the public key, but you can convert to a different public key type before signing.
AsymmetricKeyParameter publicKey = null;
// ... existing public key by some means
var castedPublicKey = (ECPublicKeyParameters)publicKey;
var newPublicKey = new ECPublicKeyParameters(
castedPublicKey.AlgorithmName
,castedPublicKey.Q,
X9ObjectIdentifiers.Prime256v1);
// ... rest of code
// SIGN on HSM
Pkcs10CertificationRequestDelaySigned csr = new Pkcs10CertificationRequestDelaySigned(
signatureAlgorithmStr,
subject,
newPublicKey,
new DerSet(attribute)
);
Change Prime256v1 to whatever curve you are using

RSA Encryption with Public Key

I am trying to RSA Encrypt a string with a given key. (I can not change the key, since I request it from another system)
I get the key as a string and it looks like this:
-----BEGIN CERTIFICATE-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSehIDcXho52VvAQAFfVD2CzOFAYKSfnRsuKE/nqE1O4l/X6opYgjxj/mSNk1bvNobwiRBL4uWfFnsHoQSiv1Gqzl+JQ2QQ2rUVGuNZ7d7agjYcb1LCpKPE1Q0kqLvbGyDWQx8ULC7/FJ49mMwbzIE4C9ovfdOBa0er6IDNSW0IQIDAQAB-----END
CERTIFICATE-----
Now I tried to encrypt the string on many ways, but none of them worked.
The last solution I tried was the following:
public static string RSA(string payload, string publicKey)
{
byte[] toEncryptData = Encoding.ASCII.GetBytes(payload);
RSACryptoServiceProvider rsaPublic = new RSACryptoServiceProvider();
rsaPublic.FromXmlString(publicKey);
byte[] encryptedRSA = rsaPublic.Encrypt(toEncryptData, false);
string EncryptedResult = Encoding.Default.GetString(encryptedRSA);
return EncryptedResult;
}
It always throws an Exception when it comes to:
rsaPublic.FromXmlString(publicKey);
So what can I do to encrypt the string with RSA?
I need it because I want to implement an API: https://www.loxone.com/dede/wp-content/uploads/sites/2/2016/08/0903_Communicating-with-the-Miniserver.pdf
On Page 5 under 6. you can see the requirement!!
public static string ConvertToXmlPublicJavaKey(string publicJavaKey)
{
RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicJavaKey));
string xmlpublicKey = string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
return xmlpublicKey;
}
The PublicKeyFactory class is from BouncyCastle.Crypto, you can Google it for more information.
Then your rsaPublic.FromXmlString(publicKey) will work fine.

Convert from RSACryptoServiceProvider to RSACng

I am currently using RSACryptoServiceProvider and I want to change to RSACng. I am using it to sign data. The reason for the change is that I am using Pkcs1 padding and I understand that Pss padding is preferred. We are undergoing security audits.
My question is how do I instantiate RSACng so that it uses the same private / public key each time?
With RSACryptoServiceProvider I am doing:
CspParameters cp = new CspParameters();
cp.KeyContainerName = "ContainerName";
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(cp);
passing in the container name means it uses the key that persists in the in the container store on the machine.
With RSACng, I tried this, but I get an exception: "The requested operation is not supported"
RSACng RSA = new RSACng(CngKey.Create(CngAlgorithm.Sha256, ContainerName));
I just need to be able to pass the store key name so it uses the same key each time instead of generating a new key.
If you want to create a named/persisted RSA key with CNG:
private static RSA CreatePersistedRSAKey(string name, int keySizeInBits)
{
CngKeyCreationParameters creationParameters = new CngKeyCreationParameters
{
// This is what an ephemeral key would have had
// (allows ExportParameters(true) to succeed). Adjust as desired.
//
// The default is not exportable (only applies to the private key)
ExportPolicy =
CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport,
};
creationParameters.Parameters.Add(
new CngProperty(
"Length",
BitConverter.GetBytes(keySizeInBits),
CngPropertyOptions.Persist));
// RSACng will extract the data it needs from this key object,
// but doesn't take ownership
using (CngKey key = CngKey.Create(CngAlgorithm.Rsa, name, creationParameters))
{
return new RSACng(key);
}
}
This skips the parts where you would do a try/catch around a call to CngKey.Open, or might want to delete the key (open it with CngKey.Open, and call Delete on the CngKey instance).
(CngAlgorithm.Rsa was added in net46. If you're on an older version then an equivalent would be new CngAlgorithm("RSA"))

Extractig public key value via PKCS#11

I am using Pkcs11 library to retreive key from device:
I am able to retreive ObjectHandle of public key. I try to extract public key value:
var publicKeyHandle = GetPublicKeyByLabel(Session, "KEY1_QAL_PUB");
var objectAttribute = Session.GetAttributeValue(publicKeyHandle, new List<CKA> { CKA.CKA_VALUE }).Single();
var keyVal = objectAttribute.GetValueAsByteArray();
Unfortunately, objectAttribute has CannotBeRead set to true and value cannot be read. Even when I am logged as user or SO.
I understand the case, when the key is private - due to security reasons I cannot get value outside HSM device. But why I cannot extract public key value?
CKA_VALUE is not a valid attribute for RSA public key objects. If you want to extract RSA public key value then you need to read CKA_PUBLIC_EXPONENT and CKA_MODULUS attributes. See PKCS#11 v2.20 for more details.

Categories