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);
Related
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?
I had generate public key using Java Spring Security, but I can not use that public key to encrypt the data using Nodejs crypto library. I think it is because of its format(X509).
My Nodejs code
module.exports.encryptRsa = (toEncrypt, pemPath) => {
let absolutePath = path.resolve(pemPath);
let publicKey = fs.readFileSync(absolutePath, "utf8");
let buffer = Buffer.from(toEncrypt);
let encrypted = crypto.publicEncrypt(publicKey, buffer);
return encrypted.toString("base64");
};
My Java code
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(keyAlgorithm);
keyGen.initialize(2048);
KeyPair keyPair = keyGen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
byte[] privateKeyBytes = privateKey.getEncoded();
byte[] publicKeyBytes = publicKey.getEncoded();
String formatPrivate = privateKey.getFormat(); // PKCS#8
String formatPublic = publicKey.getFormat(); // X.509
FileWriter fos = new FileWriter("publicKey.pem");
fos.write("-----BEGIN RSA PUBLIC KEY-----\n");
fos.write(enc.encodeToString(publicKeyBytes));
fos.write("\n-----END RSA PUBLIC KEY-----\n");
fos.close();
Java's getEncoded() method returns the public key in format called 'spki' by Node crypto. Java's name for that format is "X.509", an unfortunate choice because it causes confusion with certificates of that name.
The proper PEM header for spki keys is simply -----BEGIN PUBLIC KEY-----. Just get rid of RSA in the header and footer.
i want to generate ECDSA keypair in pkcs11 usb token. after that want to sign CSR with private key, but facing exception "Invalid signature".
Mechanism keyPairGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_EC_KEY_PAIR_GEN);
ECDSAPrivateKey ecdsaPrivateKeyTemplate = new ECDSAPrivateKey();
ecdsaPrivateKeyTemplate.getLabel().setCharArrayValue(keyAlias.toCharArray());
ecdsaPrivateKeyTemplate.getId().setByteArrayValue(keyAlias.getBytes());
ecdsaPrivateKeyTemplate.getSign().setBooleanValue(Boolean.TRUE);
ecdsaPrivateKeyTemplate.getDecrypt().setBooleanValue(Boolean.TRUE);
ecdsaPrivateKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
ecdsaPrivateKeyTemplate.getPrivate().setBooleanValue(Boolean.TRUE);
ecdsaPrivateKeyTemplate.getSensitive().setBooleanValue(Boolean.TRUE);
ecdsaPrivateKeyTemplate.getExtractable().setBooleanValue(Boolean.FALSE);
ecdsaPrivateKeyTemplate.getKeyType().setLongValue(PKCS11Constants.CKK_EC); ECDSAPublicKey ecdsaPublicKeyTemplate = new ECDSAPublicKey(); ecdsaPublicKeyTemplate.getLabel().setCharArrayValue(keyAlias.toCharArray());
ecdsaPublicKeyTemplate.getId().setByteArrayValue(keyAlias.getBytes());
ecdsaPublicKeyTemplate.getEncrypt().setBooleanValue(Boolean.TRUE);
ecdsaPublicKeyTemplate.getPrivate().setBooleanValue(Boolean.FALSE);
ecdsaPublicKeyTemplate.getVerify().setBooleanValue(Boolean.TRUE);
ecdsaPublicKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
ecdsaPublicKeyTemplate.getKeyType().setLongValue(PKCS11Constants.CKK_EC);
ecdsaPublicKeyTemplate.getModifiable().setBooleanValue(Boolean.TRUE);
ASN1ObjectIdentifier curveId = getCurveId((getEcdsaParamsOID(256)));
X962Parameters x962 = new X962Parameters(curveId);
byte[] paramsBytes = x962.getEncoded();
ecdsaPublicKeyTemplate.getEcdsaParams().setByteArrayValue(paramsBytes);
KeyPair generatedKeyPair = m_objSession.generateKeyPair(keyPairGenerationMechanism,ecdsaPublicKeyTemplate, ecdsaPrivateKeyTemplate);
ECDSAPublicKey publicKey = (ECDSAPublicKey) generatedKeyPair.getPublicKey();
ECDSAPrivateKey privateKey = (ECDSAPrivateKey) generatedKeyPair.getPrivateKey();
byte[] pubPoint = publicKey.getEcPoint().getByteArrayValue();
DEROctetString os = (DEROctetString) DEROctetString.fromByteArray(pubPoint);
AlgorithmIdentifier keyAlgID = new AlgorithmIdentifier(
X9ObjectIdentifiers.id_ecPublicKey, curveId);
SubjectPublicKeyInfo pkInfo = new SubjectPublicKeyInfo(keyAlgID, os.getOctets());
Signing code from comments:
ECDSAPrivateKey signatureKey = this.getECDSAPrivateKey(a_strKeyId,m_objSession);
MessageDigest digestEngine = MessageDigest.getInstance("SHA-256");
digestEngine.update(bUnsignedData);
byte[] digest = digestEngine.digest();
Mechanism signatureMechanism = Mechanism.get(PKCS11Constants.CKM_ECDSA);
m_objSession.signInit(signatureMechanism, signatureKey);
DigestInfo digestInfoEngine = new DigestInfo(a_objAlgorithmIdentifier, digest);
byte[] digestInfo = digestInfoEngine.getEncoded();
byte[] signatureValue = m_objSession.sign(digestInfo);
For ECDSA you don't need DigestInfo, the digest value (in bytes) is signed directly. DigestInfo is probably required for RSA.
How do we sign a message with a givenPrivateKey in java with elliptic curve(p256)
Basically a java implementation of
let elliptic = new EC('p256')
const sig = elliptic.sign(msgHashHex, privateKey, null)
I dont want to generate new private/public key pair. My privateKey = 'abc'
Also please let me know if there is an online tool to play around with digital signatures.
Thanks a lot in advance.
PrivateKey privateKey = ; // your EC p256 private key
byte[] msgHashHex = ; // byte array data
Signature signature = Signature.getInstance("ECDSA"); // or SHA256WithECDSA etc.
signature.initSign(privateKey);
signature.update(msgHashHex);
byte[] result = signature.sign();
I have this public key:
MIGJAoGBAKv4OKlpY2oq9QZPMzAjbQfiqDqTnisSvdLP+mTswZJdbtk1J+4+qAySJuZjSQljzcUu0ANg+QG0VsvoU72zu5pErZKWubfe9HB/tq69bhP60qgP6/W2VebWlqUNGtsMedxuVaFBL3SoqU7e5RELIsuArCJJIgz86BQDX0x63VpXAgMBAAE=
I am trying to use it to decode this:
Zm/qR/FrkzawabBZYk7WfQJNMVZoZrwWTvfQwIhPMzAuqEO+y+sb/x9+TZwTbqmu45/GV4yhKv0bbDL8F6rif7RJap7iQUFQBDEIAraY42IGZ8pB6A0Q0RSnJWW+tLTLJg5cTrgZQ8sLoO+U03T6DE1wy73FU5h6XhXxZERo0tQ=
In which I know the unencrypted value is this:
2ABB43E83F7EC33D0D33F64BA5782E42
I have been trying several different things including Bouncy Castle (Java implementation) but I am unable to get the public key to work, mostly ending in invalid encoding errors.
This is my current implementation:
byte[] keyBytes = Base64.decodeBase64(PUB_KEY);
try {
AlgorithmIdentifier rsaIdent = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption);
SubjectPublicKeyInfo kInfo = new SubjectPublicKeyInfo(rsaIdent, keyBytes);
ASN1Primitive primKey = kInfo.parsePublicKey();
byte[] encoded = primKey.getEncoded();
byte[] sessionBytes = Base64.decodeBase64("Zm/qR/FrkzawabBZYk7WfQJNMVZoZrwWTvfQwIhPMzAuqEO+y+sb/x9+TZwTbqmu45/GV4yhKv0bbDL8F6rif7RJap7iQUFQBDEIAraY42IGZ8pB6A0Q0RSnJWW+tLTLJg5cTrgZQ8sLoO+U03T6DE1wy73FU5h6XhXxZERo0tQ=");
Security.addProvider(new BouncyCastleProvider());
X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded);
KeyFactory factory = KeyFactory.getInstance(spec.getFormat());
Cipher cipher = Cipher.getInstance("RSA", "BC");
cipher.init(Cipher.DECRYPT_MODE, factory.generatePublic(spec));
// ----- THIS IS WHERE IT BREAKS -----
byte[] decrypted = cipher.doFinal(sessionBytes);
String tada = new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) { ... }
When I get to generate the public key from the factory I get
java.lang.IllegalArgumentException: unknown object in getInstance: org.bouncycastle.asn1.ASN1Integer
I have tried several other things but all result in the same error above.
Is there something wrong with my public key? What is the correct way to do this?
First of all, your key is PKCS#1 encoded. It's not a SubjectPublicKeyInfo structure required by Java. You can see how to decode it here.
Second, you cannot decrypt with a public key, you need a private key for that.