I have a symmetric Key in my JKS (Java Key Store) file, I want to wrap my private key with a symmetric key.
Again I am using wrappedBytes to PrivateKey Object. And finally I want the KeyPair Object.
The below code gives the following error message:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=125, too big.**
public KeyPair wrapPrivateKeyWithSymmetricKey(KeyPair keyPair) {
try {
PrivateKey priv = keyPair.getPrivate();
SecretKey symmetricKey = "bjksabfkasdbgvkasbvkkj";//symmetricKey from jks file
//wrapping Private Key
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.WRAP_MODE, symmetricKey);
byte[] wrappedKey = cipher.wrap(priv);
//wrappedKey bytes to PrivateKey Object
KeyFactory keyFactory = KeyFactory.getInstance(priv.getAlgorithm());
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(wrappedKey);
PrivateKey privateKey2 = keyFactory.generatePrivate(privateKeySpec); //above Error Throwing in this line
return new KeyPair(keyPair.getPublic(), privateKey2);;
}
How can I solve this?
In your example, wrappedBytes isn't in a PKCS #8 format. It's simply some AES encrypted blocks—essentially random data—with no encoded structure.
If you want to create an encrypted PKCS #8 (formally, EncryptedPrivateKeyInfo) you'll need a library that handles that. The built-in API you are trying to use only handles its clear-text payload, PrivateKeyInfo (as described in its documentation).
There isn't much to the wrapper, and you could write the necessary DER coding yourself, or use a library like BouncyCastle.
Here's code, using BouncyCastle to encoded and decode the EncryptyedPrivateKeyInfo structure. The useless class provided by the JCE doesn't work, because of poor handling of the key encryption algorithm identifier and its parameters.
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
final class PKCS8
{
private static final ASN1ObjectIdentifier AES = ASN1ObjectIdentifier.getInstance(NISTObjectIdentifiers.id_aes128_CBC);
static RSAPublicKey toPublic(RSAPrivateCrtKey pvt)
throws GeneralSecurityException
{
RSAPublicKeySpec pub = new RSAPublicKeySpec(pvt.getModulus(), pvt.getPublicExponent());
KeyFactory f = KeyFactory.getInstance("RSA");
return (RSAPublicKey) f.generatePublic(pub);
}
static byte[] encrypt(SecretKey secret, PrivateKey pvt)
throws Exception
{
Cipher enc = Cipher.getInstance("AES/CBC/PKCS5Padding");
enc.init(Cipher.WRAP_MODE, secret);
ASN1Encodable params = new DEROctetString(enc.getIV());
AlgorithmIdentifier algId = new AlgorithmIdentifier(AES, params);
byte[] ciphertext = enc.wrap(pvt);
return new EncryptedPrivateKeyInfo(algId, ciphertext).getEncoded();
}
static PrivateKey decrypt(SecretKey secret, byte[] pkcs8)
throws Exception
{
EncryptedPrivateKeyInfo info = new PKCS8EncryptedPrivateKeyInfo(pkcs8).toASN1Structure();
AlgorithmIdentifier id = info.getEncryptionAlgorithm();
byte[] iv = ((ASN1OctetString) id.getParameters()).getOctets();
Cipher dec = Cipher.getInstance("AES/CBC/PKCS5Padding");
dec.init(Cipher.UNWRAP_MODE, secret, new IvParameterSpec(iv));
return (PrivateKey) dec.unwrap(info.getEncryptedData(), "RSA", Cipher.PRIVATE_KEY);
}
}
Related
I have written a code in beanshell for encryption and decryption in Jmeter but somehow its not working. I am getting error : In file: inline evaluation of: ``import java.security.InvalidAlgorithmParameterException; import java.security.In . . . '' Encountered "}" at line 19, column 82.
I have added the crypto jar in test plan but issue persists.
Attached is the code.
Working :
I am generating a random string(R1) and encryption it with a public key using RSA algo.
Using the key R1, I need to encrypt the request body using AES algo, CBC mode, PKCS7Padding.
package com.sample.feedbackrating;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
public static IvParameterSpec generateIv() {
byte[] iv = new byte[16];
return new IvParameterSpec(iv);
}
public static String encrypt(String algorithm, String input, String secretKey, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException
{
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, new
SecretKeySpec(secretKey.getBytes(), "AES"), iv);
byte[] cipherText = cipher.doFinal(input.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
}
public static String decrypt(String algorithm, String cipherText, String secretKey, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException,InvalidAlgorithmParameterException, InvalidKeyException,BadPaddingException, IllegalBlockSizeException
{
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey.getBytes(), "AES"), iv);
byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(plainText);
}
public static String generateSecretKey() {
return UUID.randomUUID().toString().replace("-", "");
}
}
Encountered "}" is a syntax error so double check your Beanshell script for eventual missing semicolons or closing brackets or whatever. You can use online lint tool like this one to see where exactly the problem is
In general I cannot reproduce your issue:
So it's either a copy-paste issue or a Beanshell-related problem, be aware that starting from JMeter 3.1 you're supposed to use JSR223 Test Elements and Groovy language for scripting, especially for "heavy" tasks like cryptographic operations.
The original goal is:
Generate a https url where one of parameters is PKCS7 detached signature (RSA, SHA-256, UTF-8, BASE64).
What do I have:
private key (.key file begin with "-----BEGIN RSA PRIVATE KEY-----",
end like this "kIng0BFt5cjuur81oQqGJgvU+dC4vQio+hVc+eAQTGmNQJV56vAHcq4v
-----END RSA PRIVATE KEY-----")
self signed certificate (.cer file begin with "-----BEGIN CERTIFICATE-----",
end like this "xwRtGsSkfOFL4ehKn/K7mgQEc1ZVPrxTC7C/g+7grbKufvqNmsYW4w==
-----END CERTIFICATE-----")
data to sign
I found a java code that do almost what I need.
Method signature:
public static String sign(PrivateKey privateKey,
X509Certificate certificate,
String data);
Now I'm stuck on how to get PrivateKey and X509Certficiate classes from given files.
I looked at many examples and got confused by these moments:
1.
KeyStore ks = KeyStore.getInstance("pkcs12");
or
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
Didn't find alternatives for PKCS7 standard.
A snippet of method that builds PrivateKey using bouncycastle library:
inputStream = Files.newInputStream(privateKeyFile.toPath());
reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
pemParser = new PEMParser(reader);
PEMDecryptorProvider decryptorProvider = new JcePEMDecryptorProviderBuilder()
.setProvider(PROVIDER)
.build(privateKeyPassword.toCharArray());
PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) pemParser.readObject();
PEMKeyPair keyPair = encryptedKeyPair.decryptKeyPair(decryptorProvider);
...
In this example I have to provide some privateKeyPassword to PEMDecryptorProvider. What is the point of this password and where can I get it?
From keyPair value I can get both privateKey and publicKey.
What is the connection between publicKey from PEMKeyPair and my certificate ? Are they the same?
Any help will be appreciated, thanks!
You don't need bouncycastle to read in the public key as Java's CertificateFactory directly supports the format of your .cer file.
The private key appears to be in a PKCS1 format that openssl can produce. If you wish to keep that format this answer shows how to extract the private key. Combining the two, here is a short snippet to read in a certificate and a private key.
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import java.io.FileInputStream;
import java.io.FileReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class Main {
private static PrivateKey readPrivateKey(String filename) throws Exception {
PEMParser pemParser = new PEMParser(new FileReader(filename));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
KeyPair kp = converter.getKeyPair(pemKeyPair);
return kp.getPrivate();
}
private static X509Certificate readCertificate(String filename) throws Exception {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certificateFactory.generateCertificate(new FileInputStream(filename));
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
PrivateKey privateKey = readPrivateKey("myKey.priv");
X509Certificate cert = readCertificate("mycert.cer");
}
}
I have generated an ECDSA signature in Java and I would like to get the R and S values from it. It is my understanding that the signature I have generated is DER encoded. Can someone please provide me with some Java code (maybe using Bouncy Castle) to retrieve the R and S values as BigIntegers?
Note: In case it helps, I generated the signature using a built in provider via the JCE's Signature class and the signature lengths for my P_256 EC key pair hover between 70 and 72 bytes usually.
I was able to solve this myself. In case it helps anyone here is how I did it (most exception handling has been stripped for readability):
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.Signature;
import java.security.spec.ECGenParameterSpec;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class DecodeEcdsaSignature {
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] signature = getSignature();
ASN1Primitive asn1 = toAsn1Primitive(signature);
if (asn1 instanceof ASN1Sequence) {
ASN1Sequence asn1Sequence = (ASN1Sequence) asn1;
ASN1Encodable[] asn1Encodables = asn1Sequence.toArray();
for (ASN1Encodable asn1Encodable : asn1Encodables) {
ASN1Primitive asn1Primitive = asn1Encodable.toASN1Primitive();
if (asn1Primitive instanceof ASN1Integer) {
ASN1Integer asn1Integer = (ASN1Integer) asn1Primitive;
BigInteger integer = asn1Integer.getValue();
System.out.println(integer.toString());
}
}
}
}
private static ASN1Primitive toAsn1Primitive(byte[] data) throws Exception
{
try (ByteArrayInputStream inStream = new ByteArrayInputStream(data);
ASN1InputStream asnInputStream = new ASN1InputStream(inStream);)
{
return asnInputStream.readObject();
}
}
private static byte[] getSignature() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA");
ECGenParameterSpec ecParameterSpec = new ECGenParameterSpec("P-256");
keyPairGenerator.initialize(ecParameterSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(keyPair.getPrivate());
signature.update("message to sign".getBytes("UTF-8"));
return signature.sign();
}
}
I'm trying to create a KeyAgreement with bouncyCastle. It needs EC because I'm working with javacards.
Unfortunately it gives an error when I use Security.addProvider(new BouncyCastleProvider());:
Multiple markers at this line
- Syntax error, insert ")" to complete MethodDeclaration
- Syntax error, insert "SimpleName" to complete
QualifiedName
- Syntax error, insert "Identifier (" to complete
MethodHeaderName
- Syntax error on token ".", # expected after this token
If I leave that line out, it gives an error:
Exception in thread "main" java.security.NoSuchProviderException: no such provider: BC
at sun.security.jca.GetInstance.getService(Unknown Source)
at javax.crypto.JceSecurity.getInstance(JceSecurity.java:97)
at javax.crypto.KeyAgreement.getInstance(KeyAgreement.java:230)
at pract_LCP.keyAgreement.<init>(keyAgreement.java:30)
at pract_LCP.main.main(main.java:12)
So I have to force bouncyCastle I guess.
my code:
package pract_LCP;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import org.bouncycastle.*;
import javax.crypto.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class keyAgreement {
Security.addProvider(new BouncyCastleProvider());
//Security.addProvider(new BouncyCastleProvider());
private String StringPrivatekey="23dc5cf316583c2e60e36c405b10d03541f4a76f6da2852d";
private String StringPublicKey="47595243a6737a00fe5d911c1a3305be5c108bf4f68b01945c40358143f28077fad4a8a51f190b6d8bb8b580c88011335";
private PrivateKey convertPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException{
byte[] privateKeyBytes=privateKey.getBytes();
KeyFactory kf = KeyFactory.getInstance("EC"); // or "EC" or whatever
PrivateKey converted = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
return converted;
}
private PublicKey convertPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException{
byte[] publicKeyBytes=publicKey.getBytes();
KeyFactory kf = KeyFactory.getInstance("EC"); // or "EC" or whatever
PublicKey converted = kf.generatePublic(new PKCS8EncodedKeySpec(publicKeyBytes));
return converted;
}
public keyAgreement() throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException{
KeyAgreement aKeyAgree = KeyAgreement.getInstance("ECDH", "BC");
aKeyAgree.init(convertPrivateKey(StringPrivatekey));
aKeyAgree.doPhase(convertPublicKey(StringPublicKey), true);
byte[] aSecret = aKeyAgree.generateSecret();
String str = new String(aSecret, StandardCharsets.UTF_8);
System.out.println("keyagr"+str);
}
}
I am totally new to security and signature verification and so far I couldn't find a place which explained the basics of signature verification. I need to verify the signature of a file by obtaining the public key from the appropriate certificate available from certificate store. the tutorial in Java (https://docs.oracle.com/javase/tutorial/security/apisign/versig.html) doesn't teach how to obtain a certificate from the trusted certificate store and verify using that. I went through Bouncy castle WIKI http://www.bouncycastle.org/wiki/display/JA1/BC+Version+2+APIs but its not really explanatory for a beginner. How do I do this? Given a signed file, how can I check for its public key from the certificate store and verify if its the right person who has sent the file? Please advice.
Because you did not provide what build management you use, I assume it will be Maven.
First, include BouncyCastle in your dependency
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.53</version>
</dependency>
After that, you need to make a util class that you will be using for sign or verify the certificate. Something like this:
package your.pack.location;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* Author: harunalfat
*/
public class SignatureUtils {
private static final Logger log = LogManager.getLogger(SignatureUtils.class);
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
byte[] data = plainText.getBytes("ISO-8859-1");
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privateKey);
signature.update(data);
return Base64.toBase64String(signature.sign());
}
public static boolean verify(String plainText, String signString, PublicKey publicKey) throws Exception{
byte[] data = plainText.getBytes("ISO-8859-1");
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initVerify(publicKey);
signature.update(data);
byte[] signByte = Base64.decode(signString);
return signature.verify(signByte);
}
private static PemObject getPemObjectFromResource(String fileLocation) throws IOException {
Resource resource = new ClassPathResource(fileLocation);
InputStream is = resource.getInputStream();
PemObject pemObject = new PemReader(new InputStreamReader( is )).readPemObject();
return pemObject;
}
private static X509EncodedKeySpec getPubKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {
PemObject pemObject = getPemObjectFromResource(fileLocation);
byte[] data = pemObject.getContent();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(data);
return keySpec;
}
private static PKCS8EncodedKeySpec getPriKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {
PemObject pemObject = getPemObjectFromResource(fileLocation);
byte[] data = pemObject.getContent();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(data);
return keySpec;
}
public static PublicKey getPublicKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
KeySpec keySpec = getPubKeySpec(fileLocation);
return kf.generatePublic(keySpec);
}
public static PrivateKey getPrivateKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
KeySpec keySpec = getPriKeySpec(fileLocation);
return kf.generatePrivate(keySpec);
}
}
And then you will use it like this
package your.another.pack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import static org.junit.Assert.assertTrue;
/**
* Author: harunalfat
*/
public class SignatureUtilsTest {
private static final Logger log = LogManager.getLogger(SignatureUtilsTest.class);
private static final String PLAIN = "attack at dawn";
#Test
public void testSignAndVerify() throws Exception {
Security.addProvider(new BouncyCastleProvider()); // <-- IMPORTANT!!! This will add BouncyCastle as provider in Java Security
PrivateKey privateKey = SignatureUtils.getPrivateKey("key/private2.pem"); // This is located on src/main/resources/key/private2.pem
PublicKey publicKey = SignatureUtils.getPublicKey("key/public2.pem"); // This is located on src/main/resources/key/public2.pem
// In this example, I use junit test, so it will be on src/test/resources/...
log.info("Private Key : "+Base64.encodeBase64String(privateKey.getEncoded()));
log.info("Public Key : "+Base64.encodeBase64String(publicKey.getEncoded()));
String sign = SignatureUtils.sign(PLAIN, privateKey);
log.info("Plain String : "+PLAIN);
log.info("Sign : "+sign);
boolean result = SignatureUtils.verify(PLAIN,sign, publicKey);
log.info("Result : "+result);
assertTrue(result);
}
}
Of course, you can change the Signature instance with another Algorithm. In my case I use "SHA1WithRSA", but you get the point right?
With this, someone will encrypt their data using their private key, and send it to you. After that, you will verify the data using the public key they give.
In example, Bob send to you message about money amount he sent to you ($5000), and sign it using their private key, become encrypted. When the data arrived to you, you know Bob supposed to send $5000, then you verify the encrypted data with text $5000 and public key Bob share, but is it really $5000 OR does it comes from Bob?
If the data has been changed, OR when someday you ask for some Money to Bob, but the message tapped by someone else and s/he send you the amount message with private key other than Bob's, you will know.
Feel free to ask :)