I tried with the below suggestion,it is working for SecurityAlgorithms.HmacSha256Signature.
Suggested Code:
SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(symmetricKey), Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature)
but it is not working for SecurityAlgorithms.RsaSha256Signature.
Microsoft.IdentityModel.Tokens.SigningCredentials SigningCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(_signingKey, SecurityAlgorithms.RsaSha256Signature);
I got the below error.
DX10634: Unable to create the SignatureProvider.
SignatureAlgorithm: 'SHA256', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey' is not supported
I need to convert JWT token for Sha256. kindly provide solution with sample code.
Please find my code:
string _privateSecretKey = "MIIEogIBAAKCAQEAytYejMhaYjZwFgqP7WKh2bkf08=";
Microsoft.IdentityModel.Tokens.SymmetricSecurityKey _signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(Encoding.ASCII.GetBytes(_privateSecretKey));
Microsoft.IdentityModel.Tokens.SigningCredentials SigningCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(_signingKey, SecurityAlgorithms.RsaSha256Signature);
JwtHeader head = new JwtHeader();
head.Add("kid", "firstpublickey");//TBD
string sNewGuid = Guid.NewGuid().ToString("n");
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Iss, oTokenPostData.ClientID),
new Claim(JwtRegisteredClaimNames.Sub, oTokenPostData.ClientID),
new Claim(JwtRegisteredClaimNames.Aud, oTokenPostData.tokenUri),
new Claim(JwtRegisteredClaimNames.Jti, sNewGuid),
new Claim(JwtRegisteredClaimNames.Exp, ((int)DateTime.Now.AddMinutes(55).Subtract(UnixEpoch).TotalSeconds).ToString(System.Globalization.CultureInfo.InvariantCulture)),
new Claim(JwtRegisteredClaimNames.Iat, ((int)DateTime.Now.Subtract(UnixEpoch).TotalSeconds).ToString(System.Globalization.CultureInfo.InvariantCulture))
};
JwtPayload payload = new JwtPayload(claims);
JwtSecurityToken jwt = new JwtSecurityToken(head, payload);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
JWT is getting converted using the below code.
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace ConsoleApplication2
{
private class JWT
{
private bool verbose = false;
public string GenerateJWT()
{
DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
// Provide proper private key
string privateSecretKey = "OfED+KgbZxtu4e4+JSQWdtSgTnuNixKy1nMVAEww8QL3IN33XusJhrz9HXmIrdyX2F41xJHG4uj5/2Dzv3xjYYvqxexm3X3X5TOf3WoM1VNloJ7UnbqUJOiEjgK8sRdJntgfomO4U8s67cpysk0h9rc0He4xRspEjOapFfDg+VG8igidcNgbNDSSaV4491Fo3sq2aGSCtYvekzs7JwXJnNAyvDSJjfK/7M8MpxSMnm1vMscBXyiYFXhGC4wqWlYBE828/5DNyw3QZW5EjD7hvDrY5OlYd4smCTa53helNnJz5NT9HQaDbE2sMwIDAQABAoIBAEs63TvT94njrPDP3A/sfCEXg1F2y0D/PjzUhM1aJGcRiOUXnGlYdViGhLnnJoNZTZm9qI1LT0NWcDA5NmBN6gcrk2EApyTt1D1i4AQ66rYoTF9iEC4Wye28v245BYESA6IIelgIxXGsVyllERsbTkaphzibbYfHmvwMxkn135Zfzd/NOXl/O32vYIomzrNEP+tN2WXhhG8c8+iZ8PErBV3CqrYogYy97d2CeQbXcpd5unPiU4TK0nnzeBAXdgeYuJHFC45YHl9UvShRoe6CHR47ceIGp6WMc5BTyyTkZpctuYJTwaChdj/QuRSkTYmn6jFL+MRfYQJ8VVwSVo5DbkECgYEA4/YIMKcwObYcSuHzgkMwH645CRDoy9M98eptAoNLdJBHYz23U5IbGL1+qHDDCPXxKs9ZG7EEqyWezq42eoFoebLA5O6/xrYXoaeIb094dbCF4D932hAkgAaAZkZVsSiWDCjYSV+JoWX4NVBcIL9yyHRhaaPVULTRbPsZQWq9+hMCgYEA48j4RGO7CaVpgUVobYasJnkGSdhkSCd1VwgvHH3vtuk7/JGUBRaZc0WZGcXkAJXnLh7QnDHOzWASdaxVgnuviaDi4CIkmTCfRqPesgDR2Iu35iQsH7P2/o1pzhpXQS/Ct6J7/GwJTqcXCvp4tfZDbFxS8oewzp4RstILj+pDyWECgYByQAbOy5xB8GGxrhjrOl1OI3V2c8EZFqA/NKy5y6/vlbgRpwbQnbNy7NYj+Y/mV80tFYqldEzQsiQrlei78Uu5YruGgZogL3ccj+izUPMgmP4f6+9XnSuN9rQ3jhy4k4zQP1BXRcim2YJSxhnGV+1hReLknTX2IwmrQxXfUW4xfQKBgAHZW8qSVK5bXWPjQFnDQhp92QM4cnfzegxe0KMWkp+VfRsrw1vXNx";
rsa = DecodeRSAPrivateKey(FromBase64Url(privateSecretKey));
//convert to csp format
var bytes = rsa.ExportCspBlob(false);
var publicKey = Convert.ToBase64String(bytes);
//
RsaSecurityKey _signingKey = new RsaSecurityKey(rsa);
Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials =
new Microsoft.IdentityModel.Tokens.SigningCredentials(_signingKey, SecurityAlgorithms.RsaSha256);
JwtHeader head = new JwtHeader(signingCredentials);
head.Add("kid", "lzo-firstpublickey");
string sNewGuid = Guid.NewGuid().ToString("n");
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Iss, "s6BhdRkqt3"),
new Claim(JwtRegisteredClaimNames.Sub, "s6BhdRkqt3"),
new Claim(JwtRegisteredClaimNames.Aud, "https://cis.ncrs/connect/token"),
new Claim(JwtRegisteredClaimNames.Jti, sNewGuid),
new Claim(JwtRegisteredClaimNames.Exp, ((Int64)DateTime.Now.AddMinutes(55).Subtract(UnixEpoch).TotalSeconds).ToString(System.Globalization.CultureInfo.InvariantCulture), ClaimValueTypes.Integer64),
new Claim(JwtRegisteredClaimNames.Iat, ((Int64)DateTime.Now.Subtract(UnixEpoch).TotalSeconds).ToString(System.Globalization.CultureInfo.InvariantCulture), ClaimValueTypes.Integer64)
};
JwtPayload payload = new JwtPayload(claims);
JwtSecurityToken jwt = new JwtSecurityToken(head, payload);
jwt.SigningKey = _signingKey;
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
private static byte[] FromBase64Url(string base64Url)
{
string base64 = string.Empty;
if (!string.IsNullOrEmpty(base64Url))
{
string padded = base64Url.Length % 4 == 0
? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
base64 = padded.Replace("_", "/")
.Replace("-", "+");
}
return Convert.FromBase64String(base64);
}
private RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
System.IO.MemoryStream mem = new System.IO.MemoryStream(privkey);
System.IO.BinaryReader binr = new System.IO.BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
Console.WriteLine("showing components ..");
if (verbose)
{
showBytes("\nModulus", MODULUS);
showBytes("\nExponent", E);
showBytes("\nD", D);
showBytes("\nP", P);
showBytes("\nQ", Q);
showBytes("\nDP", DP);
showBytes("\nDQ", DQ);
showBytes("\nIQ", IQ);
}
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception)
{
return null;
}
finally
{
binr.Close();
}
}
private void showBytes(String info, byte[] data)
{
Console.WriteLine("{0} [{1} bytes]", info, data.Length);
for (int i = 1; i <= data.Length; i++)
{
Console.Write("{0:X2} ", data[i - 1]);
if (i % 16 == 0)
Console.WriteLine();
}
Console.WriteLine("\n\n");
}
private int GetIntegerSize(System.IO.BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek(-1, System.IO.SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
}
}
Related
How do I decrypt the output of this code using the private key (pem format) in C# ?
$output = json_encode(array('see'=>'me'));
define('CIPHER_BLOCK_SIZE', 100);
$encrypted = '';
$key = file_get_contents('public.txt');
$chunks = str_split($output, CIPHER_BLOCK_SIZE);
foreach($chunks as $chunk)
{
$chunkEncrypted = '';
$valid = openssl_public_encrypt($chunk, $chunkEncrypted, $key, OPENSSL_PKCS1_PADDING);
if($valid === false){
$encrypted = '';
break; //also you can return and error. If too big this will be false
} else {
$encrypted .= $chunkEncrypted;
}
}
$output = base64_encode($encrypted); //encoding the whole binary String as MIME base 64
echo $output;
Click here for a large json sample ready formatted to replace the following line in the above sample, to test chunking, as the $output json above is too small for chunking to take effect.
$output = json_encode(array('see'=>'me'));
Explanation of what the code above does
The above code is a modification of this solution which breaks the data into smaller chunks (100 bytes per chunk) and encrypts them using a public key in pem format.
Objective
I am looking at encrypting larger than a few bytes for more secure transit of data, and found that encrypting/decrypting using certificates is the best route to go.
The intent is to encrypt data in php (using the private key) which would then be received in an application written in C# and decrypted (using the public key).
C# - The road so far
Following is my attempt at decrypting in c# :
Usage :
// location of private certificate
string key = #"C:\path\to\private.txt";
// output from php script (encrypted)
string encrypted = "Bdm4s7aw.....Pvlzg=";
// decrypt and store decrypted string
string decrypted = crypt.decrypt( encrypted, key );
Class :
public static string decrypt(string encrypted, string privateKey) {
try {
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo( DecodePkcs8PrivateKey( File.ReadAllText( privateKey ) ) );
return Encoding.UTF8.GetString( rsa.Decrypt( Convert.FromBase64String( encrypted ), false ) );
} catch (CryptographicException ce) {
return ce.Message;
} catch (FormatException fe) {
return fe.Message;
} catch (IOException ie) {
return ie.Message;
} catch (Exception e) {
return e.Message;
}
}
The other methods this depends on (harvested from opensslkey.cs )
//-------- Get the binary PKCS #8 PRIVATE key --------
private static byte[] DecodePkcs8PrivateKey( string instr ) {
const string pemp8header = "-----BEGIN PRIVATE KEY-----";
const string pemp8footer = "-----END PRIVATE KEY-----";
string pemstr = instr.Trim();
byte[] binkey;
if ( !pemstr.StartsWith( pemp8header ) || !pemstr.EndsWith( pemp8footer ) )
return null;
StringBuilder sb = new StringBuilder( pemstr );
sb.Replace( pemp8header, "" ); //remove headers/footers, if present
sb.Replace( pemp8footer, "" );
string pubstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try {
binkey = Convert.FromBase64String( pubstr );
} catch ( FormatException ) { //if can't b64 decode, data is not valid
return null;
}
return binkey;
}
//------- Parses binary asn.1 PKCS #8 PrivateKeyInfo; returns RSACryptoServiceProvider ---
private static RSACryptoServiceProvider DecodePrivateKeyInfo( byte[] pkcs8 ) {
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
// this byte[] includes the sequence byte and terminal encoded null
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
// --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
MemoryStream mem = new MemoryStream( pkcs8 );
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader( mem ); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try {
twobytes = binr.ReadUInt16();
if ( twobytes == 0x8130 ) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if ( twobytes == 0x8230 )
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if ( bt != 0x02 )
return null;
twobytes = binr.ReadUInt16();
if ( twobytes != 0x0001 )
return null;
seq = binr.ReadBytes( 15 ); //read the Sequence OID
if ( !CompareBytearrays( seq, SeqOID ) ) //make sure Sequence for OID is correct
return null;
bt = binr.ReadByte();
if ( bt != 0x04 ) //expect an Octet string
return null;
bt = binr.ReadByte(); //read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count
if ( bt == 0x81 )
binr.ReadByte();
else
if ( bt == 0x82 )
binr.ReadUInt16();
//------ at this stage, the remaining sequence should be the RSA private key
byte[] rsaprivkey = binr.ReadBytes( (int)( lenstream - mem.Position ) );
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey( rsaprivkey );
return rsacsp;
} catch ( Exception ) {
return null;
} finally { binr.Close(); }
}
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider ---
private static RSACryptoServiceProvider DecodeRSAPrivateKey( byte[] privkey ) {
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream( privkey );
BinaryReader binr = new BinaryReader( mem ); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try {
twobytes = binr.ReadUInt16();
if ( twobytes == 0x8130 ) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if ( twobytes == 0x8230 )
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if ( twobytes != 0x0102 ) //version number
return null;
bt = binr.ReadByte();
if ( bt != 0x00 )
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize( binr );
MODULUS = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
E = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
D = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
P = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
Q = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
DP = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
DQ = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
IQ = binr.ReadBytes( elems );
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters( RSAparams );
return RSA;
} catch ( Exception ) {
return null;
} finally { binr.Close(); }
}
private static int GetIntegerSize( BinaryReader binr ) {
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if ( bt != 0x02 ) //expect integer
return 0;
bt = binr.ReadByte();
if ( bt == 0x81 )
count = binr.ReadByte(); // data size in next byte
else
if ( bt == 0x82 ) {
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32( modint, 0 );
} else {
count = bt; // we already have the data size
}
while ( binr.ReadByte() == 0x00 ) { //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek( -1, SeekOrigin.Current ); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
private static bool CompareBytearrays( byte[] a, byte[] b ) {
if ( a.Length != b.Length )
return false;
int i = 0;
foreach ( byte c in a ) {
if ( c != b[i] )
return false;
i++;
}
return true;
}
This is all functional now, however it still doesn't incorporate chunking in the decryption process.
What must I do to read these blocks in, as larger files will definitely be larger than the original unencrypted data.
My previous attempt was to try something like the following code, but this seems flawed as it is always padding 100 bytes (even when the total bytes are less), and the base64 decoding of the json_encode(array('see'=>'me')) using my current public key for encrypting ends up being 512 bytes.
byte[] buffer = new byte[100]; // the number of bytes to decrypt at a time
int bytesReadTotal = 0;
int bytesRead = 0;
string decrypted = "";
byte[] decryptedBytes;
using ( Stream stream = new MemoryStream( data ) ) {
while ( ( bytesRead = await stream.ReadAsync( buffer, bytesReadTotal, 100 ) ) > 0 ) {
decryptedBytes = rsa.Decrypt( buffer, false );
bytesReadTotal = bytesReadTotal + bytesRead;
decrypted = decrypted + Encoding.UTF8.GetString( decryptedBytes );
}
}
return decrypted;
For your convenience, I put up a php script to generate a public and private key to test with on tehplayground.com.
After an extensive chat with the author of the question it seems that there are two (main) issues in the code that stopped it from working:
The public key wasn't read as the code from this stackoverflow solution actually doesn't create a binary public key but a certificate. For this the X509Certificate constructor can be used followed by GetPublicKey. The method in the stackoverflow solution should have been named differently. This was later changed to a private key (as decryption using a public key doesn't provide confidentiality).
The encrypted chunks were thought to be 100 bytes in size, while the key size was 4096 bits (512 bytes). However RSA (as specified in PKCS#1 v2.1 for PKCS#1 v1.5 padding) always encrypts to exactly the RSA key size (modulus size) in bytes. So the input for decryption should be chunks of 512 bytes as well. The output however will be 100 bytes if that was encrypted (in the PHP code).
To make this work, a small modification was necessary to loop though the base64 decoded bytes of the encrypted data in chunks calculated based on KeySize / 8 (where 8 is how many bits in a byte as KeySize is an int value representing how many bytes each block is).
public static async Task<string> decrypt(string encrypted, string privateKey) {
// read private certificate into RSACryptoServiceProvider from file
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo( DecodePkcs8PrivateKey( File.ReadAllText( privateKey ) ) );
// decode base64 to bytes
byte[] encryptedBytes = Convert.FromBase64String( encrypted );
int bufferSize = (int)(rsa.KeySize / 8);
// initialize byte buffer based on certificate block size
byte[] buffer = new byte[bufferSize]; // the number of bytes to decrypt at a time
int bytesReadTotal = 0; int bytesRead = 0;
string decrypted = ""; byte[] decryptedBytes;
// convert byte array to stream
using ( Stream stream = new MemoryStream( encryptedBytes ) ) {
// loop through stream for each block of 'bufferSize'
while ( ( bytesRead = await stream.ReadAsync( buffer, bytesReadTotal, bufferSize ) ) > 0 ) {
// decrypt this chunk
decryptedBytes = rsa.Decrypt( buffer, false );
// account for bytes read & decrypted
bytesReadTotal = bytesReadTotal + bytesRead;
// append decrypted data as string for return
decrypted = decrypted + Encoding.UTF8.GetString( decryptedBytes );
}
}
return decrypted;
}
Security notes:
PKCS#1 v1.5 padding is vulnerable to padding oracle attacks, better make sure that you don't allow those, especially in transport protocols (or use the newer OAEP padding instead);
trust your public key before using it, or man-in-the-middle attacks may apply.
Hi I am using code from this link, Can you let me know why signature is verify is not working?
Java signer is using BouncyCastleProvider with SHA1withRSA, here is dotnet verify code....
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string pubkey = #"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMf54mcK3EYJn9tT9BhRoTX+8AkqojIyeSfog9ncYEye0VXyBULGg2lAQsDRt8lZsvPioORZW7eB6IKawshoWUsCAwEAAQ==";
String signature = "770bb2610bf6b2602ce2b3ad8489054f4ed59c9b0c9299327f76ecbc60a8bb9a725cfae901fc189d4bafcf73a2f4aed8dffe9842f7b6196ddfcd040c7271c7ca";
String signData = "C2:AE:D6:2B:DF:A4";
byte[] expectedSig = System.Convert.FromBase64String(signature);
byte[] baKey = System.Convert.FromBase64String(pubkey);
byte[] data = Encoding.UTF8.GetBytes(signData);
//Console.WriteLine(p.VerifyData(data, new SHA1CryptoServiceProvider(), expectedSig));
/* Init alg */
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
signer.Init(false, DecodeX509PublicKey2(baKey));
/* Calculate the signature and see if it matches */
signer.BlockUpdate(data, 0, data.Length);
Console.WriteLine(signer.VerifySignature(expectedSig));
Console.In.ReadLine();
}
public static RsaKeyParameters DecodeX509PublicKey2(byte[] x509key)
{
byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509key);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30)
ReadASNLength(reader); //skip the size
else
return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30)
identifierSize = ReadASNLength(reader);
else
return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
return null;
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03) //is the next element a bit string?
{
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30)
{
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02) //is it an integer?
{
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00) //strip off the first byte if it's 0
{
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
Array.Reverse(modulus); //convert to big-endian
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
Array.Reverse(exponent); //convert to big-endian
//RSAParameters RSAKeyInfo = new RSAParameters();
//RSAKeyInfo.Modulus = modulus;
//RSAKeyInfo.Exponent = exponent;
return MakeKey(BitConverter.ToString(modulus).Replace("-", string.Empty), BitConverter.ToString(exponent).Replace("-", string.Empty), false);
}
}
}
}
return null;
}
public static RsaKeyParameters MakeKey(String modulusHexString, String exponentHexString, bool isPrivateKey)
{
var modulus = new Org.BouncyCastle.Math.BigInteger(modulusHexString, 16);
var exponent = new Org.BouncyCastle.Math.BigInteger(exponentHexString, 16);
return new RsaKeyParameters(isPrivateKey, modulus, exponent);
}
public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
{
byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509key);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30)
ReadASNLength(reader); //skip the size
else
return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30)
identifierSize = ReadASNLength(reader);
else
return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
return null;
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03) //is the next element a bit string?
{
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30)
{
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02) //is it an integer?
{
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00) //strip off the first byte if it's 0
{
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
Array.Reverse(modulus); //convert to big-endian
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
Array.Reverse(exponent); //convert to big-endian
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
}
}
}
return null;
}
public static int ReadASNLength(BinaryReader reader)
{
//Note: this method only reads lengths up to 4 bytes long as
//this is satisfactory for the majority of situations.
int length = reader.ReadByte();
if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte
{
int count = length & 0x0000000f;
byte[] lengthBytes = new byte[4];
reader.Read(lengthBytes, 4 - count, count);
Array.Reverse(lengthBytes); //
length = BitConverter.ToInt32(lengthBytes, 0);
}
return length;
}
}
}
Java code used to sign signature data:
private static final java.security.Signature signer;
static final String transformation = "RSA/ECB/PKCS1Padding";
static {
try {
signer = java.security.Signature.getInstance("SHA1withRSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
static String sign(String clearText) {
String signed = null;
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] data = clearText.getBytes("UTF-8");
signer.initSign(getPrivateKey());
signer.update(data);
byte[] digitalSignature = signer.sign();
//--toHex
signed = org.apache.commons.codec.binary.Hex.encodeHexString(digitalSignature);
} catch (Exception e) {
e.printStackTrace();
}
return signed;
}
KeyPair generateKeyPair() {
KeyPair kp = null;
// Generate a key-pair
KeyPairGenerator kpg;
SecureRandom secureRandom;
try {
kpg = KeyPairGenerator.getInstance("RSA");
secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
secureRandom.setSeed(secureRandomSeed);
kpg.initialize(512, secureRandom);
kp = kpg.generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
}
return kp;
}
Here is code in C# that signs and verifies:
static void test3()
{
AsymmetricCipherKeyPair keys = generateNewKeys();
/* Init alg */
ISigner sig = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
sig.Init(true, keys.Private);
/* Get the bytes to be signed from the string */
var bytes = Encoding.UTF8.GetBytes(signData);
/* Calc the signature */
sig.BlockUpdate(bytes, 0, bytes.Length);
byte[] signature = sig.GenerateSignature();
/* Base 64 encode the sig so its 8-bit clean */
var signedString = Convert.ToBase64String(signature);
Console.WriteLine(signedString);
string expectedSignature = signedString;
/* Init alg */
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
signer.Init(false, keys.Public);
/* Get the signature into bytes */
var expectedSig = Convert.FromBase64String(expectedSignature);
/* Get the bytes to be signed from the string */
var msgBytes = Encoding.UTF8.GetBytes(signData);
/* Calculate the signature and see if it matches */
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
/*Verify*/
bool result= signer.VerifySignature(expectedSig);
Console.WriteLine(result);
}
There's a couple problems problems here.
String signature = "770bb ... 1c7ca";
...
byte[] expectedSig = System.Convert.FromBase64String(signature);
You're Base64 decoding the signature, but it's not Base64 encoded, it's Hex encoded.
The second problem is in the DecodeX509PublicKey methods (which admittedly is my mistake because I provided this code in another answer.) The specific problem lines are
Array.Reverse(modulus); //convert to big-endian
and
Array.Reverse(exponent); //convert to big-endian
I repeatedly read that the ASN.1 and the .Net API use opposite endieness for their keys, and so I was under the impression that the endieness needed to be reversed to account for this. (I really should have done a test like your signature verification to be sure, rather than just looking at the key values in memory >.<) Regardless, remove these lines, fix the encoding problem, and your signature will verify properly (successfully tested using your sample data as well as my own).
Also, this line in your sign method isn't quite right:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
By the time you get to that point in the code, the signer object has already been instantiated using the default provider. Also, you don't need to be adding the Bouncy Castle provider each time you want to sign some data, it will only actually add the provider the first time you make this call and will ignore it for all subsequent calls.
Further, the signer object is declaired static, but your usage of it is not thread safe.
What you more likely want to do is add the provider in the static block and then instantiate the signer explicitly using the Bouncy Castle provider. If you don't explicitly specify Bouncy Castle as the provider (or add Bouncy Castle as the highest priority using insertProviderAt), the default provider will be used instead.
static {
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (Exception e) {
e.printStackTrace();
}
}
...
String signed = null;
try {
java.security.Signature signer = java.security.Signature.getInstance("SHA1withRSA", "BC");
byte[] data = clearText.getBytes("UTF-8");
signer.initSign(getPrivateKey());
...
I have a private key which looks like this:
-----BEGIN RSA PRIVATE KEY-----
Some private key data
-----END RSA PRIVA
I need to use this key in my C# project, but I couldn't find any example how to use key in this format. Thanks
step 1 get "Some private key data" content.remove -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----, Removes all line symbols("\n");
step 2. parse key to RSA.
private RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey)
{
var privateKeyBits = System.Convert.FromBase64String(privateKey);
var RSA = new RSACryptoServiceProvider();
var RSAparams = new RSAParameters();
using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
{
byte bt = 0;
ushort twobytes = 0;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
else
throw new Exception("Unexpected value read binr.ReadUInt16()");
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102)
throw new Exception("Unexpected version");
bt = binr.ReadByte();
if (bt != 0x00)
throw new Exception("Unexpected value read binr.ReadByte()");
RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.D = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.P = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
}
RSA.ImportParameters(RSAparams);
return RSA;
}
private int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02)
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte();
else
if (bt == 0x82)
{
highbyte = binr.ReadByte();
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt;
}
while (binr.ReadByte() == 0x00)
{
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
All major .NET/C# Cryptography libraries (like BouncyCastle, or SecureBlackbox [commercial]) should support this format, as well as operations with loaded key (encryption/decryption/signing/verification).
Though an older post I though I'd glue my own answer to this question as I bumped into the same challenge earlier this year.
I wrote a library for the handling of PEM keys, encryption, decryption, signing and signature verification.
There's an complete example solution attached to it (Load-Encrypt-Decrypt-Save), so you should be able to be up running in no-time.
https://github.com/jrnker/CSharp-easy-RSA-PEM
Cheers,
Christoffer
The following article I wrote about digital signature gives a solution in c# (no additional library needed). The code shows how a RSA private key in PEM format can be converted to the XML format used by the .NET RSACryptoServiceProvider Class.
With Atlando Crypto Currency Geo Service your identity is stored in your browser after registration. At each request a contract with us is signed and encrypted by this identity with your private key. This article explains how it works.
Code below gives an implementation in C# (RSACryptoServiceProvider Class) of the authentication process through comparison of the original and a signed version. The modulus comes from the RSA public key in PEM format (exponent AQAB).
private static bool Verify(string original, string signature, string modulus)
{
SHA256Managed sha = new SHA256Managed();
byte[] bytes = Encoding.UTF8.GetBytes(original);
byte[] hash = sha.ComputeHash(bytes);
sha.Clear();
byte[] signed = new byte[signature.Length/2];
for (int i = 0; i < signature.Length; i += 2)
{
signed[i/2] = Convert.ToByte(Convert.ToInt32(signature.Substring(i, 2), 16));
}
string key = "<RSAKeyValue><Modulus>" + modulus + "</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(key);
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
RSADeformatter.SetHashAlgorithm("SHA256");
return RSADeformatter.VerifySignature(hash, signed);
}
}
public static string Modulus(string pem)
{
byte[] x509der = Convert.FromBase64String(pem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", ""));
byte[] seqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509der);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30) ReadASNLength(reader); //skip the size
else return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30) identifierSize = ReadASNLength(reader);
else return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(seqOID) == false) return null; //is the object identifier rsaEncryption PKCS#1?
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03) //is the next element a bit string?
{
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30)
{
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02) //is it an integer?
{
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00) //strip off the first byte if it's 0
{
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
rsa.ImportParameters(RSAKeyInfo);
return rsa.ToXmlString(false).Replace("<RSAKeyValue><Modulus>", "").Replace("</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>", "");
}
}
}
}
return null;
}
public static int ReadASNLength(BinaryReader reader)
{//Note: this method only reads lengths up to 4 bytes long as this is satisfactory for the majority of situations.
int length = reader.ReadByte();
if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte
{
int count = length & 0x0000000f;
byte[] lengthBytes = new byte[4];
reader.Read(lengthBytes, 4 - count, count);
Array.Reverse(lengthBytes); //
length = BitConverter.ToInt32(lengthBytes, 0);
}
return length;
}
First, you need to transform the private key to the form of RSA parameters using Bouncy Castle library. Then you need to pass the RSA parameters to the RSA algorithm as the private key. Lastly, you use the JWT library to encode and sign the token.
public string GenerateJWTToken(string rsaPrivateKey)
{
var rsaParams = GetRsaParameters(rsaPrivateKey);
var encoder = GetRS256JWTEncoder(rsaParams);
// create the payload according to your need
var payload = new Dictionary<string, object>
{
{ "iss", ""},
{ "sub", "" },
// and other key-values
};
var token = encoder.Encode(payload, new byte[0]);
return token;
}
private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
{
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(rsaParams);
var algorithm = new RS256Algorithm(csp, csp);
var serializer = new JsonNetSerializer();
var urlEncoder = new JwtBase64UrlEncoder();
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
return encoder;
}
private static RSAParameters GetRsaParameters(string rsaPrivateKey)
{
var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
using (var ms = new MemoryStream(byteArray))
{
using (var sr = new StreamReader(ms))
{
// use Bouncy Castle to convert the private key to RSA parameters
var pemReader = new PemReader(sr);
var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
}
}
}
ps: the RSA private key should have the following format:
-----BEGIN RSA PRIVATE KEY-----
{base64 formatted value}
-----END RSA PRIVATE KEY-----
I need to load the following RSA public key from a file for use with the RSACryptoServiceProvider class. How can I do this?
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm
byXPPN8zwnS5/XXXXXXXXXXXX
-----END PUBLIC KEY-----
This code works with my pub key: http://www.jensign.com/opensslkey/
Here is the code I am using
static string RSA(string input)
{
RSACryptoServiceProvider rsa = DecodeX509PublicKey(Convert.FromBase64String(GetKey()));
return (Convert.ToBase64String(rsa.Encrypt(Encoding.ASCII.GetBytes(input), false)));
}
static string GetKey()
{
return File.ReadAllText("master.pub").Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "");
//.Replace("\n", "");
}
private static bool CompareBytearrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
{
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
// --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
MemoryStream mem = new MemoryStream(x509key);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8203)
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if (bt != 0x00) //expect null byte next
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
byte lowbyte = 0x00;
byte highbyte = 0x00;
if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
else if (twobytes == 0x8202)
{
highbyte = binr.ReadByte(); //advance 2 bytes
lowbyte = binr.ReadByte();
}
else
return null;
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
int modsize = BitConverter.ToInt32(modint, 0);
byte firstbyte = binr.ReadByte();
binr.BaseStream.Seek(-1, SeekOrigin.Current);
if (firstbyte == 0x00)
{ //if first byte (highest order) of modulus is zero, don't include it
binr.ReadByte(); //skip this null byte
modsize -= 1; //reduce modulus buffer size by 1
}
byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
return null;
int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
byte[] exponent = binr.ReadBytes(expbytes);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
Just call the 'RSA' method with the text you want to encrypt and you're done.
You can create an RSACryptoServiceProvider from a PEM file using the following class (GetRSAProviderFromPemFile method).
Warning:
Don't just copy code from StackOverflow without verification! Especially not crypto code! This code has bugs (see comments). You may want to write and run tests before using this in production (if you really have no better option). I refuse to edit the code to fix it, as it would be just as unreliable as before without tests and an active maintainer.
Source:
This code seems to be taken from opensslkey from this site.
Copyright (c) 2000 JavaScience Consulting, Michel Gallant.
The original package was released under a BSD-like license, so it is probably OK to use it (but you may want to double-check). There is also a NuGet package by the same author.
Here is the copy-pasted source code originally posted to this answer:
RSACryptoServiceProvider provider = PemKeyUtils.GetRSAProviderFromPemFile( #"public_key.pem" );
public class PemKeyUtils
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
const String pempubheader = "-----BEGIN PUBLIC KEY-----";
const String pempubfooter = "-----END PUBLIC KEY-----";
const String pemp8header = "-----BEGIN PRIVATE KEY-----";
const String pemp8footer = "-----END PRIVATE KEY-----";
const String pemp8encheader = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
const String pemp8encfooter = "-----END ENCRYPTED PRIVATE KEY-----";
static bool verbose = false;
public static RSACryptoServiceProvider GetRSAProviderFromPemFile( String pemfile )
{
bool isPrivateKeyFile = true;
string pemstr = File.ReadAllText( pemfile ).Trim();
if (pemstr.StartsWith( pempubheader ) && pemstr.EndsWith( pempubfooter ))
isPrivateKeyFile = false;
byte[] pemkey;
if (isPrivateKeyFile)
pemkey = DecodeOpenSSLPrivateKey( pemstr );
else
pemkey = DecodeOpenSSLPublicKey( pemstr );
if (pemkey == null)
return null;
if (isPrivateKeyFile)
return DecodeRSAPrivateKey( pemkey );
else
return DecodeX509PublicKey( pemkey );
}
//-------- Get the binary RSA PUBLIC key --------
static byte[] DecodeOpenSSLPublicKey( String instr )
{
const String pempubheader = "-----BEGIN PUBLIC KEY-----";
const String pempubfooter = "-----END PUBLIC KEY-----";
String pemstr = instr.Trim();
byte[] binkey;
if (!pemstr.StartsWith( pempubheader ) || !pemstr.EndsWith( pempubfooter ))
return null;
StringBuilder sb = new StringBuilder( pemstr );
sb.Replace( pempubheader, "" ); //remove headers/footers, if present
sb.Replace( pempubfooter, "" );
String pubstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try
{
binkey = Convert.FromBase64String( pubstr );
}
catch (System.FormatException)
{ //if can't b64 decode, data is not valid
return null;
}
return binkey;
}
static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509Key)
{
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
// --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
using (var mem = new MemoryStream(x509Key))
{
using (var binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading
{
try
{
var twobytes = binr.ReadUInt16();
switch (twobytes)
{
case 0x8130:
binr.ReadByte(); //advance 1 byte
break;
case 0x8230:
binr.ReadInt16(); //advance 2 bytes
break;
default:
return null;
}
var seq = binr.ReadBytes(15);
if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8203)
binr.ReadInt16(); //advance 2 bytes
else
return null;
var bt = binr.ReadByte();
if (bt != 0x00) //expect null byte next
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
byte lowbyte = 0x00;
byte highbyte = 0x00;
if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
else if (twobytes == 0x8202)
{
highbyte = binr.ReadByte(); //advance 2 bytes
lowbyte = binr.ReadByte();
}
else
return null;
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
int modsize = BitConverter.ToInt32(modint, 0);
byte firstbyte = binr.ReadByte();
binr.BaseStream.Seek(-1, SeekOrigin.Current);
if (firstbyte == 0x00)
{ //if first byte (highest order) of modulus is zero, don't include it
binr.ReadByte(); //skip this null byte
modsize -= 1; //reduce modulus buffer size by 1
}
byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
return null;
int expbytes = binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
byte[] exponent = binr.ReadBytes(expbytes);
// We don't really need to print anything but if we insist to...
//showBytes("\nExponent", exponent);
//showBytes("\nModulus", modulus);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters rsaKeyInfo = new RSAParameters
{
Modulus = modulus,
Exponent = exponent
};
rsa.ImportParameters(rsaKeyInfo);
return rsa;
}
catch (Exception)
{
return null;
}
}
}
}
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider ---
static RSACryptoServiceProvider DecodeRSAPrivateKey( byte[] privkey )
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream( privkey );
BinaryReader binr = new BinaryReader( mem ); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize( binr );
MODULUS = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
E = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
D = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
P = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
Q = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
DP = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
DQ = binr.ReadBytes( elems );
elems = GetIntegerSize( binr );
IQ = binr.ReadBytes( elems );
Console.WriteLine( "showing components .." );
if (verbose)
{
showBytes( "\nModulus", MODULUS );
showBytes( "\nExponent", E );
showBytes( "\nD", D );
showBytes( "\nP", P );
showBytes( "\nQ", Q );
showBytes( "\nDP", DP );
showBytes( "\nDQ", DQ );
showBytes( "\nIQ", IQ );
}
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters( RSAparams );
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static int GetIntegerSize( BinaryReader binr )
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32( modint, 0 );
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek( -1, SeekOrigin.Current ); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
//----- Get the binary RSA PRIVATE key, decrypting if necessary ----
static byte[] DecodeOpenSSLPrivateKey( String instr )
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
String pemstr = instr.Trim();
byte[] binkey;
if (!pemstr.StartsWith( pemprivheader ) || !pemstr.EndsWith( pemprivfooter ))
return null;
StringBuilder sb = new StringBuilder( pemstr );
sb.Replace( pemprivheader, "" ); //remove headers/footers, if present
sb.Replace( pemprivfooter, "" );
String pvkstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try
{ // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
binkey = Convert.FromBase64String( pvkstr );
return binkey;
}
catch (System.FormatException)
{ //if can't b64 decode, it must be an encrypted private key
//Console.WriteLine("Not an unencrypted OpenSSL PEM private key");
}
StringReader str = new StringReader( pvkstr );
//-------- read PEM encryption info. lines and extract salt -----
if (!str.ReadLine().StartsWith( "Proc-Type: 4,ENCRYPTED" ))
return null;
String saltline = str.ReadLine();
if (!saltline.StartsWith( "DEK-Info: DES-EDE3-CBC," ))
return null;
String saltstr = saltline.Substring( saltline.IndexOf( "," ) + 1 ).Trim();
byte[] salt = new byte[saltstr.Length / 2];
for (int i = 0; i < salt.Length; i++)
salt[i] = Convert.ToByte( saltstr.Substring( i * 2, 2 ), 16 );
if (!(str.ReadLine() == ""))
return null;
//------ remaining b64 data is encrypted RSA key ----
String encryptedstr = str.ReadToEnd();
try
{ //should have b64 encrypted RSA key now
binkey = Convert.FromBase64String( encryptedstr );
}
catch (System.FormatException)
{ // bad b64 data.
return null;
}
//------ Get the 3DES 24 byte key using PDK used by OpenSSL ----
SecureString despswd = GetSecPswd( "Enter password to derive 3DES key==>" );
//Console.Write("\nEnter password to derive 3DES key: ");
//String pswd = Console.ReadLine();
byte[] deskey = GetOpenSSL3deskey( salt, despswd, 1, 2 ); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
if (deskey == null)
return null;
//showBytes("3DES key", deskey) ;
//------ Decrypt the encrypted 3des-encrypted RSA private key ------
byte[] rsakey = DecryptKey( binkey, deskey, salt ); //OpenSSL uses salt value in PEM header also as 3DES IV
if (rsakey != null)
return rsakey; //we have a decrypted RSA private key
else
{
Console.WriteLine( "Failed to decrypt RSA private key; probably wrong password." );
return null;
}
}
// ----- Decrypt the 3DES encrypted RSA private key ----------
static byte[] DecryptKey( byte[] cipherData, byte[] desKey, byte[] IV )
{
MemoryStream memst = new MemoryStream();
TripleDES alg = TripleDES.Create();
alg.Key = desKey;
alg.IV = IV;
try
{
CryptoStream cs = new CryptoStream( memst, alg.CreateDecryptor(), CryptoStreamMode.Write );
cs.Write( cipherData, 0, cipherData.Length );
cs.Close();
}
catch (Exception exc)
{
Console.WriteLine( exc.Message );
return null;
}
byte[] decryptedData = memst.ToArray();
return decryptedData;
}
//----- OpenSSL PBKD uses only one hash cycle (count); miter is number of iterations required to build sufficient bytes ---
static byte[] GetOpenSSL3deskey( byte[] salt, SecureString secpswd, int count, int miter )
{
IntPtr unmanagedPswd = IntPtr.Zero;
int HASHLENGTH = 16; //MD5 bytes
byte[] keymaterial = new byte[HASHLENGTH * miter]; //to store contatenated Mi hashed results
byte[] psbytes = new byte[secpswd.Length];
unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi( secpswd );
Marshal.Copy( unmanagedPswd, psbytes, 0, psbytes.Length );
Marshal.ZeroFreeGlobalAllocAnsi( unmanagedPswd );
//UTF8Encoding utf8 = new UTF8Encoding();
//byte[] psbytes = utf8.GetBytes(pswd);
// --- contatenate salt and pswd bytes into fixed data array ---
byte[] data00 = new byte[psbytes.Length + salt.Length];
Array.Copy( psbytes, data00, psbytes.Length ); //copy the pswd bytes
Array.Copy( salt, 0, data00, psbytes.Length, salt.Length ); //concatenate the salt bytes
// ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ----
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = null;
byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget
for (int j = 0; j < miter; j++)
{
// ---- Now hash consecutively for count times ------
if (j == 0)
result = data00; //initialize
else
{
Array.Copy( result, hashtarget, result.Length );
Array.Copy( data00, 0, hashtarget, result.Length, data00.Length );
result = hashtarget;
//Console.WriteLine("Updated new initial hash target:") ;
//showBytes(result) ;
}
for (int i = 0; i < count; i++)
result = md5.ComputeHash( result );
Array.Copy( result, 0, keymaterial, j * HASHLENGTH, result.Length ); //contatenate to keymaterial
}
//showBytes("Final key material", keymaterial);
byte[] deskey = new byte[24];
Array.Copy( keymaterial, deskey, deskey.Length );
Array.Clear( psbytes, 0, psbytes.Length );
Array.Clear( data00, 0, data00.Length );
Array.Clear( result, 0, result.Length );
Array.Clear( hashtarget, 0, hashtarget.Length );
Array.Clear( keymaterial, 0, keymaterial.Length );
return deskey;
}
static SecureString GetSecPswd( String prompt )
{
SecureString password = new SecureString();
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write( prompt );
Console.ForegroundColor = ConsoleColor.Magenta;
while (true)
{
ConsoleKeyInfo cki = Console.ReadKey( true );
if (cki.Key == ConsoleKey.Enter)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
return password;
}
else if (cki.Key == ConsoleKey.Backspace)
{
// remove the last asterisk from the screen...
if (password.Length > 0)
{
Console.SetCursorPosition( Console.CursorLeft - 1, Console.CursorTop );
Console.Write( " " );
Console.SetCursorPosition( Console.CursorLeft - 1, Console.CursorTop );
password.RemoveAt( password.Length - 1 );
}
}
else if (cki.Key == ConsoleKey.Escape)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
return password;
}
else if (Char.IsLetterOrDigit( cki.KeyChar ) || Char.IsSymbol( cki.KeyChar ))
{
if (password.Length < 20)
{
password.AppendChar( cki.KeyChar );
Console.Write( "*" );
}
else
{
Console.Beep();
}
}
else
{
Console.Beep();
}
}
}
static bool CompareBytearrays( byte[] a, byte[] b )
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
static void showBytes( String info, byte[] data )
{
Console.WriteLine( "{0} [{1} bytes]", info, data.Length );
for (int i = 1; i <= data.Length; i++)
{
Console.Write( "{0:X2} ", data[i - 1] );
if (i % 16 == 0)
Console.WriteLine();
}
Console.WriteLine( "\n\n" );
}
}
If you're talking about an X509 certificate:
FileStream fs = new FileStream("your_cert_file.crt", FileMode.Open);
byte[] certBytes = new byte[fs.Length];
fs.Read(certBytes, 0, (Int32)fs.Length);
fs.Close();
System.Security.Cryptography.X509Certificates.X509Certificate x509cert =
new X509Certificate(certBytes);
Console.WriteLine(x509cert.GetPublicKey());
Console.WriteLine(x509cert.GetPublicKeyString());
EDITED after the comment of #hkproj done in "7/16/2012 15:04:58 Z":
Looking around here I found "Reading PEM RSA Public Key Only using Bouncy Castle".
I guess what you want is this:
using (StreamReader reader = File.OpenText(#"c:\RSA.txt"))
{
Org.BouncyCastle.OpenSsl.PemReader pr =
new Org.BouncyCastle.OpenSsl.PemReader(reader);
Org.BouncyCastle.Utilities.IO.Pem.PemObject po = pr.ReadPemObject();
Console.WriteLine("PemObject, Type: " + po.Type);
Console.WriteLine("PemObject, Length: " + po.Content.Length);
}
However, with your file I get an error: System.IO.IOException : base64 data appears to be truncated.
So changing your file to something like:
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/syEKqEkMtQL0+d
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+izR
KbGMRtur2TYklnyVkjeeHfAggo8vWQmWesnOG55vQYHbOOFoJbk0EkwEr5R/PbKm
byXPPN8zwnS5/XXXXXXXXXXXXZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
-----END PUBLIC KEY-----
The result is:
PemObject, Type: PUBLIC KEY
PemObject, Length: 192
As of .Net 5.0 you can import an RSA public key from a string like so:
var rsaPublicKey = RSA.Create();
rsaPublicKey.ImportFromPem(publicKeyString);
If you don't know how to read a file to a string see
How to read an entire file to a string using C#?
Are you speaking about certificates stored in file?
If you have an object like:
X509Certificate2 certificate;
you can use following code:
RSACryptoServiceProvider rsaprovider =
(RSACryptoServiceProvider)certificate.PublicKey.Key;
and then use the class RSACryptoServiceProvider (see http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx).
To load an X509Certificate2 use its constructor (see http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx).
This program works fine for me:
static void Main(string[] args)
{
try
{
X509Certificate2 certificate =
new X509Certificate2("<PFX Certificate Path", "<Certificate-Password>");
RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)certificate.PublicKey.Key;
}
catch(Exception e)
{
}
}
string private_key = File.ReadAllText(private_key_file).Trim();
Regex regex = new Regex(#"-----(BEGIN|END) (RSA|OPENSSH|ENCRYPTED) PRIVATE KEY-----[\W]*");
string private_key_base64 = regex.Replace(private_key, "");
byte[] bytes_privake_key = Convert.FromBase64String(private_key_base64);
rsa.ImportRSAPrivateKey(new ReadOnlySpan<byte>(bytes_privake_key), out bytesRead);
I know this is a topic with lots of existing questions but I am failing to find any existing answers which cover my exact case.
I need to sign a string (a URL) in some Java code and deliver the string along with the signature to a C# program.
I run the following Java code once to generate a DSA keypair:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
PrivateKey priv = pair.getPrivate();
PublicKey pub = pair.getPublic();
/* save the private key in a file */
byte[] privkey = priv.getEncoded();
FileOutputStream privkeyfos = new FileOutputStream("key.priv");
privkeyfos.write(privkey);
privkeyfos.close();
/* save the public key in a file */
byte[] pubkey = pub.getEncoded();
FileOutputStream pubkeyfos = new FileOutputStream("key.public");
pubkeyfos.write(pubkey);
pubkeyfos.close();
I am then using the following code to generate the signature.
public static String Sign(String keyPath, byte[] data)
{
FileInputStream keyfis = new FileInputStream(new File(keyPath, "key.priv"));
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PrivateKey privKey = keyFactory.generatePrivate(privKeySpec);
Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");
dsa.initSign(privKey);
ByteArrayInputStream in = new ByteArrayInputStream(data);
BufferedInputStream bufin = new BufferedInputStream(in);
byte[] buffer = new byte[1024];
int len;
while ((len = bufin.read(buffer)) >= 0)
{
dsa.update(buffer, 0, len);
}
bufin.close();
byte[] realSig = dsa.sign();
return new String(Base64.encodeBase64(realSig), "UTF-8");
}
In my C# code I have access to the string, the Base64 encoded signature and the "key.public" file from the first step.
Can anyone provide a block of code which combines these elements along with a suitable library to determine whether the string has been tampered with?
I have now solved this with some key input coming from this article: http://www.codeproject.com/KB/security/CryptoInteropSign.aspx
The main verification is done using the following C# function.
private static Boolean isValid(String xiString, String xiSig)
{
AsnKeyParser keyParser = new AsnKeyParser("path/to/key.public");
DSAParameters publicKey = keyParser.ParseDSAPublicKey();
DSACryptoServiceProvider DSA = new DSACryptoServiceProvider();
DSA.ImportParameters(publicKey);
DSASignatureDeformatter DSADeformatter = new DSASignatureDeformatter(DSA);
UTF8Encoding UTF8 = new UTF8Encoding();
byte[] plainBytes = UTF8.GetBytes(xiString);
var sha1 = new SHA1Managed();
var hash = sha1.ComputeHash(plainBytes);
byte[] asn1SigBytes = Convert.FromBase64String(xiSig);
byte[] sigBytes = ConvertToP1363Signature(asn1SigBytes);
Boolean retVal = DSADeformatter.VerifySignature(hash, sigBytes);
return retVal;
}
This relies on two helper methods.
1) AsnKeyParser is a class attached to the linked article. The article offers a C# download from which I used two files: AsnKeyParser.cs and BerDecodeError.cs. I deleted the RSA functions from AsnKeyParser to remove the dependency on the BigInteger file.
This class handles parsing the "key.public" file created by my Java code.
2) A function for converting the 46-48 byte DER encoded signature generated by Java into a DSA signature which C# will accept.
This function is based on code in the comments of the linked article.
private static byte[] ConvertToP1363Signature(byte[] ASN1Sig)
{
AsnParser asn = new AsnParser(ASN1Sig);
asn.NextSequence();
byte[] r = asn.NextInteger();
byte[] s = asn.NextInteger();
// Returned to caller
byte[] p1363Signature = new byte[40];
if (r.Length > 21 || (r.Length == 21 && r[0] != 0))
{
// WTF???
// Reject - signature verification failed
}
else if (r.Length == 21)
{
// r[0] = 0
// r[1]'s high bit *should* be set
Array.Copy(r, 1, p1363Signature, 0, 20);
}
else if (r.Length == 20)
{
// r[0]'s high bit *should not* be set
Array.Copy(r, 0, p1363Signature, 0, 20);
}
else
{
// fewer than 20 bytes
int len = r.Length;
int off = 20 - len;
Array.Copy(r, 0, p1363Signature, off, len);
}
if (s.Length > 21 || (s.Length == 21 && s[0] != 0))
{
// WTF???
// Reject - signature verification failed
}
else if (s.Length == 21)
{
// s[0] = 0
// s[1]'s high bit *should* be set
Array.Copy(s, 1, p1363Signature, 20, 20);
}
else if (s.Length == 20)
{
// s[0]'s high bit *should not* be set
Array.Copy(s, 0, p1363Signature, 20, 20);
}
else
{
// fewer than 20 bytes
int len = s.Length;
int off = 40 - len;
Array.Copy(s, 0, p1363Signature, off, len);
}
return p1363Signature;
}