I am wanting generate a keypair once and reuse it.
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
KeyPair pair = generator.generateKeyPair();
return pair;
}
How do I go about this?
There is a bit of a problem here: Java's focus is almost entirely on TLS and the cryptography required to implement TLS. For TLS a private key and a certificate is required. So you get into a situation where you:
have to generate a (bogus) self signed certificate to go with your public key or;
have to find another way of storing the private key without a certificate.
However, with (2.) you quickly get a method that isn't very compatible. If you want to go that way, you could create a PKCS#8 encrypted private key that is encrypted using PBE / CBC.
So here's some code to create a self signed certificate and use that to store the key. Note the expiration date, you could set it to 100 years to be on the safe side (or you could actually do some key management).
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.UnrecoverableEntryException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Calendar;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
public class StoreKeyPair {
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
KeyPair pair = generator.generateKeyPair();
return pair;
}
public static Certificate selfSign(KeyPair keyPair, String subjectDN)
throws OperatorCreationException, CertificateException, IOException
{
Provider bcProvider = new BouncyCastleProvider();
Security.addProvider(bcProvider);
long now = System.currentTimeMillis();
Date startDate = new Date(now);
X500Name dnName = new X500Name(subjectDN);
// Using the current timestamp as the certificate serial number
BigInteger certSerialNumber = new BigInteger(Long.toString(now));
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
// 1 Yr validity
calendar.add(Calendar.YEAR, 1);
Date endDate = calendar.getTime();
// Use appropriate signature algorithm based on your keyPair algorithm.
String signatureAlgorithm = "SHA256WithRSA";
SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair
.getPublic().getEncoded());
X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(dnName,
certSerialNumber, startDate, endDate, dnName, subjectPublicKeyInfo);
ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).setProvider(
bcProvider).build(keyPair.getPrivate());
X509CertificateHolder certificateHolder = certificateBuilder.build(contentSigner);
Certificate selfSignedCert = new JcaX509CertificateConverter()
.getCertificate(certificateHolder);
return selfSignedCert;
}
public static void main(String[] args) throws Exception {
KeyPair generatedKeyPair = generateKeyPair();
String filename = "test_gen_self_signed.pkcs12";
char[] password = "test".toCharArray();
storeToPKCS12(filename, password, generatedKeyPair);
KeyPair retrievedKeyPair = loadFromPKCS12(filename, password);
// you can validate by generating a signature and verifying it or by
// comparing the moduli by first casting to RSAPublicKey, e.g.:
RSAPublicKey pubKey = (RSAPublicKey) generatedKeyPair.getPublic();
RSAPrivateKey privKey = (RSAPrivateKey) retrievedKeyPair.getPrivate();
System.out.println(pubKey.getModulus().equals(privKey.getModulus()));
}
private static KeyPair loadFromPKCS12(String filename, char[] password)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
FileNotFoundException, IOException, UnrecoverableEntryException {
KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fis = new FileInputStream(filename);) {
pkcs12KeyStore.load(fis, password);
}
KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(password);
Entry entry = pkcs12KeyStore.getEntry("owlstead", param);
if (!(entry instanceof PrivateKeyEntry)) {
throw new KeyStoreException("That's not a private key!");
}
PrivateKeyEntry privKeyEntry = (PrivateKeyEntry) entry;
PublicKey publicKey = privKeyEntry.getCertificate().getPublicKey();
PrivateKey privateKey = privKeyEntry.getPrivateKey();
return new KeyPair(publicKey, privateKey);
}
private static void storeToPKCS12(
String filename, char[] password,
KeyPair generatedKeyPair) throws KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException, FileNotFoundException,
OperatorCreationException {
Certificate selfSignedCertificate = selfSign(generatedKeyPair, "CN=owlstead");
KeyStore pkcs12KeyStore = KeyStore.getInstance("PKCS12");
pkcs12KeyStore.load(null, null);
KeyStore.Entry entry = new PrivateKeyEntry(generatedKeyPair.getPrivate(),
new Certificate[] { selfSignedCertificate });
KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(password);
pkcs12KeyStore.setEntry("owlstead", entry, param);
try (FileOutputStream fos = new FileOutputStream(filename)) {
pkcs12KeyStore.store(fos, password);
}
}
}
Note that I was too lazy to properly handle the exceptions.
This code uses a slightly altered version of this answer, see my comments for why I changed the code.
The public key can of course be stored separately as well. Just call getEncoded and store the resulting SubjectPublicKeyInfo structure.
Related
In have integrated oauth1.0. i am using sha1-rsa signature method. i have generated oauth signature. i got 200 status but i did not get any value in service provider. my doubt is signature generation. how to generate the oauth signature using sha1-rsa in java? i have mentioned link what am using.
`private static KeyPair getKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024, new SecureRandom());
return kpg.genKeyPair();
}`
public static String sign(PrivateKey privateKey) throws Exception {
Signature privateSignature = Signature.getInstance(" ");
privateSignature.initSign(privateKey);
//privateSignature.update(plainText.getBytes(UTF_8));
byte[] signature = privateSignature.sign();
System.out.println(Base64.encodeBase64String(signature));
return Base64.encodeBase64String(signature);
//encodeToString(signature);
}
thanks in advance,
Below snippets generate a valid signature, i.e. signs the input data using an RSA Private Key string i.e. creates a private key from contents of a .pkcs8 private key file. This is in Groovy (works with Java with minimal syntax changes:
import java.security.SignatureException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Signature;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
//-------- generate RSA-SHA1 signature from request data
def encryptUsingRSASHA1(String data, String key) throws
java.security.SignatureException
{
String result
try {
// get an rsa_sha1 key from the raw key bytes
//SHA1withRSA
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(getPrivateKey(key));
signature.update(data.getBytes("UTF-8"));
byte[] rawRSAsigned = signature.sign();
result= rawRSAsigned.encodeBase64()
} catch (Exception e) {
throw new SignatureException("Failed to generate Signature : " +
e.getMessage());
}
return result
}
//get PrivateKey from key string
def getPrivateKey(String privateKey) throws NoSuchAlgorithmException,
InvalidKeySpecException {
String privateKeyPEM = privateKey.replace("-----BEGIN PRIVATE KEY-----\n",
"").replace("-----END PRIVATE KEY-----", "").replaceAll("\n","");
byte[] privateKeyBytes = privateKeyPEM.decodeBase64();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
I am trying to calculate the performance of ECDSA signature verification in Java for secp521r1 curve.
I am getting around 3000 signature verification per second.
Adding my other work also, I tested with openssl speed command for secp521r1 curve, there I got around 9000 verification on my machine (40 cores). I tested with Golang also but the performance with secp521r1 is not good, though the performance with secp256r1 is great(28000 verification per second). I reached out to Golang community and found 256 is hand optimized but other curves are not.
I also tested with nodeJs to verify signature, there I got 9000 verification per second which is similar to Openssl. By checking the source code of nodeJs crypto module implementation, I found they are using openssl like implementation.
But I have to work on Java only, so reaching out to the community. Is this the usual result in Java or do we have any openssl like implementation in Java as well?
Dummy Code==========================
package dummy;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.json.JSONObject;
public class DSA {
static JSONObject ob = new JSONObject();
static byte[] strByte = null;
static byte[] realSig;
static String str = "a";//"{\"type\":\"issueTx\",\"userId\":1,\"transaction\":{\"amount\":1000}}"; /a
static byte[] sigdata;
static String sigEdata;
static X509Certificate c;
static String sigTestSigneddata;
public static void main(String[] args)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(new ECGenParameterSpec("secp521r1"), new SecureRandom());
KeyPair pair = keyGen.generateKeyPair();
PrivateKey priv = pair.getPrivate();
PublicKey pub = pair.getPublic();
FileWriter fw = new FileWriter("MyKeys/n.pem");
PemWriter writer = new PemWriter(fw);
writer.writeObject(new PemObject("PUBLIC KEY", pub.getEncoded()));
writer.close();
FileWriter fw2 = new FileWriter("MyKeys/n_sk");
PemWriter writer2 = new PemWriter(fw2);
writer2.writeObject(new PemObject("PRIVATE KEY ", priv.getEncoded()));
writer2.close();
Security.addProvider(new BouncyCastleProvider());
Signature ecdsa;
ecdsa = Signature.getInstance("SHA256withECDSA"); // SHA512WITHECDSA SHA512withECDSA
ecdsa.initSign(getPrivate("MyKeys/n_sk"));
strByte = str.getBytes("UTF-8");
ecdsa.update(strByte);
realSig = ecdsa.sign();
sigEdata = Base64.getEncoder().encodeToString(realSig);
Thread.sleep(1000);
verify();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void verify() throws Exception {
verifySignature();
}
private static void verifySignature() throws Exception {
Signature sig = Signature.getInstance("SHA256withECDSA", "BC"); // SHA256withECDSA // SHA512withECDSA
// sig.initVerify(c.getPublicKey()); to verify using digi cert
sig.initVerify(getPublic("MyKeys/n.pem"));
sig.update(str.getBytes());
System.out.println(sig.verify(Base64.getDecoder().decode(sigEdata)));
// sig.verify(sigEdata.getBytes(Charset.defaultCharset()));
System.out.println(str);
}
private static PrivateKey getPrivate(String filename) throws Exception {
PemReader reader = new PemReader(new FileReader(filename));
PemObject pemObject = reader.readPemObject();
byte[] content = pemObject.getContent();
reader.close();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(content);
KeyFactory kf = KeyFactory.getInstance("EC");
return kf.generatePrivate(spec);
}
private static PublicKey getPublic(String filename) throws Exception {
PemReader reader = new PemReader(new FileReader(filename));
PemObject pemObject = reader.readPemObject();
byte[] content = pemObject.getContent();
reader.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(content);
KeyFactory kf = KeyFactory.getInstance("EC");
return kf.generatePublic(spec);
}
}
I am trying to decrypt the data field inside an Apple Pay Payment Token using ECC algorithm in Java on the server side. How can I implement it?
I've been searching for such an implementation in Java for quite a while and I couldn't find one. Instead, I've found an implementation using Bouncy Castle C# library to decrypt the token:
https://github.com/chengbo/ApplePayAndroidPayDecryption
Although there is also an Bouncy Castle library in Java, but I've found some difference in the implementation between the C# one and the Java one, which lead to the failure of decryption when I try to code in accordance with the C# implementation above.
I've generated my certificate in Apple Dev Center and I'm quite sure that the certificate file needed during the process of decrytion is correct.
Has anyone succeeded in decrypting the token in Java? Any help is appreciated, Thanks a lot!
Here is the key part of the return message from Apple when I did a test payment:
passKit={"version":"EC_v1","data":"AK7UZehTHQRXYzgPCD5ijZfloc9ZfUjAutl+7v/83V7U6YjsWSrBVzILQlp2xLP4E4QXxRwadIh0Y9Vg6297BV2ljginDwoR5nneEIQP6fNCXYwll5hUYYlL0ZO7pD/8KXStAh8pnOAyFtEVrDqIRCWZbftzdsAi76qFMXd3Z2bRSjl5zrt8Qfua6Nu1b3MNNVlPQVMJsskEQFncnViNLDkRulgt5WezVF8N1m62nEqminLBF7m+36/pLi0t9JTfqQ0qNYahczAzyyCJhABkXRXXf9iF3YJ77gBD9mBFRVrePPNW0PnJyoQPvDikGzDTc4k5+NBBSEAJjBLlt94okHmh9eO2A9/xoUh7/ktI+Vjk2k+8PWDOAWIkVM4+7vPCrESYedVzTBd6BYIL7+oPmbAW5EJ1JC2twafmmVhL4lXwdz296aBtNDTIzV+of+Oc6JrEutzjVYm8qGdv4MO0DJ3eWG/r9G1QPaTR84CRXXxmoL/EAH9fLYGfQeJsGHmLKieX2b2IfHwTtTnFVloqwt0ywd47PnqLbZ+pETZgsUegZIUAPH6Hl3WMo2eXKbybyxuY70WV+OoIxKBGHQnPYndPA3aG7XeSiUXF/2vW/Qq+UVfxQc0O4X6A/qTYy5c1HlQVq7PloE2+jkGCtKpuvsuVnnRF7sxxG3Wke7Vlz6at/+CHdT0K91+a29U1E8JVwhjnXvT8E/FcvrwHaCMmK1eK8/sMFGQ=","signature":"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","header":{"ephemeralPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFnF0WIB3GTpyaP7rgW0kzUMgqfwsTecb7/JrSQXZSuILCBPBs2YQQXFfIHNYtFFMzMTY24/tgbolbKjkmIUwIw==","applicationData":"5cd2d027aa6372ea5420770272ef47a596e60f4299c16c6591c3e7e532208394","publicKeyHash":"sRANn6djBkx5m//vTDU6HFOX4j1Nn/X4bNlgxJYRZgo=","transactionId":"947a5fc21adcc692bd204fa4e1a7a4f83ab8383283f3fa46b204b514559adede"}}
This Code(JAVA) will decrypt the ApplePay token. For this code to work convert the certificate file to JKS(retrieve merchant ID) and pk8(private key) formates.
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.bouncycastle.asn1.ASN1UTCTime;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Hex;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
import java.io.*;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
public class ApplePayDecrypt {
public static final String MERCHANT_ID = "merchant.Id";
private static KeyStore keyStore;
private static PrivateKey merchantPrivateKey;
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
public static AppleDecryptData decrypt(TokenData tokenData) {
try {
// Load merchant private key
byte[] merchantbyte = IOUtils.toByteArray(Application.class.getResource("/apple_pay.pk8"));
String key = new String(merchantbyte);
key = key.replace("-----BEGIN PRIVATE KEY-----", "");
key = key.replace("-----END PRIVATE KEY-----", "");
key = key.replaceAll("\\s+", "");
byte[] merchantPrivateKeyBytes = Base64.decodeBase64(key);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(merchantPrivateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC", PROVIDER_NAME);
merchantPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
// Load Apple root certificate
keyStore = KeyStore.getInstance("BKS");
keyStore.load(GoSecureApplication.class.getResourceAsStream("/appleCA-G3"), "apple123".toCharArray());
return unwrap(tokenData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#SuppressWarnings({ "unused", "unchecked" })
public static AppleDecryptData unwrap(TokenData tokenData) throws Exception {
// Merchants should use the 'version' field to determine how to verify and
// decrypt the message payload.
// At this time the only published version is 'EC_v1' which is demonstrated
// here.
String version = tokenData.version;
byte[] signatureBytes = Base64.decodeBase64(tokenData.signature);
byte[] dataBytes = Base64.decodeBase64(tokenData.data);
// JsonObject headerJsonObject =
// jsonObject.get(PAYMENT_HEADER).getAsJsonObject();
byte[] transactionIdBytes = Hex.decode(tokenData.header.transactionId);
byte[] ephemeralPublicKeyBytes = Base64.decodeBase64(tokenData.header.ephemeralPublicKey);
// Merchants that have more than one certificate may use the 'publicKeyHash'
// field to determine which
// certificate was used to encrypt this payload.
byte[] publicKeyHash = Base64.decodeBase64(tokenData.header.publicKeyHash);
// Application data is a conditional field, present when the merchant has
// supplied it to the iOS SDK.
byte[] applicationDataBytes = null;
byte[] signedBytes = ArrayUtils.addAll(ephemeralPublicKeyBytes, dataBytes);
signedBytes = ArrayUtils.addAll(signedBytes, transactionIdBytes);
signedBytes = ArrayUtils.addAll(signedBytes, applicationDataBytes);
CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(signedBytes), signatureBytes);
// Check certificate path
Store<?> certificateStore = signedData.getCertificates();
List<X509Certificate> certificates = new ArrayList<X509Certificate>();
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
certificateConverter.setProvider(PROVIDER_NAME);
for (Object o : certificateStore.getMatches(null)) {
X509CertificateHolder certificateHolder = (X509CertificateHolder) o;
certificates.add(certificateConverter.getCertificate(certificateHolder));
}
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", PROVIDER_NAME);
CertPath certificatePath = certificateFactory.generateCertPath(certificates);
PKIXParameters params = new PKIXParameters(keyStore);
params.setRevocationEnabled(false);
// TODO: Test certificate has no CRLs. Merchants must perform revocation checks
// in production.
// TODO: Verify certificate attributes per instructions at
// https://developer.apple.com/library/ios/documentation/PassKit/Reference/PaymentTokenJSON/PaymentTokenJSON.html#//apple_ref/doc/uid/TP40014929
CertPathValidator validator = CertPathValidator.getInstance("PKIX", PROVIDER_NAME);
PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(certificatePath, params);
System.out.println(result);
// Verify signature
SignerInformationStore signerInformationStore = signedData.getSignerInfos();
boolean verified = false;
for (Object o : signerInformationStore.getSigners()) {
SignerInformation signer = (SignerInformation) o;
Collection<?> matches = certificateStore.getMatches(signer.getSID());
if (!matches.isEmpty()) {
X509CertificateHolder certificateHolder = (X509CertificateHolder) matches.iterator().next();
if (signer.verify(
new JcaSimpleSignerInfoVerifierBuilder().setProvider(PROVIDER_NAME).build(certificateHolder))) {
DERSequence sequence = (DERSequence) signer.getSignedAttributes().get(CMSAttributes.signingTime)
.toASN1Primitive();
DERSet set = (DERSet) sequence.getObjectAt(1);
ASN1UTCTime signingTime = (ASN1UTCTime) set.getObjectAt(0).toASN1Primitive();
// Merchants can check the signing time of this payment to determine its
// freshness.
System.out.println("Signature verified. Signing time is " + signingTime.getDate());
verified = true;
}
}
}
if (verified) {
// Ephemeral public key
KeyFactory keyFactory = KeyFactory.getInstance("EC", PROVIDER_NAME);
PublicKey ephemeralPublicKey = keyFactory.generatePublic(new X509EncodedKeySpec(ephemeralPublicKeyBytes));
// Key agreement
String asymmetricKeyInfo = "ECDH";
KeyAgreement agreement = KeyAgreement.getInstance(asymmetricKeyInfo, PROVIDER_NAME);
agreement.init(merchantPrivateKey);
agreement.doPhase(ephemeralPublicKey, true);
byte[] sharedSecret = agreement.generateSecret();
byte[] derivedSecret = performKDF(sharedSecret, extractMerchantIdFromCertificateOid());
// Decrypt the payment data
String symmetricKeyInfo = "AES/GCM/NoPadding";
Cipher cipher = Cipher.getInstance(symmetricKeyInfo, PROVIDER_NAME);
SecretKeySpec key = new SecretKeySpec(derivedSecret, cipher.getAlgorithm());
IvParameterSpec ivspec = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.DECRYPT_MODE, key, ivspec);
byte[] decryptedPaymentData = cipher.doFinal(dataBytes);
// JSON payload
String data = new String(decryptedPaymentData, "UTF-8");
// System.out.println(data);
AppleDecryptData decryptDat = ObjMapper.getInstance().readValue(data, AppleDecryptData.class);
return decryptDat;
} else {
return null;
}
}
private static final byte[] APPLE_OEM = "Apple".getBytes(Charset.forName("US-ASCII"));
private static final byte[] COUNTER = { 0x00, 0x00, 0x00, 0x01 };
private static final byte[] ALG_IDENTIFIER_BYTES = "id-aes256-GCM".getBytes(Charset.forName("US-ASCII"));
/**
* 00000001_16 || sharedSecret || length("AES/GCM/NoPadding") ||
* "AES/GCM/NoPadding" || "Apple" || merchantID
*/
private static byte[] performKDF(byte[] sharedSecret, byte[] merchantId) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(COUNTER);
baos.write(sharedSecret);
baos.write(ALG_IDENTIFIER_BYTES.length);
baos.write(ALG_IDENTIFIER_BYTES);
baos.write(APPLE_OEM);
baos.write(merchantId);
MessageDigest messageDigest = MessageDigest.getInstance("SHA256", PROVIDER_NAME);
return messageDigest.digest(baos.toByteArray());
}
#SuppressWarnings("unused")
private static byte[] performKDF(byte[] sharedSecret, String merchantId) throws Exception {
MessageDigest messageDigest = MessageDigest.getInstance("SHA256", PROVIDER_NAME);
return performKDF(sharedSecret, messageDigest.digest(merchantId.getBytes("UTF-8")));
}
protected static byte[] extractMerchantIdFromCertificateOid() throws Exception {
KeyStore vkeyStore = KeyStore.getInstance("JKS");
vkeyStore.load(GoSecureApplication.class.getResourceAsStream("/kapple_pay.jks"), "".toCharArray());
Enumeration<String> aliases = vkeyStore.aliases();
String alias = null;
while (aliases.hasMoreElements()) {
alias = aliases.nextElement();
}
X509Certificate cert = (X509Certificate) vkeyStore.getCertificate(alias);
byte[] merchantIdentifierTlv = cert.getExtensionValue("1.2.840.113635.100.6.32");
byte[] merchantIdentifier = new byte[64];
System.arraycopy(merchantIdentifierTlv, 4, merchantIdentifier, 0, 64);
return Hex.decode(merchantIdentifier);
}
}
I've encoded a simple json data using RSA public key and now I'm trying to decode it. The encoding part was done via terminal and the decoding is being performed programmatically. To verify the integrity of the encrypted file, I decrypted it via terminal and it works just fine. Now that I'm trying to decrypt the file programmatically, I'm running into decrypting issues. I can read the private_key.pem file perfectly and pass it to Cipher for decrypting the encoded file, however upon doing this I get the following exception.
java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(CipherSpi.java:457)
at javax.crypto.Cipher.doFinal(Cipher.java:1204)
at com.benchmark.openssl.RSADecryption.decipherString(RSADecryption.java:295)
at com.benchmark.openssl.RSADecryption.main(RSADecryption.java:263)
at com.benchmark.MainActivity$1.onComplete(MainActivity.java:157)
at io.reactivex.internal.operators.completable.CompletableObserveOn$ObserveOnCompletableObserver.run(CompletableObserveOn.java:90)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:463)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
OpenSSL Commands used:
openssl genrsa -out priv_key.pem 2048
openssl rsa -pubout -in priv_key.pem -out pub_key.pem
openssl rsautl -encrypt -in userdata.json -out user_encrypted_with_pub_key -inkey pub_key.pem –pubin
openssl rsautl -decrypt -in user_encrypted_with_pub_key -inkey priv_key.pem --> This is what I'm trying to do programmatically.
Code:
import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemReader;
import android.util.Base64;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public static void main(String privateKeyPath, String encodedFilePath) throws FileNotFoundException,
IOException, NoSuchAlgorithmException, NoSuchProviderException {
Security.addProvider(new BouncyCastleProvider());
String encodedString = readFileAsString(encodedStringPath);
Timber.v("Encoded String: %s", encodedString);
KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
try {
PrivateKey priv = generatePrivateKey(factory, privateKeyPath);
Timber.i(String.format("Instantiated private key: %s", priv));
decipherString(priv, encodedString);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
private static PrivateKey generatePrivateKey(KeyFactory factory,
String filename) throws InvalidKeySpecException,
FileNotFoundException, IOException {
PemFile pemFile = new PemFile(filename, false);
byte[] content = pemFile.getPemObject().getContent();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
return factory.generatePrivate(privKeySpec);
}
private static void decipherString(PrivateKey privateKey, String encodedStringData) {
byte[] dectyptedText = null;
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
dectyptedText = cipher.doFinal(encodedStringData.getBytes()); <---- EXCEPTION HERE
Timber.w("Deciphered text is: %s", new String(dectyptedText));
}
catch (Exception e) {
e.printStackTrace();
}
}
static class PemFile {
private PemObject pemObject;
public PemFile(String filename, boolean isBase64) throws FileNotFoundException, IOException {
PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream(filename)));
try {
this.pemObject = pemReader.readPemObject();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
pemReader.close();
}
}
public PemObject getPemObject() {
return pemObject;
}
}
userdata.json:
{
"username":"umer",
"password":"123456",
"pin" : "123"
}
I figured it out by some trial and error since I don't have much knowledge about openssl. Anyways the process for decrypting an encrypted file should be the following.
Terminal:
String -> (Encrypt) -> Encrypted String -> (convert to base64) -> EncryptedBase64EncodedString -> (Decrypt) -> Original String
Programmtically:
EncryptedBase64EncodedString -> (convert from base64 to normal string [Use Default parameters only! No Padding or other constants for decoding base64 string]) -> Pass private_key & decoded string to Cipher -> Profit.
The resultant code is:
import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemReader;
import android.util.Base64;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
public static void main(String privateKeyPath, String publicKeyPath, String encodedStringPath, boolean isPublicKeyAndDataBase64) throws FileNotFoundException,
IOException, NoSuchAlgorithmException, NoSuchProviderException {
Security.addProvider(new BouncyCastleProvider());
String encodedString = readFileAsString(encodedStringPath);
if(isPublicKeyAndDataBase64) {
KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
Timber.w("Encoded String converted from base64: %s", decodeBase64ToBytesa(encodedString));
try {
PrivateKey priv = generatePrivateKey(factory, privateKeyPath);
Timber.i(String.format("Instantiated private key: %s", priv));
decipherString(priv, decodeBase64ToBytesa(encodedString));
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return;
}
else
Timber.w("Encoded String: %s", encodedString);
KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
try {
PrivateKey priv = generatePrivateKey(factory, privateKeyPath);
Timber.i(String.format("Instantiated private key: %s", priv));
decipherString(priv, encodedString.getBytes());
PublicKey pub = generatePublicKey(factory, publicKeyPath);
Timber.i(String.format("Instantiated public key: %s", pub));
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
private static PrivateKey generatePrivateKey(KeyFactory factory,
String filename) throws InvalidKeySpecException,
FileNotFoundException, IOException {
PemFile pemFile = new PemFile(filename, false);
byte[] content = pemFile.getPemObject().getContent();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
return factory.generatePrivate(privKeySpec);
}
private static PublicKey generatePublicKey(KeyFactory factory,
String filename) throws InvalidKeySpecException,
FileNotFoundException, IOException {
PemFile pemFile = new PemFile(filename, false);
byte[] content = pemFile.getPemObject().getContent();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
return factory.generatePublic(pubKeySpec);
}
private static void decipherString(PrivateKey privateKey, byte[] encodedStringData) {
byte[] dectyptedText = null;
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
dectyptedText = cipher.doFinal(encodedStringData);
Timber.w("Deciphered text is: %s", new String(dectyptedText));
}
catch (Exception e) {
e.printStackTrace();
}
}
static class PemFile {
private PemObject pemObject;
public PemFile(String filename, boolean isBase64) throws FileNotFoundException, IOException {
PemReader pemReader = null;
if(isBase64) {
Timber.i("reading base64 encoded pem file. base64DecodedString: %s", decodeBase64(filename));
pemReader = new PemReader(new StringReader(decodeBase64(filename)));
}
else
pemReader = new PemReader(new InputStreamReader(
new FileInputStream(filename)));
try {
this.pemObject = pemReader.readPemObject();
}
catch (Exception e) {
e.printStackTrace();
}finally {
pemReader.close();
}
}
public PemObject getPemObject() {
return pemObject;
}
}
I have an RSA private key that I am trying to decrypt another files contents that has an AES key in it. So far all I can seem to get to return from the processes is jargon. Not really sure what I am doing wrong in the below code. I have looked on google and have seen this done at least a 100 different ways.
import java.io.*;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.GeneralSecurityException;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
public class RsaEncryption {
private Cipher _pkCipher;
public RsaEncryption() throws GeneralSecurityException {
// create RSA public key cipher
_pkCipher = Cipher.getInstance("RSA");
}
public String loadKey(File in, String privateKey) throws GeneralSecurityException, IOException, Exception {
privateKey = privateKey.replaceAll("-+.*?-+", "");
byte[] encodedKey = Base64.decodeBase64(privateKey);
// create private key
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey pk = (RSAPrivateKey) kf.generatePrivate(privateKeySpec);
// read AES key
_pkCipher.init(Cipher.DECRYPT_MODE, pk);
byte[] encryptedBytes = FileUtils.readFileToByteArray(in);
ByteArrayInputStream fileIn = new ByteArrayInputStream(encryptedBytes);
CipherInputStream cis = new CipherInputStream(fileIn, _pkCipher);
DataInputStream dis = new DataInputStream(cis);
byte[] decryptedData = new byte[32];
dis.read(decryptedData);
String key = new String(decryptedData);
return key;
}
}
UPDATE
New way with bouncy castles pem converter still not working
import java.io.StringReader;
import java.io.File;
import java.io.IOException;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.GeneralSecurityException;
import java.security.interfaces.RSAPublicKey;
import javax.crypto.Cipher;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
public class RsaEncryption {
private Cipher _pkCipher;
private RSAPrivateKey _PrivateKey;
private RSAPublicKey _PublicKey;
public RsaEncryption(String privateKey) throws GeneralSecurityException, IOException {
loadKey(privateKey);
// create RSA public key cipher
_pkCipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
}
private void loadKey(String privateKey) throws IOException {
PEMParser pemParser = new PEMParser(new StringReader(privateKey));
PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair keyPair = converter.getKeyPair(pemKeyPair);
_PrivateKey = (RSAPrivateKey) keyPair.getPrivate();
_PublicKey = (RSAPublicKey) keyPair.getPublic();
pemParser.close();
}
public String decrypt(File in) throws GeneralSecurityException , IOException{
_pkCipher.init(Cipher.DECRYPT_MODE, _PrivateKey);
byte[] encryptedBytes = FileUtils.readFileToByteArray(in);
String key = new String(_pkCipher.doFinal(encryptedBytes));
System.out.println(key);
return key;
}
public RSAPrivateKey getPrivateKey() { return _PrivateKey; }
public RSAPublicKey getPublicKey() { return _PublicKey; }
}
RSA can only encrypt a small amount of data which must be processed as a chunk. You don't need a stream for that. Simply call
byte[] aesKey = _pkCipher.doFinal(FileUtils.readFileToByteArray(in));
to get the AES key.
JCE jars where in the wrong directory worked just fine once they got put in the correct directory.