BouncyCastle CMSSignedData from external signature and public key certificate - java

I have an X509Certificate, and a byte array that represents a raw signature created by a remote server (which has my private key).
I'm trying to add this signature with PDFBox 2.0.18. For that, I need CMSSignedData object.
How do I generate CmsSignedData without private key (only signature, public key, certificate)?

Related

Is there any way to get the plain text from signed data using private key?

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.

Java Implementation of RSA Signature using RSAwithSHA256

I want to sign a message using RSA private key. This message will be verified by someone else maybe using another programming language other than Java.
I will use the java.security.Signature class to sign the messsage.
The code will be :
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(key); //RSA private key
privateSignature.update(text.getBytes("UTF8")); //text is the clear text string
byte[] signature = privateSignature.sign();
return Base64.getEncoder().encodeToString(signature);
When I check the result and used the publickey to decrypt the signature, I found that padding bytes are added before the SHA256 digest.
My Question is : is the resulted signature generated in a standard way so that it can be verified with the public key in widely used programming languages or is it java specific?

How to associate certificate information to already signed data?

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.

CSR generation using java or BouncyCastle without using Private key

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.

how to do verify using java.security.Signature

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);

Categories