Find public key from certificate as a xml string - windows-phone-7

i need to find the public key from certificate as xml string. I can take the public key only as a string with this method:
public string CelesiPublik()
{
X509Certificate cer;
cer = X509Certificate.CreateFromCertFile("C://certificate//EdonBajrami.crt");
string Celesi = cer.GetPublicKeyString();
return Celesi;
}
and then i take this value to the another method. Celesi value now has take celesiPublik
celesiPublik = e.Result;
string NrTelefonit = "044-419-109";
string salt = "VotimiElektronikKosove";
AesManaged aes = new AesManaged();
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(celesiPublik,Encoding.UTF8.GetBytes(salt));
aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(celesiPublik);
rsa.Encrypt(aes.Key, false);
but it shows me error. How can i resolve this problem?
GregS i cannot use in Windows Phone 7 X509Certificate2. I take my public key with the method:
`X509Certificate cer = new X509Certificate("C://certificate//EdonBajrami.crt");
string publicKey = cer.GetPublicKeyString();`
i can take the public key. Then in another method i take value of the publicKey to another string variable Key:
`string Key = publicKey;
//-----First------
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
//----------
RSAParameters RSAKeyInfo = new RSAParameters();
byte[] celesibyte = System.Text.Encoding.UTF8.GetBytes(celesiPublik);
byte[] Exponent = { 1, 0, 1 };
RSAKeyInfo.Modulus = celesibyte;
RSAKeyInfo.Exponent = Exponent;
rsa.ImportParameters(RSAKeyInfo);
//-----------
/* rsa.FromXmlString(celesi2Publik); */
string edon = "Edon";
byte[] edon1 = Encoding.UTF8.GetBytes(edon);
byte[] edon2 = rsa.Encrypt(edon1, false);'
but it sends me data that is 506 byte, i dont understand why, what is a problem ?

As far as I can tell the X509Certificate class is nearly useless. Perhaps that is why there is a class X509Certificate2? Use the X509Certificate2 class. You can create an RSACryptoServiceProvider directly from the public key like the following:
X509Certificate2 cert = new X509Certificate2(X509Certificate.CreateFromCertFile("C://certificate//EdonBajrami.crt"));
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider) cert.PublicKey.Key;

Related

exporting a public key in pem format from x509certificate2 object

I'm new to this subject, and I got confused of the differences between a public key in PEM format vs CER format.
I'm trying to export a public key from a x509certificate2 object in PEM format in c# code.
As far as I understand, the difference between a certificate in cer format vs pem format, is only the header and footer
(if I understand correctly, a certificate in .cer format in base 64 should be someBase64String and in pem format it's the same string including the begin and end header and footer).
but my question is for the public key.
let pubKey be a public key exported in .cer format from an x509certificate2 object,
is the pem format of this key, will be:
------BEGIN PUBLIC KEY-----
pubKey...
------END PUBLIC KEY------
encoded in base 64?
Thanks :)
for the public key. let pubKey be a public key exported in .cer format from an x509certificate2 object
Talking about a ".cer format" only applies when you have the whole certificate; and that's all that an X509Certificate2 will export as. (Well, or a collection of certificates, or a collection of certificates with associated private keys).
Edit (2021-08-20):
Starting in .NET 6 you can use cert.PublicKey.ExportSubjectPublicKeyInfo() to get the DER-encoded SubjectPublicKeyInfo.
In .NET Core 3+/.NET 5+ you can use cert.GetRSAPublicKey()?.ExportSubjectPublicKeyInfo() (or whatever algorithm your key is)
In .NET 5+ you can turn those answers into PEM with PemEncoding.Write("PUBLIC KEY", spki)
Regardless of your .NET/.NET Core/.NET Framework version you can use the System.Formats.Asn1 package with AsnWriter to avoid the BuildSimpleDerSequence work (published 2020-11-09).
-- Original answer continues --
Nothing built in to .NET will give you the DER-encoded SubjectPublicKeyInfo block of the certificate, which is what becomes "PUBLIC KEY" under a PEM encoding.
You can build the data yourself, if you want. For RSA it's not too bad, though not entirely pleasant. The data format is defined in https://www.rfc-editor.org/rfc/rfc3280#section-4.1:
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
https://www.rfc-editor.org/rfc/rfc3279#section-2.3.1 describes how RSA keys, in particular are to be encoded:
The rsaEncryption OID is intended to be used in the algorithm field
of a value of type AlgorithmIdentifier. The parameters field MUST
have ASN.1 type NULL for this algorithm identifier.
The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER } -- e
The language behind these structures is ASN.1, defined by ITU X.680, and the way they get encoded to bytes is covered by the Distinguished Encoding Rules (DER) ruleset of ITU X.690.
.NET actually gives you back a lot of these pieces, but you have to assemble them:
private static string BuildPublicKeyPem(X509Certificate2 cert)
{
byte[] algOid;
switch (cert.GetKeyAlgorithm())
{
case "1.2.840.113549.1.1.1":
algOid = new byte[] { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
break;
default:
throw new ArgumentOutOfRangeException(nameof(cert), $"Need an OID lookup for {cert.GetKeyAlgorithm()}");
}
byte[] algParams = cert.GetKeyAlgorithmParameters();
byte[] publicKey = WrapAsBitString(cert.GetPublicKey());
byte[] algId = BuildSimpleDerSequence(algOid, algParams);
byte[] spki = BuildSimpleDerSequence(algId, publicKey);
return PemEncode(spki, "PUBLIC KEY");
}
private static string PemEncode(byte[] berData, string pemLabel)
{
StringBuilder builder = new StringBuilder();
builder.Append("-----BEGIN ");
builder.Append(pemLabel);
builder.AppendLine("-----");
builder.AppendLine(Convert.ToBase64String(berData, Base64FormattingOptions.InsertLineBreaks));
builder.Append("-----END ");
builder.Append(pemLabel);
builder.AppendLine("-----");
return builder.ToString();
}
private static byte[] BuildSimpleDerSequence(params byte[][] values)
{
int totalLength = values.Sum(v => v.Length);
byte[] len = EncodeDerLength(totalLength);
int offset = 1;
byte[] seq = new byte[totalLength + len.Length + 1];
seq[0] = 0x30;
Buffer.BlockCopy(len, 0, seq, offset, len.Length);
offset += len.Length;
foreach (byte[] value in values)
{
Buffer.BlockCopy(value, 0, seq, offset, value.Length);
offset += value.Length;
}
return seq;
}
private static byte[] WrapAsBitString(byte[] value)
{
byte[] len = EncodeDerLength(value.Length + 1);
byte[] bitString = new byte[value.Length + len.Length + 2];
bitString[0] = 0x03;
Buffer.BlockCopy(len, 0, bitString, 1, len.Length);
bitString[len.Length + 1] = 0x00;
Buffer.BlockCopy(value, 0, bitString, len.Length + 2, value.Length);
return bitString;
}
private static byte[] EncodeDerLength(int length)
{
if (length <= 0x7F)
{
return new byte[] { (byte)length };
}
if (length <= 0xFF)
{
return new byte[] { 0x81, (byte)length };
}
if (length <= 0xFFFF)
{
return new byte[]
{
0x82,
(byte)(length >> 8),
(byte)length,
};
}
if (length <= 0xFFFFFF)
{
return new byte[]
{
0x83,
(byte)(length >> 16),
(byte)(length >> 8),
(byte)length,
};
}
return new byte[]
{
0x84,
(byte)(length >> 24),
(byte)(length >> 16),
(byte)(length >> 8),
(byte)length,
};
}
DSA and ECDSA keys have more complex values for AlgorithmIdentifier.parameters, but X509Certificate's GetKeyAlgorithmParameters() happens to give them back correctly formatted, so you would just need to write down their OID (string) lookup key and their OID (byte[]) encoded value in the switch statement.
My SEQUENCE and BIT STRING builders can definitely be more efficient (oh, look at all those poor arrays), but this would suffice for something that isn't perf-critical.
To check your results, you can paste the output to openssl rsa -pubin -text -noout, and if it prints anything other than an error you've made a legally encoded "PUBLIC KEY" encoding for an RSA key.
Like bartonjs said: "SubjectPublicKeyInfo becomes "PUBLIC KEY" under a PEM encoding, and nothing built in to .NET will give you the DER-encoded SubjectPublicKeyInfo block of the certificate."
Luckily here comes Bouncy Castle to rescue, with just few lines you can extract SubjectPublicKeyInfo (i.e Public key for PEM certificate) from X509Certificate2
Org.BouncyCastle.X509.X509Certificate bcert = new Org.BouncyCastle.X509.X509Certificate(x509Certificate2.RawData);
var publicKeyInfoBytes = bcert.CertificateStructure.SubjectPublicKeyInfo.GetDerEncoded();
Since .NET Core 3.0 (and .NET Standard 2.1) you can use ExportSubjectPublicKeyInfo method like this:
certificate.PublicKey.Key.ExportSubjectPublicKeyInfo()
In case PublicKey.Key throws an exception (supports only RSA and DSA), use one of ECDsaCertificateExtensions.GetECDsaPublicKey, RSACertificateExtensions.GetRSAPublicKey, DSACertificateExtensions.GetDSAPublicKey.

DES decryption in Ruby

The following is my java code for DES decryption:
public static byte[] decrypt(final byte[] value, final String key) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException {
final DESKeySpec objDesKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
final SecretKeyFactory objKeyFactory = SecretKeyFactory.getInstance("DES");
final SecretKey objSecretKey = objKeyFactory.generateSecret(objDesKeySpec);
final byte[] rgbIV = key.getBytes();
final IvParameterSpec iv = new IvParameterSpec(rgbIV);
final Cipher objCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
objCipher.init(2, objSecretKey, iv);
return objCipher.doFinal(value);
}
And I try to convert it to Ruby code as the following:
def decryption(key, decodeString)
ALG = 'des'
cipher = OpenSSL::Cipher::Cipher.new(ALG)
cipher.decrypt #choose descryption mode.
cipher.key = key
plain = cipher.update(decodeString )
plain << cipher.final
end
After executing the java and ruby code, I got the same size of bytes, but the contents of bytes are different. Where did I go wrong?
Thanks for your question!
To do this, use the OpenSSL::Cipher library. Here is a link with some sample code for AES: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html#class-OpenSSL::Cipher-label-Encrypting+and+decrypting+some+data
To use DES, run this command to see if your Ruby installation supports DES.
puts OpenSSL::Cipher.ciphers
According to this article : http://43n141e.blogspot.tw/2008/08/des-encryption-java-to-openssl-to-ruby.html , I try the following two steps :
1. Calculate iv value in Java:
String key = "123456"
final byte[] rgbIV = key.getBytes();
final IvParameterSpec iv = new IvParameterSpec(rgbIV);
byte[] ivBytes = iv.getIV();
StringBuffer sbuf = new StringBuffer();
for (byte b : ivBytes) {
sbuf.append(String.format("%02x", (b & 0xFF)));
}
System.out.println("iv: " + sbuf);
2. Decrypt in Ruby :
def decode(encryptedString, key, iv)
decrypt = OpenSSL::Cipher::Cipher.new('des-cbc')
decrypt.decrypt
decrypt.key = key
decrypt.iv = iv.scan(/../).map{|b|b.hex}.pack('c*')
decrypt.update(encryptedString) + decrypt.final
end
and it works !

Bad Data cryptographicexception in xml using Linq in c#

My XML file:
<?xml version="1.0" encoding="utf-8"?>
<dskh>
<khachhang maso="kh01">
<ten_kh>thehung</ten_kh>
<tuoi_kh>15</tuoi_kh>
<dchi_kh>hochiminh</dchi_kh>
</khachhang>
<khachhang maso="kh02">
<ten_kh>hung</ten_kh>
<tuoi_kh>15</tuoi_kh>
<dchi_kh>hcm</dchi_kh>
</khachhang>
</dskh>
My Encrypt and Decrypt code:
class Program
{
// Call this function to remove the key from memory after use for security.
[System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
public static extern bool ZeroMemory(ref string Destination, int Length);
// Function to Generate a 64 bits Key.
static string GenerateKey()
{
// Create an instance of Symetric Algorithm. Key and IV is generated automatically.
DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create();
// Use the Automatically generated key for Encryption.
return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
}
static void EncryptFile(string sInputFilename,
string sOutputFilename,
string sKey) {
FileStream fsInput = new FileStream(sInputFilename,FileMode.Open,FileAccess.Read);
FileStream fsEncrypted = new FileStream(sOutputFilename,FileMode.Create,FileAccess.Write);
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = Encoding.UTF8.GetBytes(sKey);
DES.IV = Encoding.UTF8.GetBytes(sKey);
ICryptoTransform desencrypt = DES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(fsEncrypted,desencrypt,CryptoStreamMode.Write);
byte[] bytearrayinput = new byte[fsInput.Length - 1];
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
}
static void DecryptFile(string sInputFilename,
string sOutputFilename,
string sKey)
{
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
//A 64 bit key and IV is required for this provider.
//Set secret key For DES algorithm.
DES.Key = Encoding.UTF8.GetBytes(sKey);
//Set initialization vector.
DES.IV = Encoding.UTF8.GetBytes(sKey);
//Create a file stream to read the encrypted file back.
FileStream fsread = new FileStream(sInputFilename,
FileMode.Open,
FileAccess.Read);
//Create a DES decryptor from the DES instance.
ICryptoTransform desdecrypt = DES.CreateDecryptor();
//Create crypto stream set to read and do a
//DES decryption transform on incoming bytes.
CryptoStream cryptostreamDecr = new CryptoStream(fsread,
desdecrypt,
CryptoStreamMode.Read);
//Print the contents of the decrypted file.
StreamWriter fsDecrypted = new StreamWriter(sOutputFilename);
fsDecrypted.Write(new StreamReader(cryptostreamDecr).ReadToEnd());
fsDecrypted.Flush();
fsDecrypted.Close();
}
static void Main(string[] args)
{
// Must be 64 bits, 8 bytes.
// Distribute this key to the user who will decrypt this file.
string sSecretKey;
// Get the key for the file to encrypt.
sSecretKey = GenerateKey();
// For additional security pin the key.
GCHandle gch = GCHandle.Alloc(sSecretKey, GCHandleType.Pinned);
// Encrypt the file.
EncryptFile(#"C:\xml_kh.xml",
#"C:\xml_encry.xml",
sSecretKey);
//Decrypt the file.
DecryptFile(#"C:\xml_encry.xml",
#"C:\xml_decry.xml",
sSecretKey);
}
}
When i excute Decrypt code, i get Cryptographicexception: Bad Data here:
//Print the contents of the decrypted file.
StreamWriter fsDecrypted = new StreamWriter(sOutputFilename);
fsDecrypted.Write(new StreamReader(cryptostreamDecr).ReadToEnd());
fsDecrypted.Flush();
fsDecrypted.Close();
Please help me!!!
Add two lines at the end of EncryptFile function:
cryptostream.Flush();
cryptostream.Close();
EDIT
I missed there is one more error:
byte[] bytearrayinput = new byte[fsInput.Length - 1];
s/b
byte[] bytearrayinput = new byte[fsInput.Length];

What is the default signature algorithm of bouncycastle CMSSignedDataGenerator if not explicitly specified

I was wondering what signature algorithm (digestOID) BouncyCastle uses by default if you do not specify it explicitly like like in the code below:
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray("Hello world!".getBytes());
certList.add(signCert);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(signKP.getPrivate());
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(sha1Signer, signCert));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, false);
Below is the code example for which I am wondering as you see there is no digestOID(SHA1withRSA) so what type of signature does it use:
import java.io.*;
import java.util.*;
import java.security.*;
import java.security.Security;
import java.security.cert.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.cms.*;
/* Create CMS/pkcs #7 signature using BC provider
M. Gallant 07/02/2003 */
class BCSignFile {
static final boolean DEBUG = false;
public static void main(String args[]) {
System.out.println("");
if (args.length < 4)
usage();
Security.addProvider(new BouncyCastleProvider());
String INFILE = args[0]; // Input file to be signed
String KEYSTORE = args[1]; // Java 2 keystore file
String ALIAS = args[2]; // Java 2 key entry alias
String PSWD = args[3]; // keystore password
// ---- in real implementation, provide some SECURE way to get keystore
// ---- password from user! -------
KeyStore keystore = null;
PublicKey pub = null;
PrivateKey priv = null;
java.security.cert.Certificate storecert = null;
java.security.cert.Certificate[] certChain = null;
ArrayList certList = new ArrayList();
CertStore certs =null;
try{
keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(KEYSTORE), PSWD.toCharArray());
certChain = keystore.getCertificateChain(ALIAS);
for ( int i = 0; i < certChain.length;i++)
certList.add(certChain[i]);
certs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC");
priv = (PrivateKey)(keystore.getKey(ALIAS, PSWD.toCharArray()));
storecert = keystore.getCertificate(ALIAS);
pub = keystore.getCertificate(ALIAS).getPublicKey();
}
catch(Exception exc){
System.out.println("Problem with keystore access: " + exc.toString()) ;
return;
}
if(DEBUG){
System.out.println("Public Key Format: " + pub.getFormat()) ;
System.out.println("Certificate " + storecert.toString()) ;
}
FileInputStream freader = null;
File f = null;
//------ Get the content data from file -------------
f = new File(INFILE) ;
int sizecontent = ((int) f.length());
byte[] contentbytes = new byte[sizecontent];
try {
freader = new FileInputStream(f);
System.out.println("\nContent Bytes: " + freader.read(contentbytes, 0, sizecontent));
freader.close();
}
catch(IOException ioe) {
System.out.println(ioe.toString());
return;
}
// --- Use Bouncy Castle provider to create CSM/PKCS#7 signed message ---
try{
CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
signGen.addSigner(priv, (X509Certificate)storecert, CMSSignedDataGenerator.DIGEST_SHA1);
signGen.addCertificatesAndCRLs(certs);
CMSProcessable content = new CMSProcessableByteArray(contentbytes);
CMSSignedData signedData = signGen.generate(content,"BC");
byte[] signeddata = signedData.getEncoded();
System.out.println("Created signed message: " + signeddata.length + " bytes") ;
FileOutputStream envfos = new FileOutputStream("BCsigned.p7s");
envfos.write(signeddata);
envfos.close();
}
catch(Exception ex){
System.out.println("Couldn't generate CMS signed message\n" + ex.toString()) ;
}
}
private static void usage() {
System.out.println("Usage:\n java BCSignFile <contentfile> <keystore> <alias> <keypasswd>") ;
System.exit(1);
}
}
The relevant line is this:
signGen.addSigner(priv, (X509Certificate)storecert, CMSSignedDataGenerator.DIGEST_SHA1);
This line specifies that the digest-algorithm will be SHA-1 and that the signing-algorithm will be decided based on the type of the private key in priv.
If priv contains an RSA key, it will sign using PKCS#1 v.1.5 with SHA-1 ("SHA1withRSA"). You can look in the source of CMSSignedGenerator.getEncOID() to see what happens with other types of private key.

SHA1 with salt on windows phone 7

I have some some time now reshearchd how to encode a password to SHA1 with a salt.
The is the code i used on my web application part, but it will not work on a phone environment.
public class Password
{
private string _password;
private int _salt;
public Password(string strPassword, int nSalt)
{
_password = strPassword;
_salt = nSalt;
}
public string ComputeSaltedHash()
{
// Create Byte array of password string
ASCIIEncoding encoder = new ASCIIEncoding();
Byte[] _secretBytes = encoder.GetBytes(_password);
// Create a new salt
Byte[] _saltBytes = new Byte[4];
_saltBytes[0] = (byte)(_salt >> 24);
_saltBytes[1] = (byte)(_salt >> 16);
_saltBytes[2] = (byte)(_salt >> 8);
_saltBytes[3] = (byte)(_salt);
// append the two arrays
Byte[] toHash = new Byte[_secretBytes.Length + _saltBytes.Length];
Array.Copy(_secretBytes, 0, toHash, 0, _secretBytes.Length);
Array.Copy(_saltBytes, 0, toHash, _secretBytes.Length, _saltBytes.Length);
SHA1 sha1 = SHA1.Create();
Byte[] computedHash = sha1.ComputeHash(toHash);
return encoder.GetString(computedHash);
}
public static int CreateRandomSalt()
{
Byte[] _saltBytes = new Byte[4];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(_saltBytes);
return ((((int)_saltBytes[0]) << 24) + (((int)_saltBytes[1]) << 16) +
(((int)_saltBytes[2]) << 8) + ((int)_saltBytes[3]));
}
public static string CreateRandomPassword(int PasswordLength)
{
String _allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ23456789!\"#ยค%&/()=?$+-_.,;'*";
Byte[] randomBytes = new Byte[PasswordLength];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(randomBytes);
char[] chars = new char[PasswordLength];
int allowedCharCount = _allowedChars.Length;
for (int i = 0; i < PasswordLength; i++)
{
chars[i] = _allowedChars[(int)randomBytes[i] % allowedCharCount];
}
return new string(chars);
}
}
Silverlight and Windows Phone 7 do not have an ASCIIEncoding. I suggest you use the UTF8Encoding instead. If you are certain that your passwords are always within the ASCII range then this encoding will work the same as the ASCIIEncoding would of had it been present.
If on the other hand you cannot guarantee that passwords are always within the ASCII range then you would need to make sure both ends hash using the UTF8Encoding to ensure generated hashs are the same.

Resources