How to generate a signature based on an XML file? - java

please help me to solve the following:
I have a code on Java
java code bellow:
StringBuilder fullText;
KeyStore p12 = KeyStore.getInstance("PKCS12");
p12.load(new FileInputStream("FileName.p12"), "1234".toCharArray());
Key key = (Key) p12.getKey("1", "1234".toCharArray());
//signing
Signature signer = Signature.getInstance("SHA1withRSA");
signer.initSign((PrivateKey) key);
signer.update(fullText.toString().getBytes());
b`yte[] digitalSignature = signer.sign();
String base64sign = new String(Base64.getEncoder().encode(digitalSignature));
I tried to reproduce it on .Net platform.
I create a code on .NET 3.5 platform. The code on X++ below:
public static boolean Encrypt(str sXmlDoc)
{
boolean bSuccess = false;
System.Security.Cryptography.X509Certificates.X509Certificate2 p12;
System.Security.Cryptography.AsymmetricAlgorithm key;
str sBase64Cert;
str sBase64Xml;
str sBase64Sign;
str sTmp;
System.Byte[] byteArray;
System.Security.Cryptography.Xml.Signature signer;
System.Exception ex;
str sKeyPublic;
System.Byte[] keyPublic;
System.Int32 myInt32;
int myInt;
System.Byte[] byteTmp, byteTmp2;
System.Text.ASCIIEncoding txtEncoder;
System.Security.Cryptography.Xml.KeyInfo keyInfo;
System.Security.Cryptography.Xml.SignedXml signedXml;
System.Xml.XmlDocument xmlDocument;
System.Xml.XmlElement xmlElement;
System.Security.Cryptography.Xml.SignedInfo signedInfo;
System.Security.Cryptography.Xml.Reference reference;
System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform env;
System.Security.Cryptography.Xml.RSAKeyValue rsaKeyValue;
System.Security.Cryptography.RSA rsaKey;
try
{
p12 = new System.Security.Cryptography.X509Certificates.X509Certificate2("fileName.p12", "pass");
if (p12)
{
//Signature
//TEST
if (p12.get_HasPrivateKey())
{
key = p12.get_PrivateKey();
rsaKey = p12.get_PrivateKey();
xmlDocument = new System.Xml.XmlDocument();
xmlDocument.set_PreserveWhitespace(true); //Allow white spaces
xmlDocument.LoadXml(sXmlDoc);
signedXml = new System.Security.Cryptography.Xml.SignedXml(xmlDocument);
signedXml.set_SigningKey(key);
keyInfo = new System.Security.Cryptography.Xml.KeyInfo();
rsaKeyValue = new System.Security.Cryptography.Xml.RSAKeyValue(rsaKey);
keyInfo.AddClause(rsaKeyValue);
signedXml.set_KeyInfo(keyInfo);
// Create a reference to be signed.
//System.Security.Cryptography.Xml.Reference reference;
reference = new System.Security.Cryptography.Xml.Reference();
reference.set_Uri("");
// Add an enveloped transformation to the reference.
env = new System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
signedXml.set_KeyInfo(keyInfo);
signedXml.ComputeSignature();
xmlElement = signedXml.GetXml();
signer = new System.Security.Cryptography.Xml.Signature();
signer = signedXml.get_Signature();
signedInfo = new System.Security.Cryptography.Xml.SignedInfo();
signedInfo = signer.get_SignedInfo();
byteTmp = signer.get_SignatureValue();
sTmp = System.Convert::ToBase64String(byteTmp);
sBase64Sign = "<signature>"+sTmp+"</signature>";
info(sBase64Sign);
}
}
}
catch (Exception::CLRError)
{
ex = ClrInterop::getLastException();
if (ex != null)
{
ex = ex.get_InnerException();
if (ex != null)
{
error(ex.ToString());
}
}
}
return bSuccess;
}
But the result differs than I got on java. I opened a p12 key. I signed an XML sting and got the signature for this XML, but got the wrong string. What did I do wrong?

The Java code doesn't say anything about XML, so I don't know if you learned anything about porting code, but if you do use XML in C# then it's going to fail.
In short, you need to use the RSA functions directly. Starting with RSA.Create() might make a lot of sense in most languages. However, .NET is rather certificate / key based (you perform operations on the key rather than using the key, for better or worse, and private keys are considered part of the certificate that they belong to). So using a constructor to read PKCS#12 is probably a better starting point.
That's all for this little tutorial. I don't believe for a second that you thought that your code would be a correct port, so start over. Happy programming.
EDIT: Oh, one last hint: SHA1withRSA is RSA using PKCS#1 v1.5 padding for signature generation, using SHA-1 as underlying hash function (which of course means it is SHATTERED and more).

The best way to sign an XML on .NET is by using a Bouncy Castle library. I hope anybody in one day will solve it on .NET framework, without using an external library, but the solution has found via using BounsyCastle.
This code on C# used BouncyCastle.
string item = string.Empty;
Pkcs12Store p12 = new Pkcs12Store();
p12.Load(_p12, _p12_psw.ToCharArray());
string alias = "";
foreach (string al in p12.Aliases)
{
if (p12.IsKeyEntry(al) && p12.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
}
//signature
var data = Encoding.UTF8.GetBytes(xmlDoc);
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
signer.Init(true,p12.GetKey(alias).Key);
signer.BlockUpdate(data, 0, data.Length);
var sign = signer.GenerateSignature();
string base64Sign = Convert.ToBase64String(sign);
item = "<signature>"+base64Sign+"</signature>", base64Sign);

Related

ECDSA signature generation KeyPair Java to C# - JcaPEMKeyConverter()

I have been converting over some code from a Java Android app to C# using Xamarin and I have come across a problem when trying to generate a signature using a certain snippet of BouncyCastle code.
Is there a replacement function in C# for the line of code
"pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);" ??
This is the Java code:
// Generating the signature
Signature signature = Signature.getInstance("SHA256withECDSA");
Reader rdr = new StringReader("privatekeygoeshere");
Object parsed = new PEMParser(rdr).readObject();
KeyPair pair;
pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);
PrivateKey signingKey = pair.getPrivate();
signature.initSign(signingKey);
signature.update(nonceData1);
signature.update(nonceData2);
signature.update(collectorID);
signature.update(publicKeyCompressed);
byte[] signedData = signature.sign();
I have found another way to read the private key and create a KeyPair. However, the private key is stored as a AsymmetricCipherKeyPair which I cannot add into the signature.InitSign() function as this requires an IPrivateKey.
The Different ways that I have tried to create a signature do not allow me to update other byte array data to the signature generation like the Java code, this doesn't work for me so I am really stuck.
I am also open to any ideas of signature generation.
Example of this here:
AsymmetricKeyParameter signingKey;
AsymmetricCipherKeyPair keyPair = null;
using (var textReader = new System.IO.StringReader("privatekeygoeshere"))
{
// Only a private key
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
signingKey = keyPair.Private;
}
I managed to come up with a solution for my problem using a string reader and looping through each array using the Update() command. This works well for me however, if any one can find a better way of doing this... Please comment below.
AsymmetricKeyParameter signingKey;
using (var textReader = new System.IO.StringReader(LONG_TERM_PRIVATE_KEY))
{
// Only a private key
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
signingKey = keyPair.Private;
}
var signer = SignerUtilities.GetSigner("SHA256withECDSA");
signer.Init(true, signingKey);
foreach (byte b in terminalNonce)
{
signer.Update(b);
}
foreach (byte b in mobileDeviceNonce)
{
signer.Update(b);
}
foreach (byte b in COLLECTOR_ID)
{
signer.Update(b);
}
foreach (byte b in terminalEphemeralPublicKeyCompressed)
{
signer.Update(b);
}
var signed = signer.GenerateSignature();

RSA SHA256 file signing in C#.Net and verification on Java [duplicate]

I need to sign some data with one private key using Algorithm SHA1RSA ,Rsa Key length 2048 with 64 base encoding.My code is like this
string sPayload = "";
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create("URI");
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.Method = WebRequestMethods.Http.Post;
using (StreamWriter streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
sPayload = "{\"id\":\"14123213213\"," +
"\"uid\":\"teller\"," +
"\"pwd\":\"abc123\"," +
"\"apiKey\":\"2343243\"," +
"\"agentRefNo\":\"234324324\"}";
httpWebRequest.Headers.Add("SIGNATURE", Convert.ToBase64String(new System.Security.Cryptography.SHA1CryptoServiceProvider().ComputeHash(Encoding.ASCII.GetBytes(sPayload))));
streamWriter.Write(sPayload);
streamWriter.Flush();
streamWriter.Close();
}
System.Net.ServicePointManager.Expect100Continue = false;
HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
string result = streamReader.ReadToEnd();
}
In the Header name Signature i need to pass the signed data(sPayload) using the private key.But using above code an error is getting as "invalid signature" from third party and i'am not sure whether the Encryption part is correct or not.
httpWebRequest.Headers.Add("SIGNATURE", Convert.ToBase64String(new System.Security.Cryptography.SHA1CryptoServiceProvider().ComputeHash(Encoding.ASCII.GetBytes(sPayload))));
Third party had provide one certificate(cert,sha1) and key.should i refer that to the code?
You have computed the SHA-1 hash of sPayload, not the RSA-SHA1 signature.
If you have an X509Certificate2:
using (RSA rsa = cert.GetRSAPrivateKey())
{
return rsa.SignData(sPayload, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
If you already have a raw RSA key then just leave off the using statement.
If you have to compute the hash of sPayload for some other reason you can do it like
byte[] hash;
byte[] signature;
using (HashAlgorithm hasher = SHA1.Create())
using (RSA rsa = cert.GetRSAPrivateKey())
{
hash = hasher.ComputeHash(sPayload);
signature = rsa.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
SignHash still requires the HashAlgorithmName value because the algorithm identifier is embedded within the signature.

How to Create Digital Signature and Verify it in C# using PKCS 7 and SHA algorithm

I am trying to digitally sign xml document and verify the signature with the original xml file with public key and signed document. I have a java code for reference. I need to convert java code to C# where I have java code like this:
certList = new ArrayList<X509Certificate>();
certList.add(signerCert);
certStore = new JcaCertStore(certList);
signedDataGenerator = new CMSSignedDataGenerator();
ContentSigner sha2Signer = new JcaContentSignerBuilder("SHA512with" + privateKey.getAlgorithm()).build(privateKey);
ignedDataGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).setDirectSignature(true).build(sha2Signer, signerCert));
signedDataGenerator.addCertificates(certStore);
CMSSignedData sigData = signedDataGenerator.generate(new CMSProcessableFile(inputXmlFile), false);
signedBytes = sigData.getEncoded();
I have converted java code to C# like this:
X509Store my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
// Find the certificate we’ll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// We found it.
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("oppose no valid application was found");
}
// Hash the data
SHA512Managed sha1 = new SHA512Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
I am trying to convert it since two days, It is generating sign byte array but not been able to verify. While verifying it is throwing bad hash\r\n error I shall be highly grateful for any assistance. I know I am somewhere wrong in converting the java code to C#. I am able to verify the code but not been able to sign the document
I have generated Signature using System.Security.Cryptography.Pkcs library like this
public static byte[] Sign(byte[] data, X509Certificate2 certificate)
{
if (data == null)
throw new ArgumentNullException("data");
if (certificate == null)
throw new ArgumentNullException("certificate");
// setup the data to sign
ContentInfo content = new ContentInfo(data);
SignedCms signedCms = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate);
// create the signature
signedCms.ComputeSignature(signer);
return signedCms.Encode();
}
and verify the signature like this
private static bool VerifySignatures(FileInfo contentFile, Stream signedDataStream)
{
CmsProcessable signedContent = null;
CmsSignedData cmsSignedData = null;
Org.BouncyCastle.X509.Store.IX509Store store = null;
ICollection signers = null;
bool verifiedStatus = false;
try
{
//Org.BouncyCastle.Security.addProvider(new BouncyCastleProvider());
signedContent = new CmsProcessableFile(contentFile);
cmsSignedData = new CmsSignedData(signedContent, signedDataStream);
store = cmsSignedData.GetCertificates("Collection");//.getCertificates();
IX509Store certStore = cmsSignedData.GetCertificates("Collection");
signers = cmsSignedData.GetSignerInfos().GetSigners();
foreach (var item in signers)
{
SignerInformation signer = (SignerInformation)item;
var certCollection = certStore.GetMatches(signer.SignerID);
IEnumerator iter = certCollection.GetEnumerator();
iter.MoveNext();
var cert = (Org.BouncyCastle.X509.X509Certificate)iter.Current;
verifiedStatus = signer.Verify(cert.GetPublicKey());
}
}
catch (Exception e)
{
throw e;
}
return verifiedStatus;
}
It is working for me

Digital signature verification error for accessing webservice

I need to send a digital signature as one the parameters to an external webservice.
The steps to create as per documentation is :
Create a DOM representation of the XML data
Create a canonicalised representation of the DOM data. The canonicalised representation should follow the form described in http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments;
Create the signature RSA encryption of the SHA1 digest of the canonicalised representation. The signature is encrypted using the Participant‟s private key;
Encode the binary signature into a base64-encoded string
Place the Signature string in the SOAP message ReqDigSig element;
Store the XML data as it may be needed later to support Non-Repudiation of the submitted XML data.
I have used the following code:
private string SignXML(X509Certificate2 Cert, string data)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = false;
xmlDoc.LoadXml(data);
XmlDsigC14NWithCommentsTransform t = new XmlDsigC14NWithCommentsTransform();
t.LoadInput(xmlDoc);
Stream s = (Stream)t.GetOutput(typeof(Stream));
SHA1 sha1 = SHA1.Create();
byte[] hash = sha1.ComputeHash(s);
RSACryptoServiceProvider rsaKey =
(RSACryptoServiceProvider)Cert.PrivateKey;
RSAParameters rsaPrivateParams = rsaKey.ExportParameters(true);
rsaKey.ImportParameters(rsaPrivateParams);
byte[] signature = rsaKey.Encrypt(hash, false);
return Convert.ToBase64String(signature);
}
But the response from the webservice says digital signature verification error.
is the code above as per the description in the documentation?
How would i verify if the digital signaature is valid? is there any online tool?
[Post edited as follows]
I have tried using the following. I have verified the signature and it returns true. But fails from the webservice end. What is the difference among signData, signHash and the rsaPKCsignatureformatter classes' createsignature method?
var document = Encoding.UTF8.GetBytes(Reqdata);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = false;
xmlDoc.LoadXml(Reqdata);
//trial with XmlDsigC14NWithCommentsTransform class
XmlDsigC14NWithCommentsTransform t = new XmlDsigC14NWithCommentsTransform();
t.LoadInput(xmlDoc);
Stream s = (Stream)t.GetOutput(typeof(Stream));
//trial with SignedXML class
SignedXml signedXml = new SignedXml(xmlDoc);
signedXml.SignedInfo.CanonicalizationMethod =
SignedXml.XmlDsigC14NWithCommentsTransformUrl;
document = Encoding.UTF8.GetBytes(signedXml.ToString());
byte[] hashedDocument;
using (var sha1 = SHA1.Create())
{
//hashedDocument = sha1.ComputeHash(document);
hashedDocument = sha1.ComputeHash(s);
}
var digitalSignature = new DigitalSignature();
digitalSignature.AssignNewKey();
byte[] signature = digitalSignature.SignData(hashedDocument);
string finalsignature = Convert.ToBase64String(signature) ;
byte[] finalSignveri = Convert.FromBase64String(finalsignature);
bool verified = digitalSignature.VerifySignature(hashedDocument, finalSignveri);
and the digital signature class is as follows:
public class DigitalSignature
{
private RSAParameters publicKey;
private RSAParameters privateKey;
public void AssignNewKey()
{
using (var rsa = new RSACryptoServiceProvider())
{
rsa.PersistKeyInCsp = false;
publicKey = rsa.ExportParameters(false);
privateKey = rsa.ExportParameters(true);
}
}
public byte[] SignData(byte[] hashOfDataToSign)
{
using (var rsa = new RSACryptoServiceProvider())
{
rsa.PersistKeyInCsp = false;
rsa.ImportParameters(privateKey);
var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
rsaFormatter.SetHashAlgorithm("SHA1");
return rsaFormatter.CreateSignature(hashOfDataToSign);
}
}
public bool VerifySignature(byte[] hashOfDataToSign, byte[] signature)
{
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(publicKey);
var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
rsaDeformatter.SetHashAlgorithm("SHA1");
return rsaDeformatter.VerifySignature(hashOfDataToSign, signature);
}
}
}
Could you please let me know what data are you specifically looking for in wsdl? Sorry i am unable to provide the complete wsdl information here.
The error returned is i believe user handled and states "Digital signature verification error"
[Redited]
Please find below a snippet of the code that generates digital signature in java. This was sent by the third party for reference. I am not a java developer. Could someone let me know if the C#code that i have written corresponds to the Java code? if not, please let me know where i am going wrong.
private static String createDigitalSignature(Key key, byte[] data) {
byte[] signature = null;
try {
// Initialize xml-security library
org.apache.xml.security.Init.init();
// Build DOM document from XML data
DocumentBuilderFactory dfactory = DocumentBuilderFactory
.newInstance();
dfactory.setNamespaceAware(true);
dfactory.setValidating(true);
DocumentBuilder documentBuilder = dfactory.newDocumentBuilder();
// This is to throw away all validation errors
documentBuilder
.setErrorHandler(new org.apache.xml.security.utils.IgnoreAllErrorHandler());
Document doc = documentBuilder
.parse(new ByteArrayInputStream(data));
// Build canonicalized XML from document
Canonicalizer c14n = Canonicalizer
.getInstance("http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
byte[] canonBytes = c14n.canonicalizeSubtree(doc);
// Initialize signing object with SHA1 digest and RSA encryption
Signature rsa = Signature.getInstance("SHA1withRSA");
// Set private key into signing object
rsa.initSign((PrivateKey) key);
// Generate signature
rsa.update(canonBytes);
signature = rsa.sign();
} catch (Exception ex) {
System.out.println("Exception occurred in createDigitalSignature: "
+ ex.toString());
System.exit(-1);
}
// Base64 encode signature
BASE64Encoder b64e = new BASE64Encoder();
String signatureString = b64e.encode(signature);
return signatureString;
}
At step 3, a digital signature is not exactly the same as encrypt a digest. A rsa digital signature with pkcs#1 format concatenates the digestAlgorithm OID(the identifier) with the digest value. So you are generating and invalid signature.
All programming languages have a method to perform digital signature without dealing with digests and ciphers. I am not a C# programmer but I guess you have to use RSACryptoServiceProvider.SignData
Use also VerifyData to verify the signature

ios create certificate request programmatically

I am trying to create certificate request programmatically (that I would send to server) in iOS am OSX without using openSSL. I have managed to create RSA key pair but am failing at doing the rest. I have the code that does exactly what I need but it is written in Java and am wondering if there is someone to help me translate this to objective c.
Here is the Java code:
test.generateKeys(); // generate RSA key pair
PrivateKey privateKey = test.keys.getPrivate();
PublicKey publicKey = test.keys.getPublic();
SecureRandom sr = new SecureRandom();
String token = "123456"; // dummy token
String uuid = "4670ff33-d9f7-4026-957d-25c00e4dec54"; // dummy uuid
// with Bouncy Castle
ContentSigner signGen = new JcaContentSignerBuilder("SHA1withRSA").setSecureRandom(sr).build(privateKey);
X500Principal subject = new X500Principal("O=" + token + ", CN=" + uuid);
PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, publicKey);
PKCS10CertificationRequest request = builder.build(signGen);
String bc = Hex.encodeHexString(request.getEncoded());
System.out.println(PEMtoString(request));
I am not very good in cryptography and the documentation for the crypto layer apple is developing is pretty poor documented so I am a bit lost here. I have came across a lot of similar samples but there is always something missing.
Thx in advance.
In case someone stumbles on the same problem here is the solution using Apples common crypto layer (no openSSL).
https://github.com/ateska/ios-csr
No need for several weeks of coding just a simple include.
SCCSR *sccsr = [[SCCSR alloc] init];
sccsr.commonName = #"some name";
sccsr.organizationName = #"some organisation";
// // aditional data you can set
// sccsr.countryName = #"";
// sccsr.organizationalUnitName = #"";
// sccsr.subjectDER = nil;
// //
//
NSData *certificateRequest = [sccsr build:pPublicKey privateKey:privateKey];
NSString *str = [certificateRequest base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString *strCertificateRequest = #"-----BEGIN CERTIFICATE REQUEST-----\n";
strCertificateRequest = [strCertificateRequest stringByAppendingString:str];
strCertificateRequest = [strCertificateRequest stringByAppendingString:#"\n-----END CERTIFICATE REQUEST-----\n"];
SCCSR.h -> DOWNLOADED FROM PROVIDED LINK

Categories

Resources