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.
Related
I'm using Java with Bouncy Castle to convert a server-side string_1 to ECDH PublicKey, then convert the PublicKey to string_2 to check the code's correctness, but string_1 and string_2 are different and I don't know where's wrong.
String key ="BJNiB1+k2n2hiYZccfwGE87mbo5+29l+PFfKzO72tvDmXCAT4QtxqqgX2vUi5go1+MpjJCFyBmIfv5jcsOo9fo3cd/v2jwZAyh2IHh7NHWkA6PFip6el2rNfvyNCd5eN6Q==";
System.out.println("server key:" + key);
byte[] keyBytes = Base64.decodeBase64(key.getBytes("utf-8"));
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp384r1");
KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
ECNamedCurveSpec params = new ECNamedCurveSpec("secp384r1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), keyBytes);
ECPublicKeySpec pubKeySpec1 = new ECPublicKeySpec(point, params);
PublicKey pk = kf.generatePublic(pubKeySpec1);
System.out.println("convert Key*: " + Base64.encodeBase64String(pk.getEncoded()));
The server key and convert key are different, can't find the reason, need help!
server key:BJNiB1+k2n2hiYZccfwGE87mbo5+29l+PFfKzO72tvDmXCAT4QtxqqgX2vUi5go1+MpjJCFyBmIfv5jcsOo9fo3cd/v2jwZAyh2IHh7NHWkA6PFip6el2rNfvyNCd5eN6Q==
convert Key*: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEk2IHX6TafaGJhlxx/AYTzuZujn7b2X48V8rM7va28OZcIBPhC3GqqBfa9SLmCjX4ymMkIXIGYh+/mNyw6j1+jdx3+/aPBkDKHYgeHs0daQDo8WKnp6Xas1+/I0J3l43p
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.
I have the public modulus and exponent for a key. I am encoding a test piece of text and getting different results than the java code that I am trying to replicate.
The java code is here:
RSAPublicKeySpec rsaPublicSpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(rsaPublicSpec);
X509EncodedKeySpec encodedPublicKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance(“RSA”);
PublicKey publicKey = keyFactory.generatePublic(encodedPublicKeySpec);
Cipher cipher = Cipher.getInstance(“RSA / ECB / PKCS1Padding”);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(clearTextString.getBytes());
BASE64Encoder b64 = new BASE64Encoder();
String base64EncodedStr = b64.encode(encryptedBytes);
What I have in C# right now using Bouncy Castle is:
BigInteger publicModulus = new BigInteger(1, Convert.FromBase64String(publicKeyString));
BigInteger publicExponent = new BigInteger(1,Convert.FromBase64String("AQAB"));
RsaKeyParameters pubParameters = new RsaKeyParameters(false, publicModulus, publicExponent);
IAsymmetricBlockCipher eng = new Pkcs1Encoding(new RsaEngine());
eng.Init(true, pubParameters);
byte[] plaintext = Encoding.UTF8.GetBytes("test data");
byte[] encdata = eng.ProcessBlock(plaintext, 0, plaintext.Length);
Console.WriteLine(Convert.ToBase64String(encdata));
I'm a confused about why the java code gets the public key and then does the X509 version (and whether I need to do that in the C# implementation).
I'm also not sure if I need to compensate for endianness of c# vs. java.
Appreciate some help.
The confusing code didn't do anything. Also, Bouncy Castle wasn't actually needed once I got the modulus as a base64 string. Here's what worked.
var publicKey =
"<RSAKeyValue><Modulus>base64modulusgoeshere</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var sbytes = Encoding.UTF8.GetBytes(s);
var rsa = new RSACryptoServiceProvider(2048);
rsa.FromXmlString(publicKey);
var encdata = rsa.Encrypt(sbytes, false);
enc = Convert.ToBase64String(encdata);
I try in this code to verify my code, I have public key , my data and signature.I am read my signature from file and convert my string to public key then get my data and verify to signature.
public static boolean verify () {
String publickey = "MIGfMA0GCSqGSIb3DQE";
byte[] encKey = Base64.decodeBase64(publickey.getBytes());
try {
byte[] MACaddress = GetData();
BufferedReader in = new BufferedReader(new FileReader(
"EndSignatuer.txt"));
FileInputStream keyfis = new FileInputStream("EndSignatuer.txt");
byte[] Signen = new byte[keyfis.available()];
keyfis.read(Signen);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
byte[] deSignen = Base64.decodeBase64(Signen);
byte[] decrypted_digest = cipher.doFinal(deSignen);
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
md5_digest.update(MACaddress);
byte[] digest = md5_digest.digest();
if (decrypted_digest == digest) {
return true;
}else {
return false;//her why give me false
}
Code encryption:
public static void GenarationKEY(byte[] data) {
try {
File fileEndSignatuer = new File("EndSignatuer.txt");
FileOutputStream fopEndSignatuer = new FileOutputStream(
fileEndSignatuer);
// /Read private key from file
FileInputStream keyfis = new FileInputStream("PiveteKey.txt");
byte[] PrivateKeyB = new byte[keyfis.available()];
keyfis.read(PrivateKeyB);
keyfis.close();
byte[] decodePrivetekey = Base64.decodeBase64(PrivateKeyB);
// /get private key
PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(
decodePrivetekey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privKey = keyFactory.generatePrivate(pubKeySpec);
// / make hash
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
// /make encoding
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
byte[] digest = md5_digest.digest(data);
byte[] cipherText = cipher.doFinal(digest);
byte[] degnatureencode = Base64.encodeBase64(cipherText);
fopEndSignatuer.write(degnatureencode);
fopEndSignatuer.flush();
fopEndSignatuer.close();
}
First of all, what you do is not quite RSA digital signature. Use Signature class instead of combination Cipher and MessageDigest. Or if you insist to do it on low level, then consult specification, particularly section 9.2.
Secondly, string MIGfMA0GCSqGSIb3DQE doesn't represent RSA public key neither it is proper Base64-encoded data.
Also you want to use Arrays.equals(byte[], byte[]) instead of equality operator, as the latter just ensures that array object is the same, while the former compares actual content of the arrays.
I want to generate a privatekey PKCS8 format encrypted with password, and I try with this code:
String password = "123456";
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048);
KeyPair key = gen.generateKeyPair();
PrivateKey privateKey = key.getPrivate();
PublicKey publicKey = key.getPublic();
FileOutputStream pvt = new FileOutputStream("d:\\pvt123456.der");
try {
pvt.write(privateKey.getEncoded());
pvt.flush();
} finally {
pvt.close();
}
FileOutputStream pub = new FileOutputStream("d:\\pub123456.der");
try {
pub.write(publicKey.getEncoded());
pub.flush();
} finally {
pub.close();
}
But I don´t know how to encrypt a password with 3des to be compatible with openssl format.
I know it's a little bit late but I also have been looking for a way to do this and while i was searching I found your question, now that I have found a way to do this I decided to come back and share this:
// generate key pair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// extract the encoded private key, this is an unencrypted PKCS#8 private key
byte[] encodedprivkey = keyPair.getPrivate().getEncoded();
// We must use a PasswordBasedEncryption algorithm in order to encrypt the private key, you may use any common algorithm supported by openssl, you can check them in the openssl documentation http://www.openssl.org/docs/apps/pkcs8.html
String MYPBEALG = "PBEWithSHA1AndDESede";
String password = "pleaseChangeit!";
int count = 20;// hash iteration count
SecureRandom random = new SecureRandom();
byte[] salt = new byte[8];
random.nextBytes(salt);
// Create PBE parameter set
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(MYPBEALG);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance(MYPBEALG);
// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
// Encrypt the encoded Private Key with the PBE key
byte[] ciphertext = pbeCipher.doFinal(encodedprivkey);
// Now construct PKCS #8 EncryptedPrivateKeyInfo object
AlgorithmParameters algparms = AlgorithmParameters.getInstance(MYPBEALG);
algparms.init(pbeParamSpec);
EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, ciphertext);
// and here we have it! a DER encoded PKCS#8 encrypted key!
byte[] encryptedPkcs8 = encinfo.getEncoded();
This example code is based on the folowing code I found: http://www.jensign.com/JavaScience/PEM/EncPrivKeyInfo/EncPrivKeyInfo.java
but the folowing resource also helped me to understand a little bit better: http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html