JWT[ RSA ], api owner shared private key - java

SO I have to write a Rest client for an integration we are working on with one of our client.
They gave us a private key and told us to create a JWT of the json payload.
Key:
"-----BEGIN RSA PRIVATE KEY-----\nYYYYYYYYYYYYYYYYYYYYYYYYYYYYY edited ..YYYYY==\n---
--END RSA PRIVATE KEY-----\n"
Question:
Is it correct to share private key, is there any java example I can use to create JWT using RSA?
Courtesy - https://wstutorial.com/misc/jwt-java-public-key-rsa.html
public String generateJwtToken(PrivateKey privateKey) {
String token = Jwts.builder().setSubject("adam")
.setExpiration(new Date(2018, 1, 1))
.setIssuer("info#wstutorial.com")
.claim("groups", new String[] { "user", "admin" })
// RS256 with privateKey
.signWith(SignatureAlgorithm.RS256, privateKey).compact();
return token;
}

No, it's not correct to share a private key. Never.
It's called private for a reason.
It is also not correct to create a token on client side.
The only correct thing here is, that you would need the private key to sign the token, but that's not the task of the client. It defeats the whole purpose of a JWT, because you could write into it (e.g. roles, expiration time), whatever you want. It seems the API owner trusts you, and probably it's an API that is not public, but anyway, I would recommend to do it in the right way. They should implement an endpoint to request a token.
Usage examples can usually be found on the websites of the jwt libraries.

RSA keys are public / private key pairs. The private key can be used for signing JWTs, and the public key can be used to verify the signature of those JWTs.
The private key should not be shared with anyone. Doing so would allow random people to access your client API.
I've had good experience using Nimbus to sign JWT with RSA. You can see some examples here: https://connect2id.com/products/nimbus-jose-jwt/examples/jwt-with-rsa-signature.

better late than never
It’s generally a bad idea to share private key. But let's assume you own the private key and you want to use it instead to generate a Keypair each time.
Here an example how to use an existing private key, which is stored in P12 file.
#Test
public void readP12() {
char[] keyStorePassword = "1234567890".toCharArray();
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyStoreData = new FileInputStream(LOCATION + "\\keystore.p12");
keyStore.load(keyStoreData, keyStorePassword);
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyStorePassword);
KeyStore.Entry keyEntry = keyStore.getEntry("1", entryPassword);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)
keyStore.getEntry("1", entryPassword);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();
String token = generateJwtToken(privateKey);
System.out.println(token);
printStructure(token, publicKey);
} catch (Exception e) {
// tbd
}
}
You can see how to save private key in p12 file

Related

Is it possible to generate a 64-byte (256-bit) key and store/retrieve it with AndroidKeyStore?

In my Android app, I need a way to encrypt the data I store in a local DB.
I chose Realm DB because the offer a seamless integration with encryption. I just need to pass a key when initializing the Realm instance. This key must be of 64 byte size.
For security reason, I found out that the best way to store this key is in AndroidKeyStore. I'm struggling to find a way to generate a key (using any algorithm) with that size, and getting it into a 64-byte array. I'm trying to keep a minSdk of API 19, but I believe I can bump it up to 23 if needed (many changes to AndroidKeyStore between these two versions).
Does anyone have an idea? Here is my code:
Class Encryption.java
private static KeyStore ks = null;
private static String ALIAS = "com.oi.pap";
public static byte[] loadkey(Context context) {
byte[] content = new byte[64];
try {
if (ks == null) {
createNewKeys(context);
}
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
content= ks.getCertificate(ALIAS).getEncoded(); //<----- HERE, I GET SIZE GREATER THAN 64
Log.e(TAG, "original key :" + Arrays.toString(content));
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = Arrays.copyOfRange(content, 0, 64); //<---- I would like to remove this part.
return content;
}
private static void createNewKeys(Context context) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
try {
// Create new key if needed
if (!ks.containsAlias(ALIAS)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setSubject(new X500Principal("CN=PapRealmKey, O=oipap"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.setKeySize(256)
.setKeyType(KeyProperties.KEY_ALGORITHM_EC)
.build();
KeyPairGenerator generator = KeyPairGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
Log.e(TAG, "generated key :" + Arrays.toString(keyPair.getPrivate().getEncoded()));
}
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
}
}
The point of AndroidKeyStore is to move sensitive key material out of your app, out of the operating system and into secure hardware where it can never leak or be compromised. So, by design, if you create a key in AndroidKeyStore, you can never get the key material out.
In this case, Realm DB wants the secret key material, so you can't give it an AndroidKeyStore key. Also, what Realm wants is two AES keys, not an EC key, as you were trying to generate.
The right way to generate the key material you need is:
byte[] dbKey = new byte[64];
Random random = new SecureRandom();
random.nextBytes(dbKey);
// Pass dbKey to Realm DB...
Arrays.fill(dbKey, 0); // Wipe key after use.
Just 64 random bytes. However, you're going to need to store those bytes somewhere. You could create an AES key with AndroidKeyStore and use it to encrypt dbKey. Something like:
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(
new KeyGenParameterSpec.Builder("dbKeyWrappingKey",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
SecretKey key = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV();
byte[] encryptedDbKey = cipher.doFinal(dbKey);
You'll need to save both iv and encryptedDbKey somewhere (not in the database!) so that you can recover dbKey. Then you can decrypt it with:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
key = (SecretKey) keyStore.getKey("dbKeyWrappingKey", null);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
byte[] dbKey = cipher.doFinal(encryptedDbKey);
// Pass dbKey to Realm DB and then wipe it.
However, with all of that said... I don't think you should do any of it. I don't think this actually gives you any security that Android doesn't give you by default anyway. If an attacker tries to dump the device storage, which contains your database, he'll get nothing because Android encrypts all of the storage anyway. If an attacker can root the device, he can run code as your app and use it to decrypt dbKey the same way your app does.
Where AndroidKeyStore may really add value is if you add some additional protections on dbKeyWrappingKey. For example, if you set it to require user authentication within, say five minutes, it will only be possible to use dbWrappingKey to decrypt dbKey when the user is around to enter their PIN/pattern/password or touch the fingerprint scanner. Note that this only works if the user has a PIN/pattern/password, but if they don't, well, your database is wide open to anyone who picks up the phone anyway.
See KeyGenParameterSpec for all of the things you can do to restrict the ways dbKeyWrappingKey can be used.
As far as I know, the usual way to solve this problem is, that you generate your own random key of the size you need (master-key), and this master-key can be encrypted with the help of the key store.
Generate your own random master-key of the size you need.
Encrypt data with this master-key (e.g. symmetric encryption).
Encrypt the master-key with the help of the key-store.
Store the encrypted master-key somewhere.
To decrypt your data:
Read the encrypted master-key.
Decrypt the master-key with the help of the key-store.
Decrypt data with the master-key.
In other words, it is not the master-key which is stored inside the key-store, but the key-store can be used to protect/encrypt your master-key.

How to extend/add a Bouncycastle digest algorithm for CMS?

With Java I want to cryptographically sign given byte[] hash with RSA and return byte[] signature in the Cryptographic Message Syntax (CMS) format.
I am using the Bouncycastle Java API (BC) for this purpose and face the problem of the not existing NONEwithRSA signature type. (java.lang.IllegalArgumentException: Unknown signature type requested: NONEWITHRSA).
I, unfortunately, cannot change the input nor the required output format so I am required to extend/add to the existing BC algorithm so that I can use NONEwithRSA instead of all the other existing (e.g. SHA256withRSA). How do I do this? I have not found any example in the BC documentation.
My desired usage would be similar to this
byte[] doSigningCMS(byte[] data, X509Certificate cert, PrivateKey key) throws Exception {
CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
CMSTypedData content = new CMSProcessableByteArray(data);
ContentSigner signer = new JcaContentSignerBuilder("NONEwithRSA").build(key);
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().build();
signGen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(dcp).build(signer, cert));
return signGen.generate(content, false).getEncoded();
}
What I have tried so far
Adding my own provider for the JCA with "copying" a bunch of BC code and "fake" SHA256withRSA to basically return a NoneDigest instead of SHA256Digest. But this seems to be very wrong.
Adding my own algorithm to BC with ((BouncyCastleProvider) Security.getProvider("BC")).addAlgorithm(ALG_ALIAS, NoneDigest.class.getName()) which does not seem to work (algorithm is not found)
I hope for some advice and guidance to do this (if even possible) "the right way". Thanks.
"NoneWithRSA" is a RSA digital signature without adding the OID of the digest algorithm. The cryptographic provider does not make the digest either. Basically it is a PKCS#1_v15 encryption with the private key.
You can see how it works in this OpenJDK test https://github.com/ddopson/openjdk-test/blob/master/java/security/Signature/NONEwithRSA.java
Since Bouncycastle seems does not support it, I think you can supply your own ContentSigner implementation using the default provider instead of using JcaContentSignerBuilder
It is supposed that the input data is already hashed, so if you are doing a PKCS#1 signature, I think you need to provide the signature algorithm. Looking at the RFC3477 it depends on the hash algorithm used.
A.2.4 RSASSA-PKCS1-v1_5
The object identifier for RSASSA-PKCS1-v1_5 shall be one of the
following. The choice of OID depends on the choice of hash
algorithm: MD2, MD5, SHA-1, SHA-256, SHA-384, or SHA-512.
String sigAlgo = "SHA256WithRSAEncryption"; // "SHA256WithRSAEncryption" for SHA256, "SHA1WithRSAEncryption" for SHA1, etc.
ContentSigner signer = new CustomContentSigner(key, sigAlgo );
public class CustomContentSigner implements ContentSigner {
private AlgorithmIdentifier algorithmIdentifier;
private Signature signature;
private ByteArrayOutputStream outputStream;
public CustomContentSigner(PrivateKey privateKey, String sigAlgo) {
//Utils.throwIfNull(privateKey, sigAlgo);
this.algorithmIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(sigAlgo);
try {
this.outputStream = new ByteArrayOutputStream();
this.signature = Signature.getInstance("NONEwithRSA");
this.signature.initSign(privateKey);
} catch (GeneralSecurityException gse) {
throw new IllegalArgumentException(gse.getMessage());
}
}
#Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return algorithmIdentifier;
}
#Override
public OutputStream getOutputStream() {
return outputStream;
}
#Override
public byte[] getSignature() {
try {
signature.update(outputStream.toByteArray());
return signature.sign();
} catch (GeneralSecurityException gse) {
gse.printStackTrace();
return null;
}
}
}
Disclaimer: I do not know if it will work but you can try it

From certificate Alias to PEM File with private key included using Java

I have this code to generate a CER file using the alias:
public class TestFromAliasToCER {
public static final int KEY_SIZE = 1024;
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
public final static String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws FileNotFoundException, IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException, CertificateException {
KeyStore keyStore = KeyStore.getInstance ("Windows-MY");
keyStore.load (null, null);
Enumeration<String> aux = keyStore.aliases();
String alias = aux.nextElement();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate (alias);
String certString = formatCrtFileContents(certificate);
PrintWriter out = new PrintWriter("cert.CER");
out.println(certString);
out.close();
}
public static String formatCrtFileContents(final Certificate certificate) throws CertificateEncodingException {
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
final byte[] rawCrtText = certificate.getEncoded();
final String encodedCertText = new String(encoder.encode(rawCrtText));
final String prettified_cert = BEGIN_CERT + LINE_SEPARATOR + encodedCertText + LINE_SEPARATOR + END_CERT;
return prettified_cert;
}
}
This creates the cer file with
-----BEGIN CERTIFICATE-----
data
-----END CERTIFICATE-----
I want to be able to create a PEM Certificate with the private key included, is it possible? If not, why?
I'm not restricted to Java only and free to use any Java API, but preferable with the least user interaction as possible.
Although I don't see it documented, according to the source the SunMSCAPI provider implements only a stub for getEncoded and cannot export Windows privatekey so you can't do this with JCA.
You could of course write JNI or JNA to call Windows CAPI, but that's not simple.
To use existing tools without user interaction you can use Runtime or ProcessBuilder to
run certutil with arguments -exportpfx -user -p password certid filename
run powershell and tell it to select an object in cert:\currentuser\my and invoke the Export('PFX','password') method -- examples for machine rather than user cert here
or in (only) recent powershell use Export-PFXCertificate cmdlet documentation here
and after any of these, extract from pkcs12 to PEM with openssl pkcs12, or if you prefer with Java by:
load the PKCS12 keystore and get the PrivateKey entry
call getEncoded and encode the result in folded (MIME) base64 like you did for the certificate except use -----BEGIN/END PRIVATE KEY-----
Warning: Java produces an unencrypted (PKCS8) privatekey, so make certain no unauthorized user or program ever has access to this file, your disk/filesystem or any backup(s).
A digital certificate doesn't have the private key inside it (the private key is not part of the certificate fields). The certificate and the private key are separate entities, although they're related (one can't exist without the other).
If you take a look at the certificate fields in RFC 5280, you'll see that only the public key is part of it:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertificate ::= SEQUENCE {
... lots of fields
subjectPublicKeyInfo SubjectPublicKeyInfo,
... lots of other fields
}
The subjectPublicKeyInfo is the public key, and there's no field for the private key.
That's because certificates are meant to be public (you can have more details on why they're public taking a look at how a Public Key Infrastructure works).
Although the certificate is public, there's always a correspondent private key somewhere, usually held by the certificate's owner (and ideally by no one else).
Anyway, the file you've got (with BEGIN CERTIFICATE and END CERTIFICATE headers) in only the digital certificate (but not the private key).
If you have the private key and the corresponding certificate, you can create a file that contains both. The most common formats for such file are: JKS (also known as Keystore) and PFX.
There are also another "format": the Windows repository (the one you're reading when you do KeyStore.getInstance("Windows-MY")). I don't know exactly in what format its files are, but the KeyStore class abstracts it.
If the private key is present, it will be together with its corresponding certificate, in the same alias. You can check if the key is present with this code:
String alias = aux.nextElement();
if (keyStore.isKeyEntry(alias)) { // alias contains a private key
Key key = keyStore.getKey(alias, "password".toCharArray()); // need to know the password
// key is the private key
// cert is the key's corresponding certificate
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
} else if (keyStore.isCertificateEntry(alias)) { // alias doesn't contain a key
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
}
After having the key, you can save it to another keystore with the following code:
// create another keystore
KeyStore output = KeyStore.getInstance("JKS");
// "alias" - choose to whatever name you want
// privateKey is the object you've got from keyStore.getKey()
// "password" is the password for this alias
// cert will be stored in the same alias
output.setKeyEntry("alias", privateKey, "password".toCharArray(), new Certificate[] { cert });
// save the keystore to a file
output.store(new FileOutputStream("outputfile.jks"), "keystore password".toCharArray());
The code above creates the file outputfile.jks containing the certificate and the private key.
If you want the file to be a PFX, you can change the code above to:
// PKCS12 == PFX format
KeyStore output = KeyStore.getInstance("PKCS12");
// alternative: in pfx, I think that alias can't have specific passwords
// so you can use this as it doesn't require a password for the alias entry
output.setKeyEntry("alias", privateKey.getEncoded(), new Certificate[] { cert });
// change file extension to ".pfx"
output.store(new FileOutputStream("outputfile.pfx"), "keystore password".toCharArray());

ios create certificate request programmatically

I am trying to create certificate request programmatically (that I would send to server) in iOS am OSX without using openSSL. I have managed to create RSA key pair but am failing at doing the rest. I have the code that does exactly what I need but it is written in Java and am wondering if there is someone to help me translate this to objective c.
Here is the Java code:
test.generateKeys(); // generate RSA key pair
PrivateKey privateKey = test.keys.getPrivate();
PublicKey publicKey = test.keys.getPublic();
SecureRandom sr = new SecureRandom();
String token = "123456"; // dummy token
String uuid = "4670ff33-d9f7-4026-957d-25c00e4dec54"; // dummy uuid
// with Bouncy Castle
ContentSigner signGen = new JcaContentSignerBuilder("SHA1withRSA").setSecureRandom(sr).build(privateKey);
X500Principal subject = new X500Principal("O=" + token + ", CN=" + uuid);
PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, publicKey);
PKCS10CertificationRequest request = builder.build(signGen);
String bc = Hex.encodeHexString(request.getEncoded());
System.out.println(PEMtoString(request));
I am not very good in cryptography and the documentation for the crypto layer apple is developing is pretty poor documented so I am a bit lost here. I have came across a lot of similar samples but there is always something missing.
Thx in advance.
In case someone stumbles on the same problem here is the solution using Apples common crypto layer (no openSSL).
https://github.com/ateska/ios-csr
No need for several weeks of coding just a simple include.
SCCSR *sccsr = [[SCCSR alloc] init];
sccsr.commonName = #"some name";
sccsr.organizationName = #"some organisation";
// // aditional data you can set
// sccsr.countryName = #"";
// sccsr.organizationalUnitName = #"";
// sccsr.subjectDER = nil;
// //
//
NSData *certificateRequest = [sccsr build:pPublicKey privateKey:privateKey];
NSString *str = [certificateRequest base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString *strCertificateRequest = #"-----BEGIN CERTIFICATE REQUEST-----\n";
strCertificateRequest = [strCertificateRequest stringByAppendingString:str];
strCertificateRequest = [strCertificateRequest stringByAppendingString:#"\n-----END CERTIFICATE REQUEST-----\n"];
SCCSR.h -> DOWNLOADED FROM PROVIDED LINK

Store PGP (public) keys in java keystore - Bouncycastle

I am using bouncycastle (JAVA) for signing, encryption, decryption and signatures' verification in implementation of SSO.
I have raw PGP public and private keys and I need to store them in Java keystore.
These PGP public keys have no certificate.
I understand that for public keys (according to javadoc of Keystore: http://docs.oracle.com/javase/6/docs/api/java/security/KeyStore.html) I have to create certificate. Once certificate is created I can import it to the keystore as KeyStore.TrustedCertificateEntry.
However, I am not able to create certificate entry for type org.bouncycastle.openpgp.PGPPublicKey.
I have searched through the web but could not find any valid example:
Bouncycastle documentation: http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation
Generates certificate for X.509 keys -
Bouncycastle examples - org.bouncycastle.openpgp.examples.DirectKeySignature:
Add certificat (object of type PGPSignature) directly to the PGPPublicKey.
To conclude - I have signed (certified) PGPPublicKey but I am not able to store this type of Key into the java keystore.
OutputStream out = new ByteArrayOutputStream();
if (armor)
{
out = new ArmoredOutputStream(out);
}
PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(secretKeyPass.toCharArray(), "BC");
PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC");
sGen.initSign(PGPSignature.DIRECT_KEY, pgpPrivKey);
BCPGOutputStream bOut = new BCPGOutputStream(out);
sGen.generateOnePassVersion(false).encode(bOut);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
boolean isHumanReadable = true;
spGen.setNotationData(true, isHumanReadable, notationName, notationValue);
PGPSignatureSubpacketVector packetVector = spGen.generate();
sGen.setHashedSubpackets(packetVector);
bOut.flush();
return PGPPublicKey.addCertification(keyToBeSigned, sGen.generate()).getEncoded();
I am mainly interested in programatic solution (java source code) but examples that use some tools will be helpful too.
Thanks!
I think you should extract a java.security.PublicKey from your PGPPublicKey and use that to construct an X509Certificate which can be stored in a keystore.
JcaPGPKeyConverter c = new JcaPGPKeyConverter();
PublicKey publicKey = c.getPublicKey(pgpPublicKey);
// ... Use Bouncy's X509V3CertificateGenerator or X509v3CertificateBuilder
// ... to construct a self-signed cert
X509Certificate x509Certificate = // ...
// ... add cert to KeyStore
To create an X509Certificate from a PublicKey see: Generate random certificates.
If you only want to save the public key, why cannot you just save the key content into Java keystore? Then retrieve the content and convert into a PGPPublicKey object when you need it.
Create a wrapper class first
public class PgpPublicKeyWrapper implements Key {
private final String keyContent;
public PgpPublicKeyWrapper(final String keyContent) {
this.keyContent = keyContent;
}
#Override
public String getAlgorithm() {
return "PGP-PublicKey"; // you can call whatever you want
}
#Override
public String getFormat() {
return "RAW"; // has to be raw format
}
#Override
public byte[] getEncoded() {
return keyContent.getBytes();
}
}
Then you can do this to save it
keyStore.setKeyEntry("think a name for alias", new PgpPublicKeyWrapper(key), PASSWORD, null);
When you want to retrieve it
Key key = this.keyStore.getKey(alias, PASSWORD);
InputStream is = new ByteArrayInputStream(key.getEncoded());
PGPPublicKey publicKey = readPublicKey(is);
For readPublicKey(), you can find a lot of examples online about how to read InputStream to a PGPPublicKey object.

Categories

Resources