I have two clients,
first client expect CMS/PKCS to verify signature
second client expect only signature (EncryptedDigestMessage). Original file and certificate separately to verify signature
So, I want to create one method that should return the appropriate output based on format (PKCS or SignOnly)
public static byte[] digitalSign(byte[] fileContent , PrivateKey privkey , X509Certificate x509Certificate , String format) throws Exception{
Security.addProvider(new BouncyCastleProvider());
List<X509Certificate> certList = new ArrayList<X509Certificate>();
CMSTypedData data = new CMSProcessableByteArray(fileContent);
certList.add(x509Certificate);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("MD5WithRSA").setProvider("BC").build(privkey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer,x509Certificate));
gen.addCertificates(certs);
CMSSignedData signedData = gen.generate(data, true);
//based on the format this method should return expected byte[]
if(format.equals("PKCS")){//should return CMS/PKCS package
return signedData.getEncoded();
}else{//should return only Signature (Encrypted Digest of fileContent)
SignerInformation signer = signedData.getSignerInfos().getSigners().iterator().next();
return signer.getSignature();// returns the Signature
}
}
I verified PKCS using below method. signature is verified
it returns true.
//CMS/PKCS signature verification
public static boolean verfySignaturePKCS(byte[] cmsSignedBytes ) throws Exception{
boolean result = false;
Security.addProvider(new BouncyCastleProvider());
CMSSignedData cms =new CMSSignedData(cmsSignedBytes );
Store<X509CertificateHolder> certificates = cms.getCertificates();
SignerInformationStore signerInfos = cms.getSignerInfos();
Collection<SignerInformation> signers = signerInfos.getSigners();
Iterator<SignerInformation> iterator = signers.iterator();
while (iterator.hasNext()) {
SignerInformation signer = iterator.next();
Collection<X509CertificateHolder> certCollection = certificates.getMatches(signer.getSID());
Iterator<X509CertificateHolder> certIt = certCollection.iterator();
X509CertificateHolder certHolder = certIt.next();
X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
result = true;
}
}
return result;
}
To verify that SignOnly data, I'm using normal Java security MessageDigest and Cipher classes.
parameters:
signedData--> Signature
fileBytes--> Original File content
cert--> X509Certificate to decrypt the signature
method process:
generate messageHash from original file content
decrypt Signature with cert and get the decryptedMessageHash
compare messageHash and decryptedMessageHash
In this way decrypt signature is going well without error. So I understand this is correct signature which is signed by my privatekey related to the given certificate.
but, both messageHash and decryptedMessageHash having different data.
public static boolean verifySignOnly(byte[] signedData,byte[] fileBytes ,X509Certificate cert) throws Exception{
Security.addProvider(new BouncyCastleProvider());
MessageDigest md = MessageDigest.getInstance("MD5" , "BC");
byte[] messageHash = md.digest(fileBytes);
Cipher cipher = Cipher.getInstance("RSA" , "BC");
cipher.init(Cipher.DECRYPT_MODE, cert);
byte[] decryptedMessageHash = cipher.doFinal(signedData);
return Arrays.equals(decryptedMessageHash, messageHash);
}
Can anyone explain what mistake I made?
Related
I am using bcprov-jdk16-1.46.jar and bcmail-jdk16-1.46.jar to verify the signature embedded inside a JSON file.
Code is as below:
try
{
Security.addProvider(new BouncyCastleProvider());
InputStream objInputStream= new ByteArrayInputStream(signData);
CMSSignedData objCMSSignedData =null;
CMSProcessableByteArray cms_data = new CMSProcessableByteArray(actualData);
objCMSSignedData= new CMSSignedData(cms_data,objInputStream);
CertStore certs = objCMSSignedData.getCertificatesAndCRLs("Collection", "BC");
SignerInformationStore signers = objCMSSignedData.getSignerInfos();
Collection<?> c = signers.getSigners();
Iterator<?> it = c.iterator();
while(it.hasNext())
{
X509Certificate cert = null;
SignerInformation signer = (SignerInformation)it.next();
Collection<?> certCollection = certs.getCertificates(signer.getSID());
if(!certCollection.isEmpty())
{
for(Iterator<?> certIt = certCollection.iterator(); certIt.hasNext();)
{
cert = (X509Certificate)certIt.next();
PublicKey publicKey = cert.getPublicKey();
String str=new String(publicKey.getEncoded());
String sha256hex = DigestUtils.sha256Hex(new String(Base64.encodeBase64(publicKey.getEncoded())));
if(verfiyHexadecimalKey(sha256hex,entityid) {//end
if(signer.verify(publicKey, "BC"))
{
verified =true;
verifyCounter++;
}
else{
verifyCounter=0;
}
}
}
}
}
}
On executing this code as a runnable jar on one of my servers, I am getting
"java.lang.NoSuchFieldError: id_TA_ECDSA_SHA_1" at line objCMSSignedData= new CMSSignedData(cms_data,objInputStream);
But on executing the same code on Eclipse the signature is verified successfully.
Help me in resolving this issue.
I'm newer to RSA and used BC get SubjectPublicKeyInfo from a public key.
String key = "-----BEGIN RSA PUBLIC KEY-----\n" +
"........\n" +// Multiple lines here
"-----END RSA PUBLIC KEY-----\n";
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) reader.readObject();
And then I want encrypt datas. I found some one use RSAEngine to do it:
AsymmetricKeyParameter aKey = (RSAKeyParameters) PublicKeyFactory.createKey(subjectPublicKeyInfo);
AsymmetricBlockCipher engine = new RSAEngine();
engine.init(false, aKey);
byte[] dataEncrypted = engine.processBlock(data, 0, data.length);
After I run those code, I found the result isn't equal to the expection. So I want to know is there any mistake in my code?
Finally I found my way out.
If anyone is familiar with BouncyCastle, he can point me the low-level mistakes.
Firstly, to encrypt data should init Engine with true at first arg in init function.
Secondly, my public key is start with '-----BEGIN RSA PUBLIC KEY-----'. It's PKCS#1 format RSA public key and should use BouncyCastle to read in, but encrypt data should have padding. So, I shouldn't use RSAEngine directly, use PKCS1Encoding instead.
At last , post my encrypt code and decrypt code:
Encryption:
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) reader.readObject();
RSAKeyParameters rsaKeyParameters = (RSAKeyParameters)
PublicKeyFactory.createKey(subjectPublicKeyInfo);
PKCS1Encoding engine = new PKCS1Encoding(new RSAEngine());
engine.init(true, rsaKeyParameters);
return engine.processBlock(data, 0, data.length);
Decryption:
public static byte[] decryptByPublicKey(String data, String key) throws Exception {
byte[] rawData = Base64.decode(data);
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
PEMKeyPair pemKeyPair = (PEMKeyPair) reader.readObject();
SubjectPublicKeyInfo publicKeyInfo = pemKeyPair.getPublicKeyInfo();
PrivateKeyInfo privateKeyInfo = pemKeyPair.getPrivateKeyInfo();
RSAKeyParameters rsaKeyParameters = (RSAKeyParameters)
PrivateKeyFactory.createKey(privateKeyInfo);
PKCS1Encoding engine = new PKCS1Encoding(new RSAEngine());
engine.init(false, rsaKeyParameters);
return engine.processBlock(rawData, 0, rawData.length);
}
For encryption , you can use modulus and public exponent to create a key that support by JDK:
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
PemObject obj = reader.readPemObject();
org.bouncycastle.asn1.pkcs.RSAPublicKey rsaPublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(obj.getContent());
BigInteger modulus = rsaPublicKey.getModulus();
BigInteger publicExponent = rsaPublicKey.getPublicExponent();
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");//This line should use right padding.For PKCS#1 format RSA key , it should be this.
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
See Also:Basic RSA example.
I have an android program that writes data and signs ECDSA to NFC Tag, and another program that reads that data and verifies the signature from the NFC Tag.
I managed to write and sign the data to the NFC Tag, but whenever I try to verify the signature, the program always returns true, even though I deliberately changed the signature.
Can someone tell me what's wrong?
Here is the code I used to sign:
public static String Generate(String x) throws Exception{
KeyPairGenerator kpg;
kpg = KeyPairGenerator.getInstance("EC","BC");
ECGenParameterSpec ecsp;
ecsp = new ECGenParameterSpec("prime192v1");
kpg.initialize(ecsp);
KeyPair kp = kpg.genKeyPair();
PrivateKey privKey = kp.getPrivate();
Signature ecdsaSign;
ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initSign(privKey);
byte[] baText = x.getBytes("UTF-8");
ecdsaSign.update(baText);
byte[] baSignature = ecdsaSign.sign();
String signature = (new BigInteger(1,baSignature).toString(16)).toUpperCase();
return signature;
}
And here is the code I used to verify:
public static boolean Verify(String x) throws Exception{
KeyPairGenerator kpg;
kpg = KeyPairGenerator.getInstance("EC","BC");
ECGenParameterSpec ecsp;
ecsp = new ECGenParameterSpec("prime192v1");
kpg.initialize(ecsp);
KeyPair kp = kpg.genKeyPair();
PrivateKey privKey = kp.getPrivate();
PublicKey pubKey = kp.getPublic();
Signature ecdsaSign;
ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initSign(privKey);
byte[] baText = x.getBytes("UTF-8");
ecdsaSign.update(baText);
byte[] baSignature = ecdsaSign.sign();
Signature ecdsaVerify;
ecdsaVerify = Signature.getInstance("SHA256withECDSA");
ecdsaVerify.initVerify(pubKey);
ecdsaVerify.update(baText);
boolean result = ecdsaVerify.verify(baSignature);
return result;
}
I need to convert Java app into C# and therefore need to migrate from java.security API into BouncyCastle lightweight API.
My working code (java.security) looks like this:
private byte[] computeSignature(byte[] message, PrivateKey key) {
Signature signature = Signature.getInstance("NONEwithRSA");
signature.initSign(privateKey);
signature.update(message);
return signature.sign();
}
This is my verification:
private void verifySignature(byte[] signature, byte[] message, PublicKey publicKey) {
Signature signature = Signature.getInstance("NONEwithRSA");
signature.initVerify(publicKey);
signature.update(message);
System.out.println(signer.verify(result) ? "OK" : "FAIL");
}
Now I am trying to migrate it to BC like this:
problem with NONEwithRSA algorithm which doesn't exist (not sure how to add it)
private byte[] computeSignature(byte[] message, AsymmetricKeyParameter key) {
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("NONEwithRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(key);
signer.getOutputStream().write(Arrays.copyOf(message, message.length), 0, message.length);
byte[] signature = signer.getSignature();
}
doesn't provide good signature
private byte[] computeSignature(byte[] message, AsymmetricKeyParameter privateKey) {
Signer signer = new GenericSigner(new RSAEngine(), new NullDigest());
signer.init(true, privateKey);
signer.update(message, 0, message.length);
return signer.generateSignature();
}
Do you have any suggestions? Or is it even possible to migrate the NONEwithRSA algorithm into BC LW API? I assume that I need to write my own Signer, but as a newb to BC and with the BC documentation I can't handle this on my own.
Try this:
RSABlindedEngine engine = new RSABlindedEngine();
PKCS1Encoding paddedEngine = new PKCS1Encoding(engine);
paddedEngine.init(true, privateKey);
return paddedEngine.processBlock(message, 0, message.length);
The question is how to generate certificate chains programmatically in Java. In other words, I would like to perform in java the operations detailed here: http://fusesource.com/docs/broker/5.3/security/i382664.html
Besically, I can create the RSA keys for a new client:
private KeyPair genRSAKeyPair(){
// Get RSA key factory:
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
log.error(e.getMessage());
e.printStackTrace();
return null;
}
// Generate RSA public/private key pair:
kpg.initialize(RSA_KEY_LEN);
KeyPair kp = kpg.genKeyPair();
return kp;
}
and I generate the corresponding certificate:
private X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm)
throws GeneralSecurityException, IOException {
PrivateKey privkey = pair.getPrivate();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
Date to = new Date(from.getTime() + days * 86400000l);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(dn);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
// Update the algorith, and resign.
algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
return cert;
}
Then I generate the cert signing request and I save it to csrFile file:
public static void writeCertReq(File csrFile, String alias, String keyPass, KeyStore ks)
throws KeyStoreException,
NoSuchAlgorithmException,
InvalidKeyException,
IOException,
CertificateException,
SignatureException,
UnrecoverableKeyException {
Object objs[] = getPrivateKey(ks, alias, keyPass.toCharArray());
PrivateKey privKey = (PrivateKey) objs[0];
PKCS10 request = null;
Certificate cert = ks.getCertificate(alias);
request = new PKCS10(cert.getPublicKey());
String sigAlgName = "MD5WithRSA";
Signature signature = Signature.getInstance(sigAlgName);
signature.initSign(privKey);
X500Name subject = new X500Name(((X509Certificate) cert).getSubjectDN().toString());
X500Signer signer = new X500Signer(signature, subject);
request.encodeAndSign(signer);
request.print(System.out);
FileOutputStream fos = new FileOutputStream(csrFile);
PrintStream ps = new PrintStream(fos);
request.print(ps);
fos.close();
}
where
private static Object[] getPrivateKey(KeyStore ks, String alias, char keyPass[])
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException {
key = null;
key = ks.getKey(alias, keyPass);
return (new Object[]{ (PrivateKey) key, keyPass });
}
Now I should sign the CSR with the CA private key, but I cannot see how to achive that in java. I have "my own" CA private key in my jks.
Besides, once I manage to sign the CSR I should chain the CA cert with the signed CSR: how that can be done in java?
I would prefer not to use bc or other external libs, just "sun.security" classes.
Thank you.
I believe the code example in the post http://www.pixelstech.net/article/1406726666-Generate-certificate-in-Java----2 will show you how to generate certificate chain with pure Java. It doesn't require you to use Bouncy Castle.
Sorry, but despite your desires, and besides writing all of your crypto code and including it with your project (not recommended), I'd recommend using Bouncy Castle here.
Specifically, please refer to https://stackoverflow.com/a/7366757/751158 - which includes code for exactly what you're looking to do.
I see you've already gone over to the BouncyCastle side of the house but just in case anyone else was wondering; you can add the cert chain to the entry when putting the key into the KeyStore. For example
// build your certs
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load([keystore stream], password.toCharArray());// or null, null if it's a brand new store
X509Certificate[] chain = new X509Certificate[2];
chain[0] = _clientCert;
chain[1] = _caCert;
keyStore.setKeyEntry("Alias", _clientCertKey, password.toCharArray(), chain);
keyStore.store([output stream], password.toCharArray());
// do other stuff