I am trying to sign an XML document using Java and I'm following along with this tutorial. I was trying to sign the document using my private key, but when I looked at the API it says
KeyValue only takes a PublicKey as the parameter. Also in the tutorial it has me sign a DOMSignContext with the private key and then the XMLSignature with the public key.
DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), doc.getDocumentElement());
KeyValue kv = kif.newKeyValue(kp.getPublic());
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
XMLSignature signature = fac.newXMLSignature(si, ki);
I thought the whole point of a private key was so that people would trust you? Why would you need a public key for anything here? Can somebody explain the details here a little bit better for me?
In general, when someone wants to verify/decrypt something encrypted with a private key, the associated public key must be known (which is the whole point of public-key/asymmetric cryptography).
In the XMLSignature context, someone wanting to verify a document with a XMLSignature will need to know what public key to use; thus the public key can be included in a XMLSignature structure for convenience.
Successful verification of the XMLSignature means one can trust that the data signed has not been modified since the signature was created. If the public key is known to be associated with a particular party, then one can trust that party created the signature.
As noted in the tutorial, you still have to "sign" the signature with:
signature.sign(dsc);
Related
The plain text is signed using java.security.Signature. Below is the code used to sign the plain text
public String getSignature(String plainText) throws Exception
{
KeyStore keyStore = loadKeyStore(); // A local method to read the keystore file from file system.
PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD.toCharArray());
Signature privateSignature = Signature.getInstance(SIGNATUREALGO);
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes("UTF-8"));
byte[] signature = privateSignature.sign();
return String.valueOf(signature);
// KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD and SIGNATUREALGO are all constant Strings
}
Note 1: I found online a way to verify the signature using the public key Java Code Examples for java.security.Signature#verify(). But this is not what I require.
Note 2: I also found a ways to encrypt and decrypt as mentioned here RSA Signing and Encryption in Java. But the use case I have in hand is to get the original plain text from a signed data. Is that possible?
No, you can't retrieve the original content from just the signature.
The signature alone does not contain enough information to restore the original clear text, no matter what keys you have access to.
The basic idea of a signature is to send it together with the clear text. That means the clear text will be visible, but the signature can be used to verify that the message was written (or at least signed) by who claims to have done so and has not been tampered with since then.
Signing something is different from encrypting it. The two often uses the same or related technologies and both fall under cryptography.
I am trying to add digital signature to pdf document using pdf-box library (v2.0.8). I am receiving already signed content from a webservice (signed with only private key). Now I would need to associate certificate information to this signed data so that it can be added to PDF document. How can we add certificate to already signed content, preferably using bouncy castle api ?
// here content is data which has to be signed
public byte[] sign(InputStream content) throws IOException {
try {
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
List<Certificate> certList = new ArrayList<Certificate>();
certList.add(certificate);
Store certs = new JcaCertStore(certList);
gen.addCertificates(certs);
CMSProcessableInputStream msg = new CMSProcessableInputStream(signPrivate(content));
CMSSignedData signedData = gen.generate(msg, false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DEROutputStream dos = new DEROutputStream(baos);
dos.writeObject(signedData.toASN1Structure());
return baos.toByteArray();
} catch (Exception e) {
throw new IOException(e);
}
}
Here, I am able to generate digital signature, but it does not contain any certificate information. I already checked this and this question but they donot take the case where content is already signed using private key seperatly and only certificate needs to be associated.
(The code you posted refers to CMS signature containers, so I assume we are talking about adbe.pkcs7.detached or ETSI.CAdES.detached PDF signatures.)
When creating a signature in a CMS signature container, one has the choice whether the signature value really only signs the (hash of the) document data or whether it signs a collection of so-called signed attributes (signedAttrs in the SignerInfo specification) and the hash of the document data is but a value of one of those attributes.
SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
(RFC 5652 section 5.3. SignerInfo Type)
All profiles hereof to be taken seriously, though, require that you use signed attributes, in particular they require you to use an ESS signing-certificate (RFC 2634 section 5.4) or ESS signing-certificate-v2 (RFC 5035 section 3) signed attribute to reference the signer certificate.
In these attributes, therefore, the association of the signature with its signing certificate is fixed before the signature value is generated.
Thus, you cannot freely associate a signing certificate to an already generated signature.
Want to create a CSR file in java, when the private/public key pair are getting generated in HSM(Hardware Security Module).
On trying out the examples in Bouncy Castle, the generation of CSR requires both the private key and public key.As the generation of keys is happening in HSM, i have only the public key and the private key sham object.
Can i generate CSR in java without having the private key?
Please find the code sample i was trying.
KeyPair pair = generateKeyPair();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal("CN=Requested Test Certificate"), pair.getPublic());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(pair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
I am pretty new to HSM, and any input or reference will be helpful.
You can generate a CSR without having the value of the private key. You do need a reference to the private key, and the key must be capable of signing. References to private keys are just special versions of classes that implement PrivateKey. They don't contain the data, just the reference. Calling getEncoded or retrieving a private exponent of an RSA key will however (usually - it may depend on the key generation parameters and PKCS#11 middleware) fail with an exception.
The way these keys can be used is by just providing them to an init method of a newly generated Signature instance. The Java runtime will then search for the right SignatureSpi implementation in the right provider (the one for your HSM). This is called delayed provider selection as it only searches for an implementation after the init method is called. Of course in your case this will all happen out of sight by the ContentSigner.
The private key data should not leave your HSM at any time, unless wrapped for backup or sharing between HSM's.
I need to send a signed XML file to a government agency in Brazil. The problem is that the digest calculated by my Java code (using the Java XML Digital Signature API is different from the one generated with another tool like XMLSEC.
Here's the code I use to generate a XML signature for some XML node:
private synchronized void sign(XmlObject obj) throws Exception {
initKeystore();
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
List<Transform> transformList = new ArrayList<Transform>();
Transform envelopedTransform = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
Transform c14NTransform = fac.newTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
(TransformParameterSpec) null);
transformList.add(envelopedTransform);
transformList.add(c14NTransform);
Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null,
null);
SignedInfo si = fac.newSignedInfo(
fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null),
fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")),
System.getProperty("javax.net.ssl.keyStorePassword").toCharArray());
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("entry",
new KeyStore.PasswordProtection(System.getProperty("javax.net.ssl.keyStorePassword").toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
X509Data xd = kif.newX509Data(Collections.singletonList(cert));
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
// Instantiate the document to be signed.
Element el = (Element) obj.getDomNode().getFirstChild();
String id = el.getAttribute("Id");
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), el);
// Create the XMLSignature, but don't sign it yet.
XMLSignature signature = fac.newXMLSignature(si, ki);
// Marshal, generate, and sign the enveloped signature.
signature.sign(dsc);
}
If I try to validate the generated XML with xmlsec, I get the following error:
$ xmlsec1 --verify consulta.xml
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=229:obj=sha1:subj=unknown:error=12:invalid data:data and digest do not match
FAIL
But if I try to sign the very same file (consult.xml) with xmlsec (using the same private key), that error goes away:
xmlsec1 --sign --output doc-signed.xml --privkey-pem cert.pem consulta.xml
The differences between consult.xml and doc-signed.xml (generated by xmlsec) are the contents of the SignatureValue and DigestValue tags:
consulta.xml:
<DigestValue>Ajn+tfX7JQc0HPNJ8KbTy7Q2f8I=</DigestValue>
...
<SignatureValue>Q1Ys0Rtj8yL2SA2NaQWQPtmNuHKK8q2anPiyLWlH7mOIjwOs0GEcD0WLUM/BZU0Q
T0kSbDTuJeTR2Ec9wu+hqXXbJ76FpX9/IyHrdyx2hLg0VhB5RRCdyBEuGlmnsFDf
XCyBotP+ZyEzolbTCN9TjCUnXNDWtFP1YapMxAIA0sth0lTpYgGJd8CSvFlHdFj+
ourf8ZGiDmSTkVkKnqDsj8O0ZLmbZfJpH2CBKicX+Ct7MUz2sqVli4XAHs6WXX+E
HJpbOKthS3WCcpG3Kw4K50yIYGTkTbWCYFxOVsMfiVy4W/Qz15Vxb8chD8LM58Ep
m/szmvnTAESxv/piDr7hyw==</SignatureValue>
doc-signed.xml:
<DigestValue>w6xElXJrZw3G86OsNkWav+pcKJo=</DigestValue>
...
<SignatureValue>YmUsnlnAY9uLhlfVBLhB8K8ArxMOkOKZJoQ6zgz55ggU6vJCO9+HWJCKQJp6Rvn/w5PCAFY0KJRb
r6/WhHML0Z+Q6TSuIL8OTvJ3iPoROAK6uy07YAflKOUklqk4uxgfMkR+hWMCyfITJVCVZo/MXmPy
g7YwmztoSlGH+p6+ND5n2u47Y2k6SpIvw3CUxwAVQkD0Hsj3G58cbUbrFCoyPVGOe4zJ9c1HPsMW
KzBEFe3QETzPJ8I1B7EEVi5oDvzXE2rMTH4K7zvNGnXpBNGwnSjEOticlqKVP5wyUD7CPwgF1Wgy
Z0njvlaW3K8YmAY8fc70v/+wSO6Fu+0zj18Xeg==</SignatureValue>
I won't post the rest of either file because they're equal and would make this post even more verbose.
From what I can gather, the web app that receives this XML file is a .NET application and it calculates a different signature digest that my Java code (much like xmlsec does). Any ideas?
If it is not too late to answer:
You create 2 Transforms in code (envelopedTransform and c14NTransform), but do not use them.
You create the reference with a single new Transform.ENVELOPED.
http://www.w3.org/TR/2001/REC-xml-c14n-20010315 (C14N) transform is not applied.
Now, I do not know for sure what the XML security standard says the behaviour should be in this case. Maybe other tools automatically apply C14N transform as well.
I know for sure if you do NOT specify any transform JDK will apply at least C14N transform.
Basically change that fac.newReference("", ...) and pass transformList into it instead of Collections.singletonList().
Ideally the DigestValue element contains the actual base64-encoded digest value in Java XML signature API. Could you please verify your digest value created from XMLSec is also base64-encoded.
I have a key pair already, public and private. How do I actually use the java.security.Signature to do verification of a string I signed with one of the keys?
Edit:
I have both the keys as Strings. The verify method, it is actually
verify(byte[] signature)
The javadoc says:
verify(byte[] signature) Indicates whether the given signature can be
verified using the public key or a certificate of the signer.
How would I make that signature recognize which public/private key to use for that verifying, before I call the verify method? In other words, how do I turn my string keys into key objects that would get accepted by signature?
Use KeyFactory to translate key specifications to objects.
Call Signature.getInstance(algName) to get a signature instance.
Use Signature's initVerify method to associate a key for signature verification.
Use update to feed the Signature bytes.
Finally, call verify.
Profit
From the KeyFactory javadoc:
The following is an example of how to use a key factory in order to instantiate a DSA public key from its encoding. Assume Alice has received a digital signature from Bob. Bob also sent her his public key (in encoded format) to verify his signature. Alice then performs the following actions:
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
Signature sig = Signature.getInstance("DSA");
sig.initVerify(bobPubKey);
sig.update(data);
sig.verify(signature);