Java - Storing Keypairs in KeyStore - java

I am implementing an Hybrid Encryption project and I have generated 2 key pairs for sender and receiver keys (private and public). I have these keys in a file.
KeyPair Generation Code
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import Decoder.BASE64Encoder;
public class GenerateRSAKeys{
public static void main(String[] args)
{
String publicKeyFilename = null;
String privateKeyFilename = null;
publicKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_publicKey";
privateKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_privateKey";
GenerateRSAKeys generateRSAKeys = new GenerateRSAKeys();
generateRSAKeys.generate(publicKeyFilename, privateKeyFilename);
}
private void generate (String publicKeyFilename, String privateFilename){
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Create the public and private keys
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
BASE64Encoder b64 = new BASE64Encoder();
SecureRandom random = createFixedRandom();
generator.initialize(1024, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("publicKey : " + b64.encode(pubKey.getEncoded()));
System.out.println("privateKey : " + b64.encode(privKey.getEncoded()));
BufferedWriter out = new BufferedWriter(new FileWriter(publicKeyFilename));
out.write(b64.encode(pubKey.getEncoded()));
out.close();
out = new BufferedWriter(new FileWriter(privateFilename));
out.write(b64.encode(privKey.getEncoded()));
out.close();
}
catch (Exception e) {
System.out.println(e);
}
}
public static SecureRandom createFixedRandom()
{
return new FixedRand();
}
private static class FixedRand extends SecureRandom {
MessageDigest sha;
byte[] state;
FixedRand() {
try
{
this.sha = MessageDigest.getInstance("SHA-1");
this.state = sha.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("can't find SHA-1!");
}
}
public void nextBytes(byte[] bytes){
int off = 0;
sha.update(state);
while (off < bytes.length)
{
state = sha.digest();
if (bytes.length - off > state.length)
{
System.arraycopy(state, 0, bytes, off, state.length);
}
else
{
System.arraycopy(state, 0, bytes, off, bytes.length - off);
}
off += state.length;
sha.update(state);
}
}
}
}
Now, I need to secure these keys (probably not on any disk directly).
I have searched for this case on the internet; saving keys into key stores is the way to secure keys and use the same keystore when reading keys for the use in encryption and decryption.
Can someone suggest me how to save public and private key in key store and how to read it in java?

The Java KeyStore API (and underlying providers) does not support storing a keypair -- that is, a privatekey and publickey (which can then be used for publickey operations like encrypt and verify). It does support storing a privatekey and a certificate chain for the publickey. The certificate chain can consist of only one certificate, especially if that certificate is a self-signed certificate (which by definition is a chain by itself). That is exactly what commandline keytool -genkeypair does: it generates a keypair and stores the privatekey plus a self-signed certificate for the publickey in the (specified or defaulted) keystore file.
If you were using only 'standard' JRE, creating a self-signed (or other) certificate in code is not very easy.
However, if you have BouncyCastle added, as you obviously do or the code you posted wouldn't work, that has numerous applicable capabilities. For a simple example of generating a cert with only bcprov (the old, deprecated X509V3CertificateGenerator) see my JAVA API to create a keystore and attaching a csr and keypair to it -- but do as I recommended there but didn't show: don't create the unneeded CSR first, instead create the cert using the desired name and publickey.
Better, if you also have or can get bcpkix, use X509v3CertificateBuilder. See examples at Self signed X509 Certificate with Bouncy Castle in Java and Generating X509 Certificate using Bouncy Castle Java .
For writing out, and reading back in, a keystore file containing this data, just see the Javadoc linked above. Note Java traditionally defaulted to JKS-format files, and you will find many older answers here and elsewhere on the web which assume that, but since Java 9 in 2017 it defaults to PKCS12-format, which is not only standard and thus (mostly) interoperable but also more secure (uses a much better PBKDF algorithm).
However, your code uses a completely bogus random generator, and as a result generates the same keypair every time. Given this, there is no real need to store it, since you can always re-generate it any time you want. In fact, there is no benefit to even having this keypairs since it provides no security at all.

Related

Extract keyUsage extension from a certificate using bouncy castle

I am writing a small CA implementation. It is possible to bootstrap this CA from an existing certificate. Upon doing this, I want to verify that the input has the correct extensions:
private static final Set<String> REQUIRED_CA_EXTENSIONS = Set.of(
Extension.keyUsage.getId(),
Extension.subjectKeyIdentifier.getId());
private static void validateExtensions(final X509Certificate certificate) {
if (!CertificateExtensions.getAll(certificate).containsAll(REQUIRED_CA_EXTENSIONS)) {
throw new RuntimeException("Attempted to create a CA from a certificate without required extensions");
}
}
// Util method
public static Set<String> getAll(final X509Certificate certificate) {
final Set<String> extensions = new HashSet<>();
extensions.addAll(certificate.getCriticalExtensionOIDs());
extensions.addAll(certificate.getNonCriticalExtensionOIDs());
return extensions;
}
However, this only verifies the extensions are present. I also need to verify that the keyUsage extension contains both keyCertSign and cRLSign to be able to sign certificates.
How can I do this using bouncy castle and/or JCA?
There is a method available on a JCA X509Certificate to return the bits of the keyUsage extension, called simply getKeyUsage(). The individual bits of the key usage represented by boolean values in the returned array, per the Java documentation.
It is also possible to do a little more work using the Bouncycastle libraries to accomplish the same result. I show both methods because the Bouncycastle libraries offer much more complete support for examining an X509Certificate, so it's useful to have an example illustrating something easy in case you want to do something more difficult.
Note that this requires two Bouncycastle libraries as of this writing, the main provider/crypto library and also the PKIX/CMS library.
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509CertificateHolder;
import java.io.FileInputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class X509CheckKeyUsage {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("DST_X3_CA.pem"); // for example
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
// check with simple JCA methods
boolean [] keyUsageBools = cert.getKeyUsage();
final int KEY_CERT_SIGN = 5;
final int CRL_SIGN = 6;
boolean usagesVerified = keyUsageBools[KEY_CERT_SIGN] && keyUsageBools[CRL_SIGN];
System.out.println("key usage bits verified? " + usagesVerified);
// Convert the jca x.509 cert to a bouncycastle x.509 cert, in two steps
org.bouncycastle.asn1.x509.Certificate bcCert = org.bouncycastle.asn1.x509.Certificate
.getInstance(ASN1Primitive.fromByteArray(cert.getEncoded())); // step 1
X509CertificateHolder bcX509Cert = new X509CertificateHolder(bcCert); // step 2
// now verify keyUsage bits
final int requiredKeyUsageBits = KeyUsage.keyCertSign | KeyUsage.cRLSign;
usagesVerified = KeyUsage.fromExtensions(bcX509Cert.getExtensions()).hasUsages(requiredKeyUsageBits);
System.out.println("key usage bits verified? " + usagesVerified);
}
}

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()));
}

JavaScript encryption based on Java generated RSA key

I'm trying to implement a solution for encryption between Java and JavaScript.
on the Java end I have the following static block:
public class Manager {
public static KeyPairGenerator keyPairGenerator;
public static KeyPair keyPair;
static{
try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
keyPair = keyPairGenerator.genKeyPair();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
...
}
This basically generates a fresh KeyPair once my server is up and running...
then I give the public key in a JSON format:
<%
JSONObject json = new JSONObject();
json.put("publicKey", "-----BEGIN PUBLIC KEY-----" + Base64.encodeBase64URLSafeString(Manager.keyPair.getPublic().getEncoded()) + "-----END PUBLIC KEY-----");
%>
and I want to use that key (be it 1024 or 2048 bit) to encode information coming from client's forms...
anyone knows how can I encode the information using an RSA 1024 bit, base64 encoded public key?
I tried jCryption and severel other libraries to no avail...
If you don't send your public key as a certificate, you are better off just sending the modulus and the public exponent separately (e.g. base 64 encoded in separate fields). The default encoding will result in a X509 SubjectPublicKeyInfo ASN.1 structure, which you would need to parse in your JavaScript libraries.
Note that you are protecting only against eavesdroppers; man-in-the-middle attacks are still viable as they can replace your public key with their own. RSA 1024 is of course outdated by now. Fortunately you still have TLS/SSL to protect you.

How to read a private key for use with OpenSAML?

OK, this is another of those "I have no real idea where to start" questions, so hopefully the answer is simple. However, I don't really know what to search for, and my attempts so far haven't turned up much of use.
I want to read a private key from a (currently on-disk) file. Ultimately the key will reside in a database, but this will be good enough for the moment and that difference should have no real bearing on parsing the key material. I have been able to create a Credential instance that holds the public part of the key (confirmed by debugger), but I can't seem to figure out how to read the private part. The key pair was generated as:
openssl genrsa 512 > d:\host.key
openssl req -new -x509 -nodes -sha1 -days 365 -key d:\host.key > d:\host.cert
(Yes, I know that 512 bit RSA keys were broken long ago. However, for trying to get the API to work, I see no reason to exhaust the system entropy supply needlessly.)
The code thus far is:
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.x509.BasicX509Credential;
private Credential getSigningCredential()
throws java.security.cert.CertificateException, IOException {
BasicX509Credential credential = new BasicX509Credential();
credential.setUsageType(UsageType.SIGNING);
// read public key
InputStream inStream = new FileInputStream("d:\\host.cert");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
inStream.close();
credential.setEntityCertificate(cert);
// TODO: read private key
// done.
return credential;
}
But how do I read the file host.key into the private key portion of credential, so I can use the generated Credential instance to sign data?
BasicX509Credential is not part from standard Java; I suppose you are talking about org.opensaml.xml.security.x509.BasicX509Credential from OpenSAML.
You want a PrivateKey which you will set with credential.setPrivateKey(). To get a PrivateKey, you must first convert the private key into a format that Java can read, namely PKCS#8:
openssl pkcs8 -topk8 -nocrypt -outform DER < D:\host.key > D:\host.pk8
Then, from Java:
RandomAccessFile raf = new RandomAccessFile("d:\\host.pk8", "r");
byte[] buf = new byte[(int)raf.length()];
raf.readFully(buf);
raf.close();
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(buf);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(kspec);
and voilà! you have your PrivateKey.
By default, openssl writes key in its own format (for RSA keys, PKCS#8 happens to be a wrapper around that format), and it encodes them in PEM, which Base64 with a header and a footer. Both characteristics are unsupported by plain Java, hence the conversion to PKCS#8. The -nocrypt option is because PKCS#8 supports optional password-based encryption of private key.
Warning: you really really want to use a longer RSA key. 512 bits are weak; a 512-bit RSA key was broken in 1999 with a few hundred computers. In 2011, with 12 years of technological advances, one should assume that a 512-bit RSA key can be broken by almost anybody. Therefore, use 1024-bit RSA keys at least (preferably, 2048-bit; the computational overhead when using the key is not that bad, you will still be able to perform hundred of signatures per second).
This question is related to SAML and is also relevant for someone who wants to retrieve a private key for signing an XMLObject. The answer above works great and it also possible to retrieve a private key from a keystore as well:
Properties sigProperties = new Properties();
sigProperties.put("org.apache.ws.security.crypto.provider","org.apache.ws.security.components.crypto.Merlin");
sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.type","jks");
sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.password","keypass");
sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.alias","keyalias");
sigProperties.put("org.apache.ws.security.crypto.merlin.keystore.file","keystore.jks");
Crypto issuerCrypto = CryptoFactory.getInstance(sigProperties);
String issuerKeyName = (String) sigProperties.get("org.apache.ws.security.crypto.merlin.keystore.alias");
//See http://ws.apache.org/wss4j/xref/org/apache/ws/security/saml/ext/AssertionWrapper.html 'signAssertion' method
// prepare to sign the SAML token
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
cryptoType.setAlias(issuerKeyName);
X509Certificate[] issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
if (issuerCerts == null) {
throw new WSSecurityException(
"No issuer certs were found to sign the SAML Assertion using issuer name: "
+ issuerKeyName);
}
String password = ADSUnitTestUtils.getPrivateKeyPasswordFromAlias(issuerKeyName);
PrivateKey privateKey = null;
try {
privateKey = issuerCrypto.getPrivateKey(issuerKeyName, password);
} catch (Exception ex) {
throw new WSSecurityException(ex.getMessage(), ex);
}
BasicX509Credential signingCredential = new BasicX509Credential();
signingCredential.setEntityCertificate(issuerCerts[0]);
signingCredential.setPrivateKey(privateKey);
signature.setSigningCredential(signingCredential);
This is more code than the original query requested, but it looks they are trying to get at a BasicX509Credential.
Thanks,
Yogesh

Categories