Calculate HMAC-SHA1 Signature using JavaScript [duplicate] - java

This question already has answers here:
one way SHA1 hash javascript implementation?
(3 answers)
Closed 8 years ago.
I am writing a JavaScript Client Application that needs to communicate with Server. I tried to implement the API, but i stuck on one method and I need help.
Infect i don't know how to translate this from Java to JavaScript (I don't know where to find analog libraries written in javascript that are used in this method):
import java.security.SignatureException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* This class defines common routines for generating
* authentication signatures for AWS requests.
*/
public class Signature {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
/**
* Computes RFC 2104-compliant HMAC signature.
* * #param data
* The data to be signed.
* #param key
* The signing key.
* #return
* The Base64-encoded RFC 2104-compliant HMAC signature.
* #throws
* java.security.SignatureException when signature generation fails
*/
public static String calculateRFC2104HMAC(String data, String key)
throws java.security.SignatureException
{
String result;
try {
// get an hmac_sha1 key from the raw key bytes
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
// get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
// compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(data.getBytes());
// base64-encode the hmac
result = Encoding.EncodeBase64(rawHmac);
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
}
return result;
}
}
This method is from AWS Documentations:
Java Sample Code for Calculating HMAC-SHA1 Signatures
I am asking if someone can give me some references (websites) where I can find solution or analog libraries, written in javascript.
I searched AWS Documentation and SDK for JavaScript and I couldn't find translation in JS.
Thanks very much in advance.

Hi i think this might be helpful for you.
Here you can find out the link to calculate hmac sha1:
http://caligatio.github.io/jsSHA/
Here you can find the source code in javascript.
https://github.com/Caligatio/jsSHA/releases/tag/v1.5.0

Related

Asymmetric cryptographic algorithm for large text data

I am looking for a solution that meets the following requirements:
Let's assume, there are: the Application installed on a computing device and controlling it, Users that use this application, and Maintainers, that provide some support for the application. Application has the Configuration, for example in the file or database. Configuration is updated manually by Maintainers when required, for example weekly. Configuration contains, for example, list of emails, Application sends it's alerts to. Let's assume, that it is not possible for Users to modify the Application in any way. Although, Application is written in Java, so it is easy for Users to copy and debug it. Internally, the Application decrypts the Configuration in the Application's memory, in order to use the Configuration.
Users shall able to view the Configuration from inside the Application. Users shall be unable to change the Configuration, or to use their own (which is basically the same), for example to change any email or remove existing email or add a new one.
Additional requirement, that is not mandatory: It shall not be possible to directly view the Configuration without the Application. I understand it's hardly really possible, so, it shall be at least just difficult, like decryption necessary to view the Configuration without the Application.
Question: how to achieve this and is it possible at all?
Possible solutions I can realize, and attacks:
1) To use some signing. To sign each Configuration with some Digest and to check the Digest in the Application then. Attack: as I understand, App shall calculate the Digest using the public key stored in it. Then the Application shall compare calculated Digest with the one provided with the Configuration. So, attack is simple : Users will modify the Configuration, then debug the Application, put a breakpoint on the place where Application has already calculated Digest for comparing it with the stored one, then Users could dump the calculated Digest and replace provided Digest with this calculated one.
2) To use hybrid encryption. In this case the attack is the same: breakpoint in the place where decrypted symmetric key is available, dump this key, then to use it for the new Configuration encryption.
3) To use asymmetric encryption. Maintainers encrypt Configuration with the public key, then Application decrypts the Configuration with the private key. Attack is simple : Users could dump private key from the Application and derive a new public key, then use it for encryption.
Is there a solution, like "encrypt with the public key, then decrypt with private" for large chunks of data (up to 10 kb), or maybe any other possible way to achieve that?
Thank you
Colleagues,
I have an application that shall receive and store some read-only data. The data shall be available for users to read but not available to change. For example, assume that there are text files with data, that keep some texts available for end users to read but not available for edits. New files shall be periodically received by the application, and shall be available for users in the same read-only mode.
So, local content (for example files content) is accessible by users. Users can copy content from application, save decrypted copies and so on. The only thing I need to prevent, is to replace existing data with any other data, including changing the content or adding new by users. I do not need changes detection, I need to make changes impossible (ok, as hard as possible).
I suppose, that the easiest way to do that, is to encrypt data with some secret key and to include the public key in the app, so app could decrypt and show the data, but without the secret key users would not be able to change the content.
I know, that standard RSA supports just a small data blocks to encrypt, usually slightly less than the key length. (I made tests and found that for RSA 2048 Java throws an exception after 254 bytes) I also read, that it's not a good idea to split source data to chunks and to encrypt these chunks then. I read that it is advised to use symmetric key, like AES, for encryption and decryption, then to encrypt this AES key with RSA key pair.
I see a big (as I suppose) security risk in this scenario - as my app is written in Java, it's quite easy to debug it and to dump the decrypted AES key, then to use it for data modifications, even without any modifications of the application itself.
So, my question is: how to solve this problem and what is considered to be secure to use in such a case?
Thank you
Update:
Of course, users are able to copy the file and to use a copy as they want. The goal is, to disallow users to change data used by application, not a copy of these data. In case of asymmetric encryption it is easy to achieve - I encrypt data with my private key, pass to app, app in runtime decrypts data with it's public key and use. In case someone would like to change data, app would not decrypt the data properly and data will be spoiled and app would fail to work till data will be reverted back.
As #President James K. Polk stated in one of his comments the only solution in my humble opinion is to sign the read-only data and use it only if the data is verified. In your "Possible solution & attack" section you write that the program compares some Digits that can easily been overwritten. Usually the signature is done with the (SHA256-)
hash of the data, but you can sign the complete data without hashing it first and 4 KB of data does not bring performance issues on my desktop Java.
I setup a full working example that simulates the maintainer-side and the app-side and as a little goodie I encrypt the plaintext with AES CBC (key generated out of signature). I know that this mode of encryption is not the "best way" as the data does not need to be kept totally secret but not direct visible it's a good solution.
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;
import java.security.*;
import java.util.Arrays;
import java.util.Random;
public class Cyptosystem {
public static void main(String[] args) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
System.out.println("Cryptosystem for \nhttps://stackoverflow.com/questions/62361876/asymmetric-cryptographic-algorithm-for-large-text-data/62398723#62398723");
System.out.println("Warning: this program is experimental and has no proper exception handling");
byte[] plaintext = new byte[4000]; // content to get secured, provided by maintainers
byte[] ciphertext = new byte[0]; // encryped plaintext
byte[] dataForApp = new byte[0]; // initvector | ciphertext
new Random().nextBytes(plaintext);
// generate rsa keypair
System.out.println("generate the RSA keypair");
KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom random = new SecureRandom();
rsaGenerator.initialize(4096, random);
KeyPair rsaKeyPair = rsaGenerator.generateKeyPair();
PrivateKey rsaPrivateKey = rsaKeyPair.getPrivate(); // for signature
PublicKey rsaPublicKey = rsaKeyPair.getPublic(); // for verification, implemented in app resources
System.out.println("sign & encrypt the plaintext");
// signature done by maintainers
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(rsaPrivateKey);
sig.update(plaintext);
byte[] signature = sig.sign(); // provide to app as byte array, hexstring or base64 as you like
// encrypt plaintext with signature
byte[] initvector = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(initvector); // random initvector
// you can use another aes mode for encryption e.g. gcm
// you can use a hmac as key derivation ...
// i'm using sha256 to get a 32 byte long key
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] aeskey = md.digest(signature);
SecretKeySpec keySpec = new SecretKeySpec(aeskey, "AES");
IvParameterSpec ivKeySpec = new IvParameterSpec(initvector);
Cipher aesCipherEnc = Cipher.getInstance("AES/CBC/PKCS5PADDING");
aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec, ivKeySpec);
ciphertext = aesCipherEnc.doFinal(plaintext);
// copy iv | ciphertext
dataForApp = new byte[ciphertext.length + 16]; // initvector length 16 byte
System.arraycopy(initvector, 0, dataForApp, 0, initvector.length);
System.arraycopy(ciphertext, 0, dataForApp, initvector.length, ciphertext.length);
// send the dataForApp to the app (as byte array, hex string, base64 as you like
System.out.println("dataForApp length: " + dataForApp.length);
// app side, receive dataForApp & signature, already has public key
byte[] dataForAppApp = dataForApp.clone();
byte[] signatureApp = signature.clone();
System.out.println("decrypt and verify the signature");
// get initvector & ciphertext
byte[] initvectorApp = new byte[16];
byte[] ciphertextApp = new byte[(dataForAppApp.length - 16)];
System.arraycopy(dataForAppApp, 0, initvectorApp, 0, 16);
System.arraycopy(dataForAppApp,16, ciphertextApp, 0, (dataForAppApp.length - 16));
// decrypt data
MessageDigest mdApp = MessageDigest.getInstance("SHA-256");
byte[] aeskeyApp = md.digest(signature);
SecretKeySpec keySpecApp = new SecretKeySpec(aeskeyApp, "AES");
IvParameterSpec ivKeySpecApp = new IvParameterSpec(initvectorApp);
Cipher aesCipherDec = Cipher.getInstance("AES/CBC/PKCS5PADDING");
aesCipherDec.init(Cipher.DECRYPT_MODE, keySpecApp, ivKeySpecApp);
byte[] decrypttext = aesCipherDec.doFinal(ciphertextApp);
System.out.println("plaintext equals decrypttext: " + Arrays.equals(decrypttext, plaintext));
// don't use the ciphertext as the signature is not verified
Signature sigApp = Signature.getInstance("SHA256withRSA");
sigApp.initVerify(rsaPublicKey);
sigApp.update(decrypttext);
boolean signatureVerified = sigApp.verify(signatureApp);
System.out.println("signatureApp verified: " + signatureVerified);
System.out.println("if verified == true we can use the decrypttext");
}
}
Firstly, don't roll your own crypto. Cryptography is very hard, and if you make any mistake, it will have vulnerabilities you could have avoided by using a well-established library to do the heavy lifting. You could, for example, use libsodium. It has many abstractions, and probably has a solution for what you need.
With that out of the way, let's discuss how that would make it safer: the user needs to be able to read the contents, but not edit it. What exactly do you mean by "cannot edit"? Can he not be able to modify anything locally, or just not be able to upload it to your server as if he was authorized to do so?
If the former, encryption can't help you much - you need to be able to decrypt it locally, so an attacker can always dump your process' memory to get to the data - sure it would be hard, but definitely possible. Just not allowing people to edit/save/download in your application would be the strongest guarantee you can get.
If the latter, then using authentication would be the way to go - be that a simple method like HTTP basic authentication with user and password, or signing the file to be uploaded. Dealing with authentication on your application's side would be the more practical way.
Asymmetric encryption is done with the Public Key and the decryption is performed with the Private Key. As the app has to be capable to decrypt the data the app needs to know the Private Key and that's the problem with the common used algorithms, because for RSA or ECIES the Public key can get derived from the Private key. Therefore it's not a real problem to derive the Public key and store changed/appended data after encryption with the Public key.
Second thing is - you did not specify how "large" your text will be - some KB, MB, GB?
Some months ago I tested some "new" algorithms that are "Post quantum safe" and as an example I used the McEliece Fujisaki algorithm that is available with the Bouncy Castle Crypto provider (I used version 1.65, bcprov-jdk15to18-165.jar).
The program creates a 50 MB large byte array that gets encrypted with the Public key and decrypted with the Private key.
At the moment I did not find any Public key deriving methods so you definitely need to know the Private and the Public key.
I did not test larger byte arrays because this parameter depends on the memory of the target system (you need the double memory
as the complete data is captured in ciphertextByte and then again in decryptedtextByte).
Edit June 16th 2020: President James K. Polk programmed a method that easily retrieves a public key from a given private key. The source is available in his GitHub-Repo (https://github.com/james-k-polk/McEliece/blob/master/McElieceRecoverPublicFromPrivate.java) and for later convenience shown at the end of this answer. So everyone that has access to a private McEliece key is been able to encrypt data with the retrieved public key! Thanks to President James for his help.
Here are the outputs on the console:
McEliece Fujisaki Pqc Encryption
key generation
PrivateKey length: 4268 algorithm: McEliece-CCA2 format: PKCS#8
PublicKey length: 103429 algorithm: McEliece-CCA2 format: X.509
initialize cipher for encryption
pt length: 52428800 (50 mb)
ct length: 52429056 (50 mb)
initialize cipher for decryption
dt length: 52428800 (50 mb)
compare plaintext <-> decryptedtext: true
class McElieceFujisakiPqcEncryptionLargeData.java
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.mceliece.*;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PrivateKey;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PublicKey;
import java.security.*;
import java.util.Arrays;
import java.util.Random;
public class McElieceFujisakiPqcEncryptionLargeData {
public static void main(String[] args) throws InvalidCipherTextException {
System.out.println("McEliece Fujisaki Pqc Encryption");
if (Security.getProvider("BCPQC") == null) {
Security.addProvider(new BouncyCastlePQCProvider());
// used Bouncy Castle: bcprov-jdk15to18-165.jar
}
System.out.println("key generation");
SecureRandom keyRandom = new SecureRandom();
McElieceCCA2Parameters params = new McElieceCCA2Parameters();
McElieceCCA2KeyPairGenerator mcElieceCCA2KeyGen = new McElieceCCA2KeyPairGenerator();
McElieceCCA2KeyGenerationParameters genParam = new McElieceCCA2KeyGenerationParameters(keyRandom, params);
mcElieceCCA2KeyGen.init(genParam);
AsymmetricCipherKeyPair pair = mcElieceCCA2KeyGen.generateKeyPair();
AsymmetricKeyParameter mcEliecePrivateKey = pair.getPrivate();
AsymmetricKeyParameter mcEliecePublicKey = pair.getPublic();
PrivateKey privateKey = new BCMcElieceCCA2PrivateKey((McElieceCCA2PrivateKeyParameters) pair.getPrivate()); // conversion neccessary only for key data
PublicKey publicKey = new BCMcElieceCCA2PublicKey((McElieceCCA2PublicKeyParameters) pair.getPublic()); // conversion neccessary only for key data
System.out.println("PrivateKey length: " + privateKey.getEncoded().length + " algorithm: " + privateKey.getAlgorithm() + " format: " + privateKey.getFormat());
System.out.println("PublicKey length: " + publicKey.getEncoded().length + " algorithm: " + publicKey.getAlgorithm() + " format: " + publicKey.getFormat());
// generate cipher for encryption
System.out.println("\ninitialize cipher for encryption");
ParametersWithRandom param = new ParametersWithRandom(mcEliecePublicKey, keyRandom);
McElieceFujisakiCipher mcElieceFujisakiDigestCipher = new McElieceFujisakiCipher();
mcElieceFujisakiDigestCipher.init(true, param);
// random plaintext
byte[] plaintext = new byte[52428800]; // 50 mb, 50 * 1024 * 1024
new Random().nextBytes(plaintext);
System.out.println("pt length: " + plaintext.length + " (" + (plaintext.length / (1024 * 1024)) + " mb)");
byte[] ciphertext = mcElieceFujisakiDigestCipher.messageEncrypt(plaintext);
System.out.println("ct length: " + ciphertext.length + " (" + (ciphertext.length / (1024 * 1024)) + " mb)");
System.out.println("\ninitialize cipher for decryption");
mcElieceFujisakiDigestCipher.init(false, mcEliecePrivateKey);
byte[] decryptedtext = mcElieceFujisakiDigestCipher.messageDecrypt(ciphertext);
System.out.println("dt length: " + decryptedtext.length + " (" + (decryptedtext.length / (1024 * 1024)) + " mb)");
System.out.println("\ncompare plaintext<-> decryptedtext: " + Arrays.equals(plaintext, decryptedtext));
}
}
Public key Retrieval class by President James K. Polk, available under MIT-Licence:
package com.github.jameskpolk;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.mceliece.*;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PrivateKey;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PublicKey;
import org.bouncycastle.pqc.math.linearalgebra.*;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
public class McElieceRecoverPublicFromPrivate {
private static final SecureRandom RAND = new SecureRandom();
public static AsymmetricCipherKeyPair generateKeyPair() {
McElieceCCA2KeyPairGenerator kpg = new McElieceCCA2KeyPairGenerator();
McElieceCCA2Parameters params = new McElieceCCA2Parameters();
McElieceCCA2KeyGenerationParameters genParam = new McElieceCCA2KeyGenerationParameters(RAND, params);
kpg.init(genParam);
return kpg.generateKeyPair();
}
public static McElieceCCA2PublicKeyParameters recoverPubFromPriv(McElieceCCA2PrivateKeyParameters priv) {
GF2mField field = priv.getField();
PolynomialGF2mSmallM gp = priv.getGoppaPoly();
GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
Permutation p = priv.getP();
GF2Matrix hp = (GF2Matrix) h.rightMultiply(p);
GF2Matrix sInv = hp.getLeftSubMatrix();
GF2Matrix s = (GF2Matrix) sInv.computeInverse();
GF2Matrix shp = (GF2Matrix)s.rightMultiply(hp);
GF2Matrix m = shp.getRightSubMatrix();
GoppaCode.MaMaPe mmp = new GoppaCode.MaMaPe(sInv, m, p);
GF2Matrix shortH = mmp.getSecondMatrix();
GF2Matrix shortG = (GF2Matrix) shortH.computeTranspose();
// generate public key
return new McElieceCCA2PublicKeyParameters(
priv.getN(), gp.getDegree(), shortG,
priv.getDigest());
}
public static void main(String[] args) throws Exception{
// generate a McEliece key pair
AsymmetricCipherKeyPair bcKeyPair = generateKeyPair();
McElieceCCA2PrivateKeyParameters bcPriv = (McElieceCCA2PrivateKeyParameters) bcKeyPair.getPrivate();
BCMcElieceCCA2PrivateKey priv = new BCMcElieceCCA2PrivateKey(bcPriv);
// get the first public key
McElieceCCA2PublicKeyParameters bcPub1 = (McElieceCCA2PublicKeyParameters) bcKeyPair.getPublic();
BCMcElieceCCA2PublicKey pub1 = new BCMcElieceCCA2PublicKey(bcPub1);
// Now generate a second public key for the private key
McElieceCCA2PublicKeyParameters bcPub2 = recoverPubFromPriv(bcPriv);
BCMcElieceCCA2PublicKey pub2 = new BCMcElieceCCA2PublicKey(bcPub2);
// print some info about sizes
System.out.printf("Size of encrypted messages in bits(bytes): %d(%d)\n",
priv.getEncoded().length, priv.getEncoded().length / 8);
System.out.printf("private key length: %d\n", bcPriv.getK());
System.out.printf("public key1 length: %d\n", pub1.getEncoded().length);
System.out.printf("public key2 length: %d\n", pub2.getEncoded().length);
// now encrypt different messages with each public key.
String message1 = "Deposits should be made to account # 3.1415929";
String message2 = "Deposits should be made to account # 2.71828";
ParametersWithRandom params1 = new ParametersWithRandom(bcPub1, RAND);
ParametersWithRandom params2 = new ParametersWithRandom(bcPub2, RAND);
McElieceFujisakiCipher mcElieceFujisakiDigestCipher1 = new McElieceFujisakiCipher();
McElieceFujisakiCipher mcElieceFujisakiDigestCipher2 = new McElieceFujisakiCipher();
mcElieceFujisakiDigestCipher1.init(true, params1);
mcElieceFujisakiDigestCipher2.init(true, params2);
byte[] ciphertext1 = mcElieceFujisakiDigestCipher1.messageEncrypt(message1.getBytes(StandardCharsets.UTF_8));
byte[] ciphertext2 = mcElieceFujisakiDigestCipher2.messageEncrypt(message2.getBytes(StandardCharsets.UTF_8));
System.out.println("ct1 length: " + ciphertext1.length + " (" + (ciphertext1.length / (1024 * 1024)) + " mb)");
System.out.println("ct2 length: " + ciphertext2.length + " (" + (ciphertext2.length / (1024 * 1024)) + " mb)");
mcElieceFujisakiDigestCipher1.init(false, bcPriv);
mcElieceFujisakiDigestCipher2.init(false, bcPriv);
byte[] decryptedtext1 = mcElieceFujisakiDigestCipher1.messageDecrypt(ciphertext1);
byte[] decryptedtext2 = mcElieceFujisakiDigestCipher2.messageDecrypt(ciphertext2);
System.out.printf("Decrypted message 1: %s\n", new String(decryptedtext1, StandardCharsets.UTF_8));
System.out.printf("Decrypted message 2: %s\n", new String(decryptedtext2, StandardCharsets.UTF_8));
}
}
I know, that standard RSA supports just a small data blocks to encrypt,
That's why we use a hybrid cryptosystem. Data are encrypted using a symmetric cipher (data key), and the symmetric data key is encrypted using an asymmetric cipher.
I do not need changes detection, I need to make changes impossible (ok, as hard as possible).
If you are unable to enforce any read-only input/filesystem, then detecting changes is the best you can do. Either it's failed decryption or signature.
Actually to ensure data integrity I'd really use signing, not pure encryption. I see you don't want that, but at the end it will there. Some ciphers / cipher modes are malleable - data can be changed even when encrypted and without any authentication (mac, signature) the decryption is valid and you won't be able to detect the integrity failure.
If you would just rely on application to detect that data are corrupted after failed decryption, you are creating a perfect decryption oracle (breaking security)
I suppose, that the easiest way to do that, is to encrypt data with some secret key and to include the public key in the app, so app could decrypt and show the data, but without the secret key users would not be able to change the content.
Anything hardcoded in your app you can consider as revealed/public. You correctly identified the risk. If you have a dedicated user, nothing prevents the user to change the key in the app and pass invalid data. So - for anything that runs at the client, you can make the integrity stronger, but not perfect. At the end - you have to make some assumptions about adversary's abilities.
I encrypt data with my private key, pass to app, app in runtime decrypts data with it's public key and
In theory (mathematically) you can do that, but most of the current libraries will not let you use the key pairs wrong way (private key is intended for decryption or signing, public for encryption or validation). If you want to code such a solution yourself, you are in risk of creating weaknesses you may not be aware of (proper padding, timing,..)
I believe there are even some weaknesses in the scheme (encrypting using the private key), but I cannot recall details, there are people with deeper knowledge in the topic (e. g. James Polk from comments)
Edit:
Examples to create a signature or MAC : https://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html
btw - using aes-gcm the Java Cipher implementation automatically appends the mac tag to the end of the ciphertext
An interesting question. As far as I can see from your question, the data itself is not secret. Your problem is that the user should not be able to change the data (or that you should be able to detect that he or she has changed it). In this case, a hash function (including possibly a cryptographic hash function) might well be a better approach. See https://en.wikipedia.org/wiki/Hash_function and https://en.wikipedia.org/wiki/Cryptographic_hash_function. If you use a hash function, then you can always detect whether the user has tried to change the data.
The cryptographic hash functions are one-way, so that you do not need to store any key in your program.

RSA algorithm on swift and java

I need to generate public/private key for RSA algorithm on IOS device and send public key to server with encrypted text. Server must read public key and decrypt user message.
I have code on swift:
func generateKeys(){
var publicKey: SecKey?
var privateKey: SecKey?
let publicKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"publicTag".data(using: String.Encoding.utf8)! as NSObject]
let privateKeyAttr: [NSObject: NSObject] = [kSecAttrIsPermanent:true as NSObject, kSecAttrApplicationTag:"privateTag".data(using: String.Encoding.utf8)! as NSObject]
var keyPairAttr = [NSObject: NSObject]()
keyPairAttr[kSecAttrKeyType] = kSecAttrKeyTypeRSA
keyPairAttr[kSecAttrKeySizeInBits] = 4096 as NSObject
keyPairAttr[kSecPublicKeyAttrs] = publicKeyAttr as NSObject
keyPairAttr[kSecPrivateKeyAttrs] = privateKeyAttr as NSObject
_ = SecKeyGeneratePair(keyPairAttr as CFDictionary, &publicKey, &privateKey)
var error:Unmanaged<CFError>?
if #available(iOS 10.0, *) {
if let cfdata = SecKeyCopyExternalRepresentation(publicKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString(options: .lineLength64Characters)
print("public base 64 : \n\(b64Key)")
}
if let cfdata = SecKeyCopyExternalRepresentation(privateKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString(options: .lineLength64Characters)
print("private base 64 : \n\(b64Key)")
}
}
let encrypted = encryptBase64(text: "test", key: publicKey!)
let decrypted = decpryptBase64(encrpted: encrypted, key: privateKey!)
print("decrypted \(String(describing: decrypted))")
self.dismiss(animated: true, completion: nil);
}
func encryptBase64(text: String, key: SecKey) -> String {
let plainBuffer = [UInt8](text.utf8)
var cipherBufferSize : Int = Int(SecKeyGetBlockSize(key))
var cipherBuffer = [UInt8](repeating:0, count:Int(cipherBufferSize))
// Encrypto should less than key length
let status = SecKeyEncrypt(key, SecPadding.PKCS1, plainBuffer, plainBuffer.count, &cipherBuffer, &cipherBufferSize)
if (status != errSecSuccess) {
print("Failed Encryption")
}
let mudata = NSData(bytes: &cipherBuffer, length: cipherBufferSize)
return mudata.base64EncodedString()
}
func decpryptBase64(encrpted: String, key: SecKey) -> String? {
let data : NSData = NSData(base64Encoded: encrpted, options: .ignoreUnknownCharacters)!
let count = data.length / MemoryLayout<UInt8>.size
var array = [UInt8](repeating: 0, count: count)
data.getBytes(&array, length:count * MemoryLayout<UInt8>.size)
var plaintextBufferSize = Int(SecKeyGetBlockSize(key))
var plaintextBuffer = [UInt8](repeating:0, count:Int(plaintextBufferSize))
let status = SecKeyDecrypt(key, SecPadding.PKCS1, array, plaintextBufferSize, &plaintextBuffer, &plaintextBufferSize)
if (status != errSecSuccess) {
print("Failed Decrypt")
return nil
}
return NSString(bytes: &plaintextBuffer, length: plaintextBufferSize, encoding: String.Encoding.utf8.rawValue)! as String
}
This code returns public key in PKCS1. I found the library: SwCrypt
This code helps me to convert PKCS1 into PKCS8 and read public key with java
SwKeyConvert.PublicKey.pemToPKCS1DER(publicKeyPEM)
But I can't decrypt user message. Can you help me with message decryption? I wrote small unit test.
import org.junit.Test;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import static org.junit.Assert.assertNotNull;
public class TestExample {
String publicKeyContent = "MIMAAiMwDQYJKoZIhvcNAQEBBQADgwACDwAwggIKAoICAQC4K4zr1jTi4SSypXbrNeGd2HbYlrDRIPsPcL5a4JwGUKXwi+Rpf8Xh0D4dcRRH+Rtd5F66aqdGnhCBKtU5XsmlT+QssIggihI0iF3LEPsMlKapDrDdSbWmuitVDSSlulReMcN3hEUl8AzlNyu817snZtYESiFxm87QV6xZAcrWzvIdyiStBbngCT/v76tOZDX56IIRGoLMi3WND7538PqqYheh2+oZk05O+Bf5LZc6YteTRLLOSyIIxesoABo8tvaFyIo2ihMcnDRnGAzOMNTLXiQdj2scAMCVr3oiLpU48+Iw8ptOUBDQioW15FsYd3ugZhUX+/mFtMFsYkJyYjyG5HCqAs2/wm6eIjjy1QQwUF2hB8Z7sqyF5KrVZOv6Q7+pB83tT02ZXcDXCdsiP10G3sA4kjc/r9TuQHjCIwZa1LO4tPaO8qAzlROHIkQ4FhdaAM9U9DUq3nBywQLcEVQmXeH1OA1ve96QbMQoN+SRPh0Kq6W0U4TbzvMskQ7bePKDjiWP2fdtgSfrnOsyJaLi04n+hDsgiMfd4N9tauSMpCY6H9l7yYPc5Z+3qG2ANhteZGa7wT1OZoGLkZV0OurnA4xkzwcB7h0RVEvABB9dtl6S60FK1NELQy6sC/HCcivo9sJ+C1g2Sln+8qEdiju86X5ja5pGiRhJAxwSp2ZKgwIDAQAB";
String encryptedMessage = "g81SOC9XOD9zq5qfyhkdP/7ronNb82g3ueDtEh711L43zPSgrFksLEdIud/1fiDcV6N97RD41vb/iXtCg2/Gu6XliEhCaoG28reetG1cBndKF9UzQw9cYChp54S1wnhBkAAZQ4Of3c77DtPBCL4gcgv2ilBTm7o+NR2wXunfJ7Olbbau+7C1pa+Qv/+sz45r4gJmQ1MfGjHtw9e/U/3vjL9BfCEPn9Mo2zAZhkI81S0Ewth+csHwb3YTlE8mtHni1fvLRVXjvHk+57U3keoYPZk+93ytFL6pqkWMk+9VbLuUFHXn1mpSMiEr9GRN6XKRvEbbPp5lI9WjwRvtWfmRm5gLY76QinTrPb0KJg7oWmEoQie5o9W6MOkD+8vYV/SkkLT855SB3O57QLKCZmlSPlccE6GWfglHhAwRwrcTDY1bO/xH38gvYYPaAJMtJKtOVrqGxNkIUPwCCkdBa9JQwDSyTYxeh8AxC0ACs9cYVjMPrmC9zIZuRbmcneIGSugtzMZmI9qbLtW1aMlWuGrVyVhJlcCZuTJXWyBgx8xj8coX9YwUXSi1A4dL/Hl5Sme+HhAQs7OcH6ZZpsPmIIozXxHgOMhUo8k++cWg6+pudSoB2tr4NhxX/ID2jd1ELsg1C6mbxaKaGgXwfU9w4ZngbRxGTBlKWXwUP/xBa5BARZ4=";
#Test
public void encryptTest() throws Exception {
PublicKey publicKey = convertPublicKey(publicKeyContent);
assertNotNull(publicKey);
String s = decryptString(publicKey, encryptedMessage);
assertNotNull(s);
}
private PublicKey convertPublicKey(String publicKey) throws RSAAlgorithmException {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//generate public key
byte[] publicBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RSAAlgorithmException("Unable to generate public key from string " + publicKey + " . " + e.getMessage());
}
}
private String decryptString(PublicKey publicKey, String value) throws Exception {
byte[] decodedBytes;
try {
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c.init(Cipher.DECRYPT_MODE, publicKey);
decodedBytes = c.doFinal(value.getBytes());
} catch (Exception e) {
System.out.println("Error = " + e);
throw new Exception(e);
}
return new String(decodedBytes);
}
}
I have next error:
java.lang.Exception: javax.crypto.IllegalBlockSizeException: Data must not be longer than 512 bytes
In an asymmetric cryptosystem, you have a key pair consisting of both a public and a private key.
You encrypt with the public key and you decrypt with the private key. The public key can be shared (publicly) with other parties, enabling them to send you encrypted messages. The private key is kept secret so that only you can decrypt messages encrypted with your public key.
You normally don't encrypt messages directly with RSA, since the message has to be shorter than the modulus and it might have security implications. What you do instead is, you generate a random key for a symmetric encryption scheme, for example AES-256-CTR (or AES-256-GCM if you need authentication in addition to secrecy), encrypt the message with the symmetric encryption scheme, encrypt the key for the symmetric cipher with the asymmetric encryption scheme and send both the (asymmetrically) encrypted key and the (symmetrically) encrypted message to the receiver.
The receiver will first use his/her private key to decrypt the key for the symmetric encryption scheme, then use that to decrypt the actual message. This is sometimes referred to as "hybrid encryption" and it enables the message to be (more or less) arbitrarily long.
So, what you have to do is the following.
You have to generate a key pair for the receiver of the encrypted message. Therefore, if your communication is one-way (iOS device sends data to server, but no data ever comes back), you need to generate a key pair for your server only. If your server needs to talk back, you need to generate a key pair for your client as well.
In order to send an encrypted message to the server, the client needs to have the public key of your server. Therefore, you have to somehow transfer it there. The problem is that this transfer needs to be secure, otherwise an attacker may impersonate the server, present you his/her public key instead (for which he/she knows the private counterpart), intercept all traffic, decrypt it with his/her private key, re-encrypt it with the server's public key and pass it on to the server. This is called a man in the middle attack and enables the attacker to intercept (and possibly manipulate) all communication between you and the server. Therefore, your best choice might be not to exchange public keys at all but rather to embed them into the application. This will prevent man in the middle attacks, as long as the application code can be shared by an authenticated means.
When you want to send a message to the server, generate a random symmetric encryption key (with a cryptographically secure random number generator - this is not your language's default "random" function), encrypt the message with it and an appropriate symmetric encryption scheme, which you choose according to your requirements (e. g. authentication required? then use AES-GCM - only secrecy required? then use AES-CTR). Most encryption schemes also require a random (unpredictable) initialization vector which you also generate with a CSPRNG and have to send along to the receiver since it's required for decryption, but needs not be kept secret.
Encrypt the key for the symmetric encryption scheme with an asymmetric encryption scheme and the server's public key. RSA-PKCS1 is "dated". I'd try to use RSA-OAEP instead since it has more desirable security properties. Send the encrypted key to the server.
The server decrypts the key for the symmetric encryption scheme with the asymmetric encryption scheme and his private key (which is kept secret). Then it decrypts the message with the symmetric encryption scheme.
Since most of this is complicated and a lot of subtle details can lead to security breaches, I'd suggest you do not implement this yourself. I'd suggest you just use TLS (possibly with a restricted parameter set) and implement your own certificate validator where you compare the server's public key to a known-good value to get rid of the entire PKI stuff, which costs money and also is not very secure in the first place. At least, that's how I would do it.
Alternatively, if you want to roll out your own, "proprietary" protocol, you can try to use one of the more "developer friendly" cryptographic libraries, especially NaCl. This abstracts away a lot of the "gory details" and chooses lots of sane defaults for you, which cannot be overridden, all of which makes it a lot harder to implement insecure protocols.
Keep in mind this is not to say you're "too dumb". It's just the proper way of doing these things. When it comes to crypto, the less "DIY", the better. The more widespread the crypto is, that you use, the more it gets reviewed and the quicker flaws will get fixed, so using something like NaCl, which is used in thousands of applications, is pretty neat. As long as other NaCl applications are secure, your application is (probably) secure as well. When a breach is found, NaCl will get updated, you just update the library in your application and are automatically safe, so you're left with (almost) no need for internal review and patching and your windows of vulnerability will (usually) be short.

Implement Diffie-Hellman key exchange in Java

I am trying to implement Diffie-Hellman key exchange in Java, but I'm having a hard time understanding the specification:
Complete the Diffie-Hellman key exchange process as a local mechanism
according to JWA (RFC 7518) in Direct Key Agreement mode using curve
P-256, dT and QC to produce a pair of CEKs (one for each direction)
which are identified by Transaction ID. The parameter values supported
in this version of the specification are:
“alg”: ECDH-ES
“apv”: SDK Reference Number
“epk”: QC, in JSON Web Key (JWK) format
{“kty”:”EC” “crv”:“P-256”}
All other parameters: not present
CEK: “kty”:oct - 256 bits
Create a JSON object of the following data as the JWS payload to be
signed:
{“MyPublicKey”: “QT”, “SDKPublicKey”:” QC”}
Generate a digital signature of the full JSON object according to JWS
(RFC 7515) using JWS Compact Serialization. The parameter values
supported in this version of the specification are:
“alg”: PS256 or ES256
“x5c”: X.5C v3: Cert(MyPb) plus optionally chaining certificates
From my understanding, ECDH will produce a secret key. After sharing my ephemeral public key (QT), the SDK produces the same secret key, so we can later exchange JWE messages encrypted with the same secret key.
The JSON {“MyPublicKey”: “QT”, “SDKPublicKey”:” QC”} will be signed and sent, but I do not understand how I will use apv and epk since these header params are used in JWE and not in the first JWS to be shared.
On the same specification, they talk about these JWE messages, but they do not have these apv and epk parameters.
Encrypt the JSON object according to JWE (RFC 7516) using the same
“enc” algorithm used by the SDK, the CEK obtained identified by “kid”
and JWE Compact Serialization. The parameter values supported in this
version of the specification are:
“alg”: dir
“enc”: either A128CBC-HS256 or A128GCM
“kid”: Transaction ID
All other parameters: not present
I also read the example in RFC 7518 where I can see the header params apv and epk being used but I'm not sure which header params, JWE's or JWS's ?
Any thought on how this could be implemented using nimbus-jose-jwt or any other java library would be really helpful. Thanks
Both apv (Agreement PartyVInfo) and epk (Ephemeral Public Key) are optional, so they can be used in multiple ways. You can use apv to reflect SDK version for example. They are added to the JWE header.
You can read more about JWE in Nimbus here
An example using Nimbus JOSE would be:
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.KeyOperation;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jose.util.Base64URL;
public class Security {
public void generateJWE() throws JOSEException, URISyntaxException {
JWEHeader jweHeader = buildJWEHeader(new Base64URL("202333517"), buildECKey());
JWEObject jweObject = new JWEObject(jweHeader, new Payload("Hello World!"));
}
private JWEHeader buildJWEHeader(Base64URL apv, ECKey epk) {
JWEHeader.Builder jweBuilder = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A128GCM);
jweBuilder.agreementPartyVInfo(apv);
jweBuilder.ephemeralPublicKey(epk);
return jweBuilder.build();
}
private ECKey buildECKey() throws URISyntaxException {
Set<KeyOperation> keyOperations = new HashSet<>();
keyOperations.add(KeyOperation.ENCRYPT);
String transactionID = "73024831";
URI x5u = new URI("https//website.certificate");
KeyStore keystore = null; //initialize it
List<Base64> x5c = new ArrayList<>();
return new ECKey(Curve.P_256, new Base64URL("x"), new Base64URL("y"), KeyUse.ENCRYPTION, keyOperations, Algorithm.NONE, transactionID, x5u, new Base64URL("x5t"), new Base64URL("x5t256"), x5c, keystore);
}
}
Instead of EncryptionMethod.A128GCM you can use EncryptionMethod.A128CBC-HS256 as in your specification. apv and epk are added to JWEHeader inside builder.
Other parameters can be chosen in constructors of JWEHeader.Builder and ECKey. I used ECDH-ES algorithm, A128GCM encryption method, P-256 curve (elliptic curve is default in ECKey generation),
transaction ID is a string. I chose other parameters without any clear pattern. Initialization of KeyStore would be too broad for the example. Encryption is only one thing you can do with JWE, among signature and others.
Nimbus (as well as Jose4j) is not the best choice for implementing the specification (I guess it's 3D secure 2.x.x).
These libraries do not return content encrypion key but use it to encrypt or decrypt messages as per JWE spec.
I found that Apache CXF Jose library does its job well as a JWE/JWS/JWK implementation. Except generating ephemeral key pairs. But it's can easily be done with Bouncy Castle:
Security.addProvider(new BouncyCastleProvider());
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec(JsonWebKey.EC_CURVE_P256);
KeyPairGenerator g = KeyPairGenerator.getInstance(ALGORITHM_SIGNATURE_EC, "BC");
g.initialize(ecGenSpec, new SecureRandom());
KeyPair keyPair = g.generateKeyPair();
Content encrypion key can be produced with this code:
byte[] cek = JweUtils.getECDHKey(privateKey, publicKey, null, SDKReferenceNumber, "", 256);
And used to encrypt messages getting JWE compact representation:
JweEncryption jweEncryption = JweUtils.getDirectKeyJweEncryption(cek, contentAlgorithm);
JweHeaders head = new JweHeaders();
head.setHeader(JoseConstants.HEADER_KEY_ID, keyId);
String encrypted = jweEncryption.encrypt(contentToEncrypt.getBytes(StandardCharsets.UTF_8), head);
Or decrypt:
JweCompactConsumer compactConsumer = new JweCompactConsumer(encrypted);
JweHeaders head = compactConsumer.getJweHeaders();
JweDecryption jweDecryption = JweUtils.getDirectKeyJweDecryption(cek, head.getContentEncryptionAlgorithm());
JweDecryptionOutput out = jweDecryption.decrypt(encrypted);
String decrypted = out.getContentText();
I able to generate the Secret Key for A128CBC-HS256. However still not succeed in A128GCM. Adding the working sample for A128CBC-HS256 with help nimbus-jose library. This below code is only for key generation.
Please also note that I override the nimbus-jose classes for my usage. Hope it helps.
private void generateSHA256SecretKey(AREQ areq, ECKey sdkPubKey, KeyPair acsKeyPair) throws Exception {
// Step 4 - Perform KeyAgreement and derive SecretKey
SecretKey Z = CustomECDH.deriveSharedSecret(sdkPubKey.toECPublicKey(), (ECPrivateKey)acsKeyPair.getPrivate(), null);
CustomConcatKDF concatKDF = new CustomConcatKDF("SHA-256");
String algIdString = "";
String partyVInfoString = areq.getSdkReferenceNumber();
int keylength = 256; //A128CBC-HS256
byte[] algID = CustomConcatKDF.encodeDataWithLength(algIdString.getBytes(StandardCharsets.UTF_8));
byte[] partyUInfo = CustomConcatKDF.encodeDataWithLength(new byte[0]);
byte[] partyVInfo = CustomConcatKDF.encodeDataWithLength(partyVInfoString.getBytes(StandardCharsets.UTF_8));
byte[] suppPubInfo = CustomConcatKDF.encodeIntData(keylength);
byte[] suppPrivInfo = CustomConcatKDF.encodeNoData();
SecretKey derivedKey = concatKDF.deriveKey(
Z,
keylength,
algID,
partyUInfo,
partyVInfo,
suppPubInfo,
suppPrivInfo);
System.out.println("Generated SHA256 DerivedKey : "+SecureUtils.bytesToHex(derivedKey.getEncoded()));
}

How to encrypt random value using some unique key in java

Hello I want to encrypt a unique random value using a key in Java. I will send this unique random value to each webservices to make system secure so nobody can hit my web services url on rest client.
Please guide me a way to achieve this.
Thanks in advance.
There are two solutions for that problem:
In summary, the first; you encrypt the data over sockets (by reverse engineering, like brute-force, you can break the password used to encrypt). The second; use a SSL (Security Socket Layer). I had used the first solution, then I can detail for you how to implement. Here you are:
1- There are some API's to help you doing that. I used jasypt a time ago, and I recommend. But there are others too; like bouncy castle.
Usually, they are simple to implement. In jasypt, you can solve this issue like that, just run to test:
public class SecurityUtil {
private static String passEncrypt;
/*
* Get the message encrypted
* #param String string to encrypt
* #return encrypted message
*/
public static String Encryptor(String message){
SecurityUtil.testEncryptPassSet();
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword(SecurityUtil.passEncrypt);
String encryptedText = encryptor.encrypt(message);
return encryptedText;
}
/*
* Get the decrypt message
* #param encrypted message
* #return String decrypted message
*
*/
public static String Decryptor(String message) {
SecurityUtil.testEncryptPassSet();
StandardPBEStringEncryptor decryptor = new StandardPBEStringEncryptor();
decryptor.setPassword(SecurityUtil.passEncrypt);
String decryptedText = decryptor.decrypt(message);
return decryptedText;
}
/*
* set the encryption password
*/
public static void setPassEncrypt(String passEncrypt) {
SecurityUtil.passEncrypt = passEncrypt;
}
public static void testEncryptPassSet() {
if (SecurityUtil.passEncrypt == null){
System.out.println("Must set the password after");
}
}
public static void main (String[] args){
SecurityUtil.setPassEncrypt("Test"); //here you key
String encrypted;
System.out.println("Encrypted: "+(encrypted = SecurityUtil.Encryptor("This is a test message")));
System.out.println("Decryp: "+SecurityUtil.Decryptor(encrypted));
}
}
OUTPUT:
Encrypted: eESU3c2IzRSl2VvHs4Otyh+Q3aBisiP6XPfyKpbXMdQ=
Decryp: This is a test message
2- You can study how implement SSL over sockets here. Also, here are some examples. And here we have a question of similar subject in StackOverflow.

Decrypt gpg in Java without using Java.Runtime

I have a .gpg file and a RSA private key. How can I programatically decrypt it without using operating system? e.g. without using something like Runtime.getRuntime().exec("gpg -decrypt.....");
Libraries I've found all run operating system. Like GnuPG or gnugpg-for-java.
As Skyr mentioned: Bouncy Castle is the way to go.
What do you want to do with this key? If your goal is to en- or decrypt files you might want to take a look at bouncy-gpg (shameless plug: I wrote it).
Using secret keys is actually three steps
Parse the key and put it into a PGPSecretKeyRing
Extract the secret key from the keyring
Decrypt it with the password
1. Parsing the exported key
In any case look here for the part that parses keys:
class ...
private PGPSecretKeyRingCollection secretKeyRings = new PGPSecretKeyRingCollection(EMPTY_LIST);
...
/**
* Add a new secret keyring to the public keyrings.
* .
* Can read the result of "gpg --export" and "gpg --export -a keyid"
* .
* E.g. "gpg --export-secret-key -a keyid":
* addSecretKey("-----BEGIN PGP PRIVATE KEY BLOCK----- ....".getBytes("US-ASCII")
* <p>
* The password is queried via the callback (decryptionSecretKeyPassphraseForSecretKeyId).
*
* #param encodedPrivateKey the key ascii armored or binary
* #throws IOException IO is dangerous
* #throws PGPException E.g. this is nor a valid key
*/
public void addSecretKey(byte[] encodedPrivateKey) throws IOException, PGPException {
if (encodedPrivateKey == null) {
throw new NullPointerException("encodedPrivateKey must not be null");
}
try (
final InputStream raw = new ByteArrayInputStream(encodedPrivateKey);
final InputStream decoded = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(raw)
) {
PGPSecretKeyRing pgpPrivate = new PGPSecretKeyRing(decoded, getKeyFingerPrintCalculator());
this.secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(this.secretKeyRings, pgpPrivate);
}
}
2. Getting the key from the keyring
final PGPSecretKeyRingCollection pgpSec = ...
final PGPSecretKey encryptedKey = pgpSec.getSecretKey(keyID);
3. Decrypting the key
Later you have to decrypt the key using a password like so:
/**
* Decrypt an encrypted PGP secret key.
*
* #param encryptedKey An encrypted key
* #param passphrase The passphrase for the key
* #return the decrypted secret key
* #throws PGPException E.g. wrong passphrase
*/
public static PGPPrivateKey extractPrivateKey(PGPSecretKey encryptedKey, final char[] passphrase) throws PGPException {
LOGGER.debug("Extracting secret key with key ID '0x{}'", Long.toHexString(encryptedKey.getKeyID()));
PGPDigestCalculatorProvider calcProvider = new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
PBESecretKeyDecryptor decryptor = new JcePBESecretKeyDecryptorBuilder(
calcProvider).setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(passphrase);
return encryptedKey.extractPrivateKey(decryptor);
}
there are too many examples that I've tried on Bouncy Castle with PGP. The common issue is keyID can't be found in KeyRing.
So, I found #Jens' bouncy-gpg (not sure if he still maintains it.)
Here is his documentation from github.io. It's simple to follow and works!
https://neuhalje.github.io/bouncy-gpg/
The Bouncy Castle library provides (among other features) an OpenPGP implementation. The package org.bouncycastle.openpgp.examples contains several usage examples, one of them showing how to encrypt/decrypt a file using a public/secret key pair (you can have a look at the examples on GrepCode or on the project's Github mirror).

Categories